+++ /dev/null
-;; -*- emacs-lisp -*-
-;;
-;; This file is processed by the dirvars emacs package. Each variable
-;; setting below is performed when this dirvars file is loaded.
-;;
-c-file-style: "gnu";
-fill-column: 78
-indent-tabs-mode: nil
-tab-width: 8
+++ /dev/null
-*~
-build
-doc/*.xml
-tests/solver/data.libzypp/*/*.result
-src/solvversion.h
+++ /dev/null
-language: C
-before_script: sudo apt-get install cmake
-script: mkdir build && cd build && cmake -DDEBIAN=1 -DMULTI_SEMANTICS=1 .. && make && make test
-
-
--- /dev/null
+package BSSolv;
+
+use strict;
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+
+our $VERSION = '0.08';
+
+require XSLoader;
+
+XSLoader::load('BSSolv', $VERSION);
+
+package BSSolv::repo;
+
+1;
--- /dev/null
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#define MULTI_SEMANTICS
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "evr.h"
+#include "hash.h"
+#include "chksum.h"
+#include "repo_solv.h"
+#include "repo_write.h"
+#include "repo_rpmdb.h"
+#include "repo_deb.h"
+#if 1
+#include "repo_arch.h"
+#endif
+
+typedef struct _Expander {
+ Pool *pool;
+
+ Map ignored;
+ Map ignoredx;
+
+ Queue preferposq;
+ Map preferpos;
+ Map preferposx;
+
+ Map preferneg;
+ Map prefernegx;
+
+ Queue conflictsq;
+ Map conflicts;
+
+ int debug;
+ int havefileprovides;
+ int ignoreconflicts;
+ int ignoreignore;
+
+ char *debugstr;
+ int debugstrl;
+ int debugstrf;
+} Expander;
+
+typedef Pool *BSSolv__pool;
+typedef Repo *BSSolv__repo;
+typedef Expander *BSSolv__expander;
+
+static Id buildservice_id;
+static Id buildservice_repocookie;
+static Id buildservice_external;
+static Id buildservice_dodurl;
+static Id expander_directdepsend;
+static Id buildservice_dodcookie;
+
+/* make sure bit n is usable */
+#define MAPEXP(m, n) ((m)->size < (((n) + 8) >> 3) ? map_grow(m, n + 256) : 0)
+
+#define REPOCOOKIE "buildservice repo 1.1"
+
+static int
+myrepowritefilter(Repo *repo, Repokey *key, void *kfdata)
+{
+ int i;
+ if (key->name == SOLVABLE_URL)
+ return KEY_STORAGE_DROPPED;
+ if (key->name == SOLVABLE_HEADEREND)
+ return KEY_STORAGE_DROPPED;
+ if (key->name == SOLVABLE_PACKAGER)
+ return KEY_STORAGE_DROPPED;
+ if (key->name == SOLVABLE_GROUP)
+ return KEY_STORAGE_DROPPED;
+ if (key->name == SOLVABLE_LICENSE)
+ return KEY_STORAGE_DROPPED;
+ if (key->name == SOLVABLE_PKGID)
+ return KEY_STORAGE_INCORE;
+ if (key->name == SOLVABLE_CHECKSUM)
+ return KEY_STORAGE_INCORE;
+ i = repo_write_stdkeyfilter(repo, key, kfdata);
+ if (i == KEY_STORAGE_VERTICAL_OFFSET)
+ return KEY_STORAGE_DROPPED;
+ return i;
+}
+
+static inline char *
+hvlookupstr(HV *hv, const char *key, int keyl)
+{
+ SV **svp = hv_fetch(hv, key, keyl, 0);
+ if (!svp)
+ return 0;
+ return SvPV_nolen(*svp);
+}
+
+static inline AV *
+hvlookupav(HV *hv, const char *key, int keyl)
+{
+ SV *sv, **svp = hv_fetch(hv, key, keyl, 0);
+ if (!svp)
+ return 0;
+ sv = *svp;
+ if (!sv || !SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV)
+ return 0;
+ return (AV *)SvRV(sv);
+}
+
+static Id
+makeevr(Pool *pool, char *e, char *v, char *r)
+{
+ char *s;
+
+ if (!v)
+ return 0;
+ if (e && !strcmp(e, "0"))
+ e = 0;
+ if (e)
+ s = pool_tmpjoin(pool, e, ":", v);
+ else
+ s = v;
+ if (r)
+ s = pool_tmpjoin(pool, s, "-", r);
+ return pool_str2id(pool, s, 1);
+}
+
+static inline char *
+avlookupstr(AV *av, int n)
+{
+ SV **svp = av_fetch(av, n, 0);
+ if (!svp)
+ return 0;
+ return SvPV_nolen(*svp);
+}
+
+static inline Id
+id2name(Pool *pool, Id id)
+{
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ id = rd->name;
+ }
+ return id;
+}
+
+static Id
+dep2id(Pool *pool, char *s)
+{
+ char *n;
+ Id id;
+ int flags;
+
+ if ((n = strchr(s, '|')) != 0)
+ {
+ id = dep2id(pool, n + 1);
+ *n = 0;
+ id = pool_rel2id(pool, dep2id(pool, s), id, REL_OR, 1);
+ *n = '|';
+ return id;
+ }
+ while (*s == ' ' || *s == '\t')
+ s++;
+ n = s;
+ while (*s && *s != ' ' && *s != '\t' && *s != '<' && *s != '=' && *s != '>')
+ s++;
+#ifdef REL_MULTIARCH
+ if (s - n > 4 && s[-4] == ':' && !strncmp(s - 4, ":any", 4))
+ {
+ id = pool_strn2id(pool, n, s - n - 4, 1);
+ id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
+ }
+ else
+#endif
+ id = pool_strn2id(pool, n, s - n, 1);
+ if (!*s)
+ return id;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ flags = 0;
+ for (;;s++)
+ {
+ if (*s == '<')
+ flags |= REL_LT;
+ else if (*s == '=')
+ flags |= REL_EQ;
+ else if (*s == '>')
+ flags |= REL_GT;
+ else
+ break;
+ }
+ if (!flags)
+ return id;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ n = s;
+ while (*s && *s != ' ' && *s != '\t')
+ s++;
+ return pool_rel2id(pool, id, pool_strn2id(pool, n, s - n, 1), flags, 1);
+}
+
+static inline Offset
+importdeps(HV *hv, const char *key, int keyl, Repo *repo)
+{
+ Pool *pool = repo->pool;
+ int i;
+ AV *av = hvlookupav(hv, key, keyl);
+ Offset off = 0;
+ if (av)
+ {
+ for (i = 0; i <= av_len(av); i++)
+ {
+ char *str = avlookupstr(av, i);
+ if (str)
+ off = repo_addid_dep(repo, off, dep2id(pool, str), 0);
+ }
+ }
+ return off;
+}
+
+void
+exportdeps(HV *hv, const char *key, int keyl, Repo *repo, Offset off, Id skey)
+{
+ Pool *pool = repo->pool;
+ AV *av;
+ Id id, *pp;
+ const char *str;
+
+ if (!off || !repo->idarraydata[off])
+ return;
+ pp = repo->idarraydata + off;
+ av = 0;
+ while ((id = *pp++))
+ {
+ if (id == SOLVABLE_FILEMARKER)
+ break;
+ str = pool_dep2str(pool, id);
+ if (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ if (skey == SOLVABLE_CONFLICTS && rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
+ {
+ if (!strncmp(str, "namespace:", 10))
+ str += 10;
+ }
+ if (skey == SOLVABLE_SUPPLEMENTS)
+ {
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_FILESYSTEM)
+ {
+ if (!strncmp(str, "namespace:", 10))
+ str += 10;
+ }
+ else if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_MODALIAS)
+ {
+ if (!strncmp(str, "namespace:", 10))
+ str += 10;
+ }
+ else if (rd->flags == REL_AND)
+ {
+ /* either packageand chain or modalias */
+ str = 0;
+ if (ISRELDEP(rd->evr))
+ {
+ Reldep *mrd = GETRELDEP(pool, rd->evr);
+ if (mrd->flags == REL_NAMESPACE && mrd->name == NAMESPACE_MODALIAS)
+ {
+ str = pool_tmpjoin(pool, "modalias(", pool_dep2str(pool, rd->name), ":");
+ str = pool_tmpappend(pool, str, pool_dep2str(pool, mrd->evr), ")");
+ }
+ else if (mrd->flags >= 8)
+ continue;
+ }
+ if (!str)
+ {
+ /* must be and chain */
+ str = pool_dep2str(pool, rd->evr);
+ for (;;)
+ {
+ id = rd->name;
+ if (!ISRELDEP(id))
+ break;
+ rd = GETRELDEP(pool, id);
+ if (rd->flags != REL_AND)
+ break;
+ str = pool_tmpjoin(pool, pool_dep2str(pool, rd->evr), ":", str);
+ }
+ str = pool_tmpjoin(pool, pool_dep2str(pool, id), ":", str);
+ str = pool_tmpjoin(pool, "packageand(", str, ")");
+ }
+ }
+ else if (rd->flags >= 8)
+ continue;
+ }
+ }
+ if (skey == SOLVABLE_REQUIRES)
+ {
+ if (id == SOLVABLE_PREREQMARKER)
+ continue;
+ if (*str == 'r' && !strncmp(str, "rpmlib(", 7))
+ continue;
+ }
+ if (!av)
+ av = newAV();
+ av_push(av, newSVpv(str, 0));
+ }
+ if (av)
+ (void)hv_store(hv, key, keyl, newRV_noinc((SV*)av), 0);
+}
+
+void
+data2solvables(Repo *repo, Repodata *data, HV *rhv)
+{
+ Pool *pool = repo->pool;
+ SV *sv;
+ HV *hv;
+ char *str, *key;
+ I32 keyl;
+ Id p;
+ Solvable *s;
+
+ hv_iterinit(rhv);
+ while ((sv = hv_iternextsv(rhv, &key, &keyl)) != 0)
+ {
+ if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV)
+ continue;
+ hv = (HV *)SvRV(sv);
+ str = hvlookupstr(hv, "name", 4);
+ if (!str)
+ continue; /* need to have a name */
+ p = repo_add_solvable(repo);
+ s = pool_id2solvable(pool, p);
+ s->name = pool_str2id(pool, str, 1);
+ str = hvlookupstr(hv, "arch", 4);
+ if (!str)
+ str = ""; /* dummy, need to have arch */
+ s->arch = pool_str2id(pool, str, 1);
+ s->evr = makeevr(pool, hvlookupstr(hv, "epoch", 5), hvlookupstr(hv, "version", 7), hvlookupstr(hv, "release", 7));
+ str = hvlookupstr(hv, "path", 4);
+ if (str)
+ {
+ char *ss = strrchr(str, '/');
+ if (ss)
+ {
+ *ss = 0;
+ repodata_set_str(data, p, SOLVABLE_MEDIADIR, str);
+ *ss++ = '/';
+ }
+ else
+ ss = str;
+ repodata_set_str(data, p, SOLVABLE_MEDIAFILE, ss);
+ }
+ str = hvlookupstr(hv, "id", 2);
+ if (str)
+ repodata_set_str(data, p, buildservice_id, str);
+ str = hvlookupstr(hv, "source", 6);
+ if (str)
+ repodata_set_poolstr(data, p, SOLVABLE_SOURCENAME, str);
+ str = hvlookupstr(hv, "hdrmd5", 6);
+ if (str && strlen(str) == 32)
+ repodata_set_checksum(data, p, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, str);
+ s->provides = importdeps(hv, "provides", 8, repo);
+ s->obsoletes = importdeps(hv, "obsoletes", 9, repo);
+ s->conflicts = importdeps(hv, "conflicts", 9, repo);
+ s->requires = importdeps(hv, "requires", 8, repo);
+ s->recommends = importdeps(hv, "recommends", 10, repo);
+ s->suggests = importdeps(hv, "suggests", 8, repo);
+ s->supplements = importdeps(hv, "supplements", 11, repo);
+ s->enhances = importdeps(hv, "enhances", 8, repo);
+ if (!s->evr && s->provides)
+ {
+ /* look for self provides */
+ Id pro, *prop = s->repo->idarraydata + s->provides;
+ while ((pro = *prop++) != 0)
+ {
+ Reldep *rd;
+ if (!ISRELDEP(pro))
+ continue;
+ rd = GETRELDEP(pool, pro);
+ if (rd->name == s->name && rd->flags == REL_EQ)
+ s->evr = rd->evr;
+ }
+ }
+ if (s->evr)
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ str = hvlookupstr(hv, "checksum", 8);
+ if (str)
+ {
+ char *cp, typebuf[8];
+ Id ctype;
+ if (*str != ':' && (cp = strchr(str, ':')) != 0 && cp - str < sizeof(typebuf))
+ {
+ strncpy(typebuf, str, cp - str);
+ typebuf[cp - str] = 0;
+ ctype = solv_chksum_str2type(typebuf);
+ if (ctype)
+ repodata_set_checksum(data, p, SOLVABLE_CHECKSUM, ctype, cp + 1);
+ }
+ }
+ }
+
+ repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
+ str = hvlookupstr(rhv, "/url", 4);
+ if (str)
+ repodata_set_str(data, SOLVID_META, buildservice_dodurl, str);
+ str = hvlookupstr(rhv, "/dodcookie", 10);
+ if (str)
+ repodata_set_str(data, SOLVID_META, buildservice_dodcookie, str);
+}
+
+static SV *
+retrieve(unsigned char **srcp, STRLEN *srclp, int depth)
+{
+ SV *sv, *rv;
+ AV *av;
+ HV *hv;
+ unsigned char *src = *srcp;
+ STRLEN srcl = *srclp;
+ int type;
+ unsigned int i, len;
+ STRLEN size;
+
+ if (depth > 10)
+ return 0;
+ if (srcl-- == 0)
+ return 0;
+ type = *src++;
+ switch (type)
+ {
+ case 1:
+ if (srcl < 4)
+ return 0;
+ size = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
+ srcl -= 4;
+ src += 4;
+ if (srcl < size)
+ return 0;
+ sv = NEWSV(10002, size);
+ sv_setpvn(sv, (char *)src, size);
+ srcl -= size;
+ src += size;
+ break;
+ case 2:
+ if (srcl < 4)
+ return 0;
+ len = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
+ srcl -= 4;
+ src += 4;
+ if (len > srcl)
+ return 0;
+ av = newAV();
+ if (len)
+ av_extend(av, len);
+ for (i = 0; i < len; i++)
+ {
+ sv = retrieve(&src, &srcl, depth + 1);
+ if (!sv)
+ return 0;
+ if (av_store(av, i, sv) == 0)
+ return 0;
+ }
+ sv = (SV *)av;
+ break;
+ case 3:
+ if (srcl < 4)
+ return 0;
+ len = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
+ srcl -= 4;
+ src += 4;
+ if (len > srcl)
+ return 0;
+ hv = newHV();
+ if (len)
+ hv_ksplit(hv, len + 1);
+ for (i = 0; i < len; i++)
+ {
+ sv = retrieve(&src, &srcl, depth + 1);
+ if (!sv)
+ return 0;
+ if (srcl < 4)
+ return 0;
+ size = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
+ srcl -= 4;
+ src += 4;
+ if (srcl < size)
+ return 0;
+ if (hv_store(hv, (char *)src, (U32)size, sv, 0) == 0)
+ return 0;
+ srcl -= size;
+ src += size;
+ }
+ sv = (SV *)hv;
+ break;
+ case 4:
+ rv = NEWSV(10002, 0);
+ sv = retrieve(&src, &srcl, depth + 1);
+ if (!sv)
+ return 0;
+ sv_upgrade(rv, SVt_RV);
+ SvRV_set(rv, sv);
+ SvROK_on(rv);
+ sv = rv;
+ break;
+ case 10:
+ if (srcl-- == 0)
+ return 0;
+ size = *src++;
+ if (srcl < size)
+ return 0;
+ sv = NEWSV(10002, size);
+ sv_setpvn(sv, (char *)src, size);
+ srcl -= size;
+ src += size;
+ break;
+ default:
+ /* fprintf(stderr, "unknown tag %d\n", type); */
+ return 0;
+ }
+ *srcp = src;
+ *srclp = srcl;
+ return sv;
+}
+
+static void
+expander_dbg(Expander *xp, const char *format, ...)
+{
+ va_list args;
+ char buf[1024];
+ int l;
+ if (!xp->debug)
+ return;
+ va_start(args, format);
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+ printf("%s", buf);
+ l = strlen(buf);
+ if (buf[0] != ' ' || (l && buf[l - 1] == '\n'))
+ fflush(stdout);
+ if (l >= xp->debugstrf) /* >= because of trailing \0 */
+ {
+ xp->debugstr = solv_realloc(xp->debugstr, xp->debugstrl + l + 1024);
+ xp->debugstrf = l + 1024;
+ }
+ strcpy(xp->debugstr + xp->debugstrl, buf);
+ xp->debugstrl += l;
+ xp->debugstrf -= l;
+}
+
+static const char *
+expander_solvid2name(Expander *xp, Id p)
+{
+ const char *n = pool_id2str(xp->pool, xp->pool->solvables[p].name);
+ Repo *r;
+ if (!xp->debug)
+ return n;
+ r = xp->pool->solvables[p].repo;
+ if (!r)
+ return n;
+ return pool_tmpjoin(xp->pool, n, "@", r->name);
+}
+
+static inline void
+expander_installed(Expander *xp, Id p, Map *installed, Map *conflicts, Queue *conflictsinfo, int *cidone, Queue *out, Queue *todo)
+{
+ Pool *pool = xp->pool;
+ Solvable *s = pool->solvables + p;
+ Id req, id, *reqp, con, *conp;
+ const char *n;
+
+ MAPSET(installed, p);
+ queue_push(out, p);
+ if (MAPTST(&xp->conflicts, s->name))
+ {
+ int i;
+ for (i = 0; i < xp->conflictsq.count; i++)
+ {
+ Id p2, pp2;
+ Id id = xp->conflictsq.elements[i];
+ if (id != s->name)
+ continue;
+ id = xp->conflictsq.elements[i ^ 1];
+ FOR_PROVIDES(p2, pp2, id)
+ {
+ if (pool->solvables[p2].name == id)
+ {
+ MAPEXP(conflicts, pool->nsolvables);
+ MAPSET(conflicts, p2);
+ }
+ }
+ }
+ }
+ if (s->requires)
+ {
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ continue;
+ id = id2name(pool, req);
+ if (!xp->ignoreignore)
+ {
+ if (MAPTST(&xp->ignored, id))
+ continue;
+ if (MAPTST(&xp->ignoredx, id))
+ {
+ Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, s->name), ":", pool_id2str(pool, id)), 0);
+ if (xid && MAPTST(&xp->ignored, xid))
+ continue;
+ }
+ }
+ n = pool_id2str(pool, id);
+ if (!strncmp(n, "rpmlib(", 7))
+ {
+ MAPEXP(&xp->ignored, id);
+ MAPSET(&xp->ignored, id);
+ continue;
+ }
+ if (*n == '/')
+ {
+ if (!xp->havefileprovides || pool->whatprovides[id] <= 1)
+ {
+ MAPEXP(&xp->ignored, id);
+ MAPSET(&xp->ignored, id);
+ continue;
+ }
+ }
+ queue_push2(todo, req, p);
+ }
+ }
+ if (!xp->ignoreconflicts)
+ {
+ if (s->conflicts)
+ {
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ Id p2, pp2;
+ FOR_PROVIDES(p2, pp2, con)
+ {
+ if (p2 == p)
+ continue;
+ MAPEXP(conflicts, pool->nsolvables);
+ MAPSET(conflicts, p2);
+ if (xp->debug)
+ queue_push2(conflictsinfo, p2, p);
+ }
+ }
+ }
+ if (s->obsoletes)
+ {
+ conp = s->repo->idarraydata + s->obsoletes;
+ while ((con = *conp++) != 0)
+ {
+ Id p2, pp2;
+ FOR_PROVIDES(p2, pp2, con)
+ {
+ if (p2 == p || !pool_match_nevr(pool, pool->solvables + p2, con))
+ continue;
+ MAPEXP(conflicts, pool->nsolvables);
+ MAPSET(conflicts, p2);
+ if (xp->debug)
+ queue_push2(conflictsinfo, p2, -p);
+ }
+ }
+ }
+ if (xp->debug)
+ *cidone = out->count;
+ }
+}
+
+static inline int
+expander_checkconflicts(Expander *xp, Id p, Map *installed, Id *conflicts, int isobsoletes)
+{
+ Pool *pool = xp->pool;
+ Id con, p2, pp2;
+
+ if (xp->ignoreconflicts)
+ return 0;
+ while ((con = *conflicts++) != 0)
+ {
+ FOR_PROVIDES(p2, pp2, con)
+ {
+ if (p == p2)
+ continue;
+ if (isobsoletes && !pool_match_nevr(pool, pool->solvables + p2, con))
+ continue;
+ if (MAPTST(installed, p2))
+ return p2;
+ }
+ }
+ return 0;
+}
+
+static void
+expander_updateconflictsinfo(Expander *xp, Queue *conflictsinfo, int *cidone, Queue *out)
+{
+ Pool *pool = xp->pool;
+ int i;
+ if (xp->ignoreconflicts)
+ return;
+ for (i = *cidone; i < out->count; i++)
+ {
+ Id p, p2, pp2;
+ Id con, *conp;
+ Solvable *s;
+ p = out->elements[i];
+ s = pool->solvables + p;
+ /* keep in sync with expander_installed! */
+ if (s->conflicts)
+ {
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ FOR_PROVIDES(p2, pp2, con)
+ {
+ if (p2 == p)
+ continue;
+ queue_push2(conflictsinfo, p2, p);
+ }
+ }
+ }
+ if (s->obsoletes)
+ {
+ conp = s->repo->idarraydata + s->obsoletes;
+ while ((con = *conp++) != 0)
+ {
+ FOR_PROVIDES(p2, pp2, con)
+ {
+ if (p2 == p || !pool_match_nevr(pool, pool->solvables + p2, con))
+ continue;
+ queue_push2(conflictsinfo, p2, -p);
+ }
+ }
+ }
+ }
+ *cidone = out->count;
+}
+
+static inline int
+findconflictsinfo(Queue *conflictsinfo, Id p)
+{
+ int i;
+
+ for (i = 0; i < conflictsinfo->count; i++)
+ if (conflictsinfo->elements[i] == p)
+ return conflictsinfo->elements[i + 1];
+ return 0;
+}
+
+#define ERROR_NOPROVIDER 1
+#define ERROR_CHOICE 2
+#define ERROR_CONFLICTINGPROVIDER 3
+#define ERROR_CONFLICTINGPROVIDERS 4
+#define ERROR_PROVIDERINFO 5
+#define ERROR_PROVIDERINFO2 6
+
+
+int
+expander_expand(Expander *xp, Queue *in, Queue *out, Queue *inconfl)
+{
+ Pool *pool = xp->pool;
+ Queue todo, errors, cerrors, qq, posfoundq;
+ Map installed;
+ Map conflicts;
+ Queue conflictsinfo;
+ int cidone;
+ Solvable *s;
+ Id q, p, pp;
+ int i, j, nerrors, doamb, ambcnt;
+ Id id, who, whon, pn;
+ Id conflprov, conflprovpc;
+
+ map_init(&installed, pool->nsolvables);
+ map_init(&conflicts, 0);
+ queue_init(&conflictsinfo);
+ queue_init(&todo);
+ queue_init(&qq);
+ queue_init(&errors);
+ queue_init(&cerrors);
+ queue_init(&posfoundq);
+
+ queue_empty(out);
+ cidone = 0;
+
+ if (inconfl)
+ {
+ for (i = 0; i < inconfl->count; i += 2)
+ {
+ Id con = inconfl->elements[i];
+ FOR_PROVIDES(p, pp, con)
+ {
+ if (inconfl->elements[i + 1] && !pool_match_nevr(pool, pool->solvables + p, con))
+ continue;
+ MAPEXP(&conflicts, pool->nsolvables);
+ MAPSET(&conflicts, p);
+ }
+ }
+ }
+ /* do direct expands */
+ for (i = 0; i < in->count; i++)
+ {
+ id = in->elements[i];
+ if (id == expander_directdepsend)
+ {
+ for (i = i + 1; i < in->count; i++)
+ if (in->elements[i] != expander_directdepsend)
+ queue_push2(&todo, in->elements[i], 0);
+ break;
+ }
+ q = 0;
+ FOR_PROVIDES(p, pp, id)
+ {
+ s = pool->solvables + p;
+ if (!pool_match_nevr(pool, s, id))
+ continue;
+ if (q)
+ {
+ q = 0;
+ break;
+ }
+ q = p;
+ }
+ if (!q)
+ {
+ /* unclear, resolve later */
+ queue_push2(&todo, id, 0);
+ continue;
+ }
+ if (MAPTST(&installed, q))
+ continue;
+ if (conflicts.size && MAPTST(&conflicts, q))
+ {
+ queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
+ queue_push2(&errors, id, 0);
+ if (cidone < out->count)
+ expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
+ queue_push(&errors, ERROR_PROVIDERINFO2);
+ queue_push2(&errors, q, findconflictsinfo(&conflictsinfo, q));
+ continue;
+ }
+ if (pool->solvables[q].conflicts && (pp = expander_checkconflicts(xp, q, &installed, pool->solvables[q].repo->idarraydata + pool->solvables[q].conflicts, 0)) != 0)
+ {
+ queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
+ queue_push2(&errors, id, 0);
+ queue_push(&errors, ERROR_PROVIDERINFO);
+ queue_push2(&errors, q, pp);
+ continue;
+ }
+ if (pool->solvables[q].obsoletes && (pp = expander_checkconflicts(xp, q, &installed, pool->solvables[q].repo->idarraydata + pool->solvables[q].obsoletes, 1)) != 0)
+ {
+ queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
+ queue_push2(&errors, id, 0);
+ queue_push(&errors, ERROR_PROVIDERINFO);
+ queue_push2(&errors, q, -pp);
+ continue;
+ }
+ if (xp->debug)
+ expander_dbg(xp, "added %s because of %s (direct dep)\n", expander_solvid2name(xp, q), pool_dep2str(pool, id));
+ expander_installed(xp, q, &installed, &conflicts, &conflictsinfo, &cidone, out, &todo); /* unique match! */
+ }
+
+ doamb = 0;
+ ambcnt = todo.count;
+ while (todo.count)
+ {
+ id = queue_shift(&todo);
+ who = queue_shift(&todo);
+ if (ambcnt == 0)
+ {
+ if (doamb)
+ break; /* amb pass had no progress, stop */
+ if (xp->debug)
+ expander_dbg(xp, "now doing undecided dependencies\n");
+ doamb = 1; /* start amb pass */
+ ambcnt = todo.count;
+ }
+ else
+ ambcnt -= 2;
+// printf("todo %s %s ambcnt %d\n", pool_id2str(pool, pool->solvables[who].name), pool_dep2str(pool, id), ambcnt);
+// fflush(stdout);
+ whon = who ? pool->solvables[who].name : 0;
+ queue_empty(&qq);
+ conflprov = 0;
+ conflprovpc = 0;
+ FOR_PROVIDES(p, pp, id)
+ {
+ Id pc;
+ if (MAPTST(&installed, p))
+ break;
+ if (who && !xp->ignoreignore)
+ {
+ Id pn = pool->solvables[p].name;
+ if (MAPTST(&xp->ignored, pn))
+ break;
+ if (MAPTST(&xp->ignoredx, pn))
+ {
+ Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
+ if (xid && MAPTST(&xp->ignored, xid))
+ break;
+ }
+ }
+ if (conflicts.size && MAPTST(&conflicts, p))
+ {
+ if (xp->debug)
+ {
+ Id pc = findconflictsinfo(&conflictsinfo, p);
+ if (pc)
+ expander_dbg(xp, "ignoring provider %s of %s because installed %s %s it\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc > 0 ? pc : -pc), pc > 0 ? "conflicts with" : "obsoletes");
+ else
+ expander_dbg(xp, "ignoring conflicted provider %s of %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id));
+ }
+ conflprov = conflprov ? 1 : p;
+ conflprovpc = 0;
+ continue;
+ }
+ if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0)) != 0)
+ {
+ expander_dbg(xp, "ignoring provider %s of %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc));
+ conflprov = conflprov ? 1 : p;
+ conflprovpc = pc;
+ continue;
+ }
+ if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1)) != 0)
+ {
+ expander_dbg(xp, "ignoring provider %s of %s because it obsoletes installed %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc));
+ conflprov = conflprov ? 1 : p;
+ conflprovpc = -pc;
+ continue;
+ }
+ queue_push(&qq, p);
+ }
+ if (p)
+ continue;
+ if (qq.count == 0)
+ {
+ if (!conflprov)
+ {
+ queue_push(&errors, ERROR_NOPROVIDER);
+ queue_push2(&errors, id, who);
+ continue;
+ }
+ /* more work for conflicts */
+ if (conflprov != 1)
+ {
+ /* nice, just one provider */
+ queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
+ queue_push2(&errors, id, who);
+ if (!conflprovpc)
+ {
+ if (cidone < out->count)
+ expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
+ conflprovpc = findconflictsinfo(&conflictsinfo, conflprov);
+ queue_push(&errors, ERROR_PROVIDERINFO2);
+ queue_push2(&errors, conflprov, conflprovpc);
+ }
+ else
+ {
+ queue_push(&errors, ERROR_PROVIDERINFO);
+ queue_push2(&errors, conflprov, conflprovpc);
+ }
+ continue;
+ }
+ /* even more work if all providers conflict */
+ queue_push(&errors, ERROR_CONFLICTINGPROVIDERS);
+ queue_push2(&errors, id, who);
+ if (cidone < out->count)
+ expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
+ FOR_PROVIDES(p, pp, id)
+ {
+ Id pc;
+ if (conflicts.size && MAPTST(&conflicts, p))
+ {
+ pc = findconflictsinfo(&conflictsinfo, p);
+ queue_push(&errors, ERROR_PROVIDERINFO2);
+ queue_push2(&errors, p, pc);
+ continue;
+ }
+ if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0)) != 0)
+ {
+ queue_push(&errors, ERROR_PROVIDERINFO);
+ queue_push2(&errors, p, pc);
+ continue;
+ }
+ if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1)) != 0)
+ {
+ queue_push(&errors, ERROR_PROVIDERINFO);
+ queue_push2(&errors, p, -pc);
+ continue;
+ }
+ }
+ continue;
+ }
+ if (qq.count > 1 && !doamb)
+ {
+ /* try again later */
+ queue_push2(&todo, id, who);
+ if (xp->debug)
+ {
+ expander_dbg(xp, "undecided about %s:%s:", whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
+ for (i = 0; i < qq.count; i++)
+ expander_dbg(xp, " %s", expander_solvid2name(xp, qq.elements[i]));
+ expander_dbg(xp, "\n");
+ }
+ continue;
+ }
+
+ /* prune neg prefers */
+ if (qq.count > 1)
+ {
+ for (i = j = 0; i < qq.count; i++)
+ {
+ p = qq.elements[i];
+ pn = pool->solvables[p].name;
+ if (MAPTST(&xp->preferneg, pn))
+ continue;
+ if (who && MAPTST(&xp->prefernegx, pn))
+ {
+ Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
+ if (xid && MAPTST(&xp->preferneg, xid))
+ continue;
+ }
+ qq.elements[j++] = p;
+ }
+ if (j)
+ queue_truncate(&qq, j);
+ }
+
+ /* prune pos prefers */
+ if (qq.count > 1)
+ {
+ queue_empty(&posfoundq);
+ for (i = j = 0; i < qq.count; i++)
+ {
+ p = qq.elements[i];
+ pn = pool->solvables[p].name;
+ if (MAPTST(&xp->preferpos, pn))
+ {
+ queue_push2(&posfoundq, pn, p);
+ qq.elements[j++] = p;
+ continue;
+ }
+ if (who && MAPTST(&xp->preferposx, pn))
+ {
+ Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
+ if (xid && MAPTST(&xp->preferpos, xid))
+ {
+ queue_push2(&posfoundq, xid, p);
+ qq.elements[j++] = p;
+ continue;
+ }
+ }
+ }
+ if (posfoundq.count == 2)
+ {
+ queue_empty(&qq);
+ queue_push(&qq, posfoundq.elements[1]);
+ }
+ else if (posfoundq.count)
+ {
+ /* found a pos prefer, now find first hit */
+ /* (prefers are ordered) */
+ for (i = 0; i < xp->preferposq.count; i++)
+ {
+ Id xid = xp->preferposq.elements[i];
+ for (j = 0; j < posfoundq.count; j += 2)
+ if (posfoundq.elements[j] == xid)
+ break;
+ if (j < posfoundq.count)
+ {
+ queue_empty(&qq);
+ queue_push(&qq, posfoundq.elements[j + 1]);
+ break;
+ }
+ }
+ }
+ }
+
+ /* prune OR deps */
+ if (qq.count > 1 && ISRELDEP(id) && GETRELDEP(pool, id)->flags == REL_OR)
+ {
+ Id rid = id;
+ for (;;)
+ {
+ Reldep *rd = 0;
+ if (ISRELDEP(rid))
+ {
+ rd = GETRELDEP(pool, rid);
+ if (rd->flags != REL_OR)
+ rd = 0;
+ }
+ if (rd)
+ rid = rd->name;
+ queue_empty(&qq);
+ FOR_PROVIDES(p, pp, rid)
+ queue_push(&qq, p);
+ if (qq.count)
+ break;
+ if (rd)
+ rid = rd->evr;
+ else
+ break;
+ }
+ }
+ if (qq.count > 1)
+ {
+ queue_push(&cerrors, ERROR_CHOICE);
+ queue_push2(&cerrors, id, who);
+ for (i = 0; i < qq.count; i++)
+ queue_push(&cerrors, qq.elements[i]);
+ queue_push(&cerrors, 0);
+ /* try again later */
+ queue_push2(&todo, id, who);
+ continue;
+ }
+ if (xp->debug)
+ expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, qq.elements[0]), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
+ expander_installed(xp, qq.elements[0], &installed, &conflicts, &conflictsinfo, &cidone, out, &todo);
+ doamb = 0;
+ ambcnt = todo.count;
+ queue_empty(&cerrors);
+ }
+ map_free(&installed);
+ map_free(&conflicts);
+ queue_free(&conflictsinfo);
+ nerrors = 0;
+ if (errors.count || cerrors.count)
+ {
+ queue_empty(out);
+ for (i = 0; i < errors.count; i += 3)
+ {
+ queue_push(out, errors.elements[i]);
+ queue_push(out, errors.elements[i + 1]);
+ queue_push(out, errors.elements[i + 2]);
+ nerrors++;
+ }
+ for (i = 0; i < cerrors.count; )
+ {
+ queue_push(out, cerrors.elements[i]);
+ queue_push(out, cerrors.elements[i + 1]);
+ queue_push(out, cerrors.elements[i + 2]);
+ i += 3;
+ while (cerrors.elements[i])
+ {
+ queue_push(out, cerrors.elements[i]);
+ i++;
+ }
+ queue_push(out, 0);
+ i++;
+ nerrors++;
+ }
+ }
+ else
+ {
+ if (todo.count)
+ {
+ fprintf(stderr, "Internal expansion error!\n");
+ queue_empty(out);
+ queue_push(out, ERROR_NOPROVIDER);
+ queue_push(out, 0);
+ queue_push(out, 0);
+ }
+ }
+ queue_free(&todo);
+ queue_free(&qq);
+ queue_free(&errors);
+ queue_free(&cerrors);
+ queue_free(&posfoundq);
+ return nerrors;
+}
+
+static void
+set_disttype(Pool *pool, int disttype)
+{
+ pool_setdisttype(pool, disttype);
+#ifdef POOL_FLAG_HAVEDISTEPOCH
+ /* make newer mandriva work, hopefully there are no ill effects... */
+ pool_set_flag(pool, POOL_FLAG_HAVEDISTEPOCH, disttype == DISTTYPE_RPM ? 1 : 0);
+#endif
+}
+
+static void
+set_disttype_from_location(Pool *pool, Solvable *so)
+{
+ unsigned int medianr;
+ const char *s = solvable_get_location(so, &medianr);
+ int disttype = -1;
+ int sl;
+ if (!s)
+ return;
+ sl = strlen(s);
+ if (disttype < 0 && sl >= 4 && !strcmp(s + sl - 4, ".rpm"))
+ disttype = DISTTYPE_RPM;
+#ifdef DISTTYPE_DEB
+ if (disttype < 0 && sl >= 4 && !strcmp(s + sl - 4, ".deb"))
+ disttype = DISTTYPE_DEB;
+#endif
+#ifdef DISTTYPE_ARCH
+ if (disttype < 0 && sl >= 11 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz")))
+ disttype = DISTTYPE_ARCH;
+#endif
+ if (disttype >= 0 && pool->disttype != disttype)
+ set_disttype(pool, disttype);
+}
+
+void
+create_considered(Pool *pool, Repo *repoonly, Map *considered)
+{
+ Id p, pb,*best;
+ Solvable *s, *sb;
+ int ridx;
+ Repo *repo;
+ int olddisttype = -1;
+ int dodrepo;
+
+ map_init(considered, pool->nsolvables);
+ best = solv_calloc(sizeof(Id), pool->ss.nstrings);
+
+ FOR_REPOS(ridx, repo)
+ {
+ if (repoonly && repo != repoonly)
+ continue;
+ dodrepo = repo_lookup_str(repo, SOLVID_META, buildservice_dodurl) != 0;
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+ continue;
+ pb = best[s->name];
+ if (pb)
+ {
+ sb = pool->solvables + pb;
+ if (s->repo != sb->repo)
+ continue; /* first repo wins */
+ if (s->arch != sb->arch)
+ {
+ int r;
+ if (s->arch == ARCH_NOARCH || s->arch == ARCH_ALL || s->arch == ARCH_ANY)
+ continue;
+ if (sb->arch != ARCH_NOARCH && sb->arch != ARCH_ALL && sb->arch != ARCH_ANY)
+ {
+ /* the strcmp is kind of silly, but works for most archs */
+ r = strcmp(pool_id2str(pool, sb->arch), pool_id2str(pool, s->arch));
+ if (r >= 0)
+ continue;
+ }
+ }
+ else if (s->evr != sb->evr)
+ {
+ /* same repo, check versions */
+ int r;
+ if (olddisttype < 0)
+ {
+ olddisttype = pool->disttype;
+ set_disttype_from_location(pool, s);
+ }
+ r = pool_evrcmp(pool, sb->evr, s->evr, EVRCMP_COMPARE);
+ if (r > 0)
+ continue;
+ else if (r == 0)
+ {
+ r = strcmp(pool_id2str(pool, sb->evr), pool_id2str(pool, s->evr));
+ if (r >= 0)
+ continue;
+ }
+ }
+ }
+ if (dodrepo)
+ {
+ /* we only consider dod packages */
+ const char *bsid = solvable_lookup_str(s, buildservice_id);
+ if (!bsid || strcmp(bsid, "dod") != 0)
+ continue;
+ }
+ if (pb)
+ MAPCLR(considered, pb);
+ best[s->name] = p;
+ MAPSET(considered, p);
+ }
+ /* dodrepos have a second pass: replace dod entries with downloaded ones */
+ if (dodrepo)
+ {
+ const char *bsid;
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+ continue;
+ pb = best[s->name];
+ if (!pb || pb == p)
+ continue;
+ sb = pool->solvables + pb;
+ if (sb->repo != s->repo || sb->name != s->name || sb->arch != s->arch || sb->evr != s->evr)
+ continue;
+ bsid = solvable_lookup_str(s, buildservice_id);
+ if (bsid && strcmp(bsid, "dod") == 0)
+ continue; /* not downloaded */
+ MAPCLR(considered, pb);
+ best[s->name] = p;
+ MAPSET(considered, p);
+ }
+ }
+ }
+ solv_free(best);
+ if (olddisttype >= 0 && pool->disttype != olddisttype)
+ set_disttype(pool, olddisttype);
+}
+
+struct metaline {
+ char *l;
+ int lastoff;
+ int nslash;
+ int killed;
+};
+
+static int metacmp(const void *ap, const void *bp)
+{
+ const struct metaline *a, *b;
+ int r;
+
+ a = ap;
+ b = bp;
+ r = a->nslash - b->nslash;
+ if (r)
+ return r;
+ r = strcmp(a->l + 34, b->l + 34);
+ if (r)
+ return r;
+ r = strcmp(a->l, b->l);
+ if (r)
+ return r;
+ return a - b;
+}
+
+#ifndef REPO_NO_LOCATION
+# define REPO_NO_LOCATION 0
+#endif
+
+Id
+repodata_addbin(Repodata *data, char *prefix, char *s, int sl, char *sid)
+{
+ Id p = 0;
+ char *path;
+#if REPO_NO_LOCATION == 0
+ char *sp;
+#endif
+
+ path = solv_dupjoin(prefix, "/", s);
+ if (sl >= 4 && !strcmp(s + sl - 4, ".rpm"))
+ p = repo_add_rpm(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|RPM_ADD_WITH_PKGID|RPM_ADD_NO_FILELIST|RPM_ADD_NO_RPMLIBREQS);
+ else if (sl >= 4 && !strcmp(s + sl - 4, ".deb"))
+ p = repo_add_deb(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|DEBS_ADD_WITH_PKGID);
+#ifdef ARCH_ADD_WITH_PKGID
+ else if (sl >= 11 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz")))
+ p = repo_add_arch_pkg(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|ARCH_ADD_WITH_PKGID);
+#endif
+ solv_free(path);
+ if (!p)
+ return 0;
+#if REPO_NO_LOCATION != 0
+ repodata_set_location(data, p, 0, 0, s);
+#else
+ if ((sp = strrchr(s, '/')) != 0)
+ {
+ *sp = 0;
+ repodata_set_str(data, p, SOLVABLE_MEDIADIR, s);
+ *sp = '/';
+ }
+ else
+ repodata_delete_uninternalized(data, p, SOLVABLE_MEDIADIR);
+#endif
+ repodata_set_str(data, p, buildservice_id, sid);
+ return p;
+}
+
+static int
+subpack_sort_cmp(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = (Pool *)dp;
+ const Id *a = ap;
+ const Id *b = bp;
+ int r = a[1] - b[1];
+ if (r)
+ return r;
+ r = strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
+ return r ? r : a[0] - b[0];
+}
+
+/* This is an OpenSSL-compatible implementation of the RSA Data Security,
+ * Inc. MD5 Message-Digest Algorithm.
+ *
+ * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
+ * the public domain. */
+
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+#if defined(__i386__) || defined(__vax__)
+#define SET(n) \
+ (*(MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+#else
+#define SET(n) \
+ (ctx->block[(n)] = \
+ (MD5_u32plus)ptr[(n) * 4] | \
+ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+ (ctx->block[(n)])
+#endif
+
+typedef unsigned long MD5_u32plus;
+
+typedef struct {
+ MD5_u32plus lo, hi;
+ MD5_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+} MD5_CTX;
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters. There're no alignment requirements.
+ */
+static void *md5_body(MD5_CTX *ctx, void *data, unsigned long size)
+{
+ unsigned char *ptr;
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ ptr = data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+/* Round 1 */
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+static void md5_init(MD5_CTX *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+static void md5_update(MD5_CTX *ctx, void *data, unsigned long size)
+{
+ MD5_u32plus saved_lo;
+ unsigned long used, free;
+
+ saved_lo = ctx->lo;
+ if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+ ctx->hi++;
+ ctx->hi += size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if (used) {
+ free = 64 - used;
+
+ if (size < free) {
+ memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ memcpy(&ctx->buffer[used], data, free);
+ data = (unsigned char *)data + free;
+ size -= free;
+ md5_body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = md5_body(ctx, data, size & ~(unsigned long)0x3f);
+ size &= 0x3f;
+ }
+
+ memcpy(ctx->buffer, data, size);
+}
+
+static void md5_final(MD5_CTX *ctx, unsigned char *result)
+{
+ unsigned long used, free;
+ used = ctx->lo & 0x3f;
+ ctx->buffer[used++] = 0x80;
+ free = 64 - used;
+ if (free < 8) {
+ memset(&ctx->buffer[used], 0, free);
+ md5_body(ctx, ctx->buffer, 64);
+ used = 0;
+ free = 64;
+ }
+ memset(&ctx->buffer[used], 0, free - 8);
+ ctx->lo <<= 3;
+ ctx->buffer[56] = ctx->lo;
+ ctx->buffer[57] = ctx->lo >> 8;
+ ctx->buffer[58] = ctx->lo >> 16;
+ ctx->buffer[59] = ctx->lo >> 24;
+ ctx->buffer[60] = ctx->hi;
+ ctx->buffer[61] = ctx->hi >> 8;
+ ctx->buffer[62] = ctx->hi >> 16;
+ ctx->buffer[63] = ctx->hi >> 24;
+ md5_body(ctx, ctx->buffer, 64);
+ result[0] = ctx->a;
+ result[1] = ctx->a >> 8;
+ result[2] = ctx->a >> 16;
+ result[3] = ctx->a >> 24;
+ result[4] = ctx->b;
+ result[5] = ctx->b >> 8;
+ result[6] = ctx->b >> 16;
+ result[7] = ctx->b >> 24;
+ result[8] = ctx->c;
+ result[9] = ctx->c >> 8;
+ result[10] = ctx->c >> 16;
+ result[11] = ctx->c >> 24;
+ result[12] = ctx->d;
+ result[13] = ctx->d >> 8;
+ result[14] = ctx->d >> 16;
+ result[15] = ctx->d >> 24;
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+static unsigned int buz_noise[256] =
+{
+ 0x9be502a4U, 0xba7180eaU, 0x324e474fU, 0x0aab8451U, 0x0ced3810U,
+ 0x2158a968U, 0x6bbd3771U, 0x75a02529U, 0x41f05c14U, 0xc2264b87U,
+ 0x1f67b359U, 0xcd2d031dU, 0x49dc0c04U, 0xa04ae45cU, 0x6ade28a7U,
+ 0x2d0254ffU, 0xdec60c7cU, 0xdef5c084U, 0x0f77ffc8U, 0x112021f6U,
+ 0x5f6d581eU, 0xe35ea3dfU, 0x3216bfb4U, 0xd5a3083dU, 0x7e63e9cdU,
+ 0xaa9208f6U, 0xda3f3978U, 0xfe0e2547U, 0x09dfb020U, 0xd97472c5U,
+ 0xbbce2edeU, 0x121aebd2U, 0x0e9fdbebU, 0x7b6f5d9cU, 0x84938e43U,
+ 0x30694f2dU, 0x86b7a7f8U, 0xefaf5876U, 0x263812e6U, 0xb6e48ddfU,
+ 0xce8ed980U, 0x4df591e1U, 0x75257b35U, 0x2f88dcffU, 0xa461fe44U,
+ 0xca613b4dU, 0xd9803f73U, 0xea056205U, 0xccca7a89U, 0x0f2dbb07U,
+ 0xc53e359eU, 0xe80d0137U, 0x2b2d2a5dU, 0xcfc1391aU, 0x2bb3b6c5U,
+ 0xb66aea3cU, 0x00ea419eU, 0xce5ada84U, 0xae1d6712U, 0x12f576baU,
+ 0x117fcbc4U, 0xa9d4c775U, 0x25b3d616U, 0xefda65a8U, 0xaff3ef5bU,
+ 0x00627e68U, 0x668d1e99U, 0x088d0eefU, 0xf8fac24dU, 0xe77457c7U,
+ 0x68d3beb4U, 0x921d2acbU, 0x9410eac9U, 0xd7f24399U, 0xcbdec497U,
+ 0x98c99ae1U, 0x65802b2cU, 0x81e1c3c4U, 0xa130bb09U, 0x17a87badU,
+ 0xa70367d6U, 0x148658d4U, 0x02f33377U, 0x8620d8b6U, 0xbdac25bdU,
+ 0xb0a6de51U, 0xd64c4571U, 0xa4185ba0U, 0xa342d70fU, 0x3f1dc4c1U,
+ 0x042dc3ceU, 0x0de89f43U, 0xa69b1867U, 0x3c064e11U, 0xad1e2c3eU,
+ 0x9660e8cdU, 0xd36b09caU, 0x4888f228U, 0x61a9ac3cU, 0xd9561118U,
+ 0x3532797eU, 0x71a35c22U, 0xecc1376cU, 0xab31e656U, 0x88bd0d35U,
+ 0x423b20ddU, 0x38e4651cU, 0x3c6397a4U, 0x4a7b12d9U, 0x08b1cf33U,
+ 0xd0604137U, 0xb035fdb8U, 0x4916da23U, 0xa9349493U, 0xd83daa9bU,
+ 0x145f7d95U, 0x868531d6U, 0xacb18f17U, 0x9cd33b6fU, 0x193e42b9U,
+ 0x26dfdc42U, 0x5069d8faU, 0x5bee24eeU, 0x5475d4c6U, 0x315b2c0cU,
+ 0xf764ef45U, 0x01b6f4ebU, 0x60ba3225U, 0x8a16777cU, 0x4c05cd28U,
+ 0x53e8c1d2U, 0xc8a76ce5U, 0x8045c1e6U, 0x61328752U, 0x2ebad322U,
+ 0x3444f3e2U, 0x91b8af11U, 0xb0cee675U, 0x55dbff5aU, 0xf7061ee0U,
+ 0x27d7d639U, 0xa4aef8c9U, 0x42ff0e4fU, 0x62755468U, 0x1c6ca3f3U,
+ 0xe4f522d1U, 0x2765fcb3U, 0xe20c8a95U, 0x3a69aea7U, 0x56ab2c4fU,
+ 0x8551e688U, 0xe0bc14c2U, 0x278676bfU, 0x893b6102U, 0xb4f0ab3bU,
+ 0xb55ddda9U, 0xa04c521fU, 0xc980088eU, 0x912aeac1U, 0x08519badU,
+ 0x991302d3U, 0x5b91a25bU, 0x696d9854U, 0x9ad8b4bfU, 0x41cb7e21U,
+ 0xa65d1e03U, 0x85791d29U, 0x89478aa7U, 0x4581e337U, 0x59bae0b1U,
+ 0xe0fc9df3U, 0x45d9002cU, 0x7837464fU, 0xda22de3aU, 0x1dc544bdU,
+ 0x601d8badU, 0x668b0abcU, 0x7a5ebfb1U, 0x3ac0b624U, 0x5ee16d7dU,
+ 0x9bfac387U, 0xbe8ef20cU, 0x8d2ae384U, 0x819dc7d5U, 0x7c4951e7U,
+ 0xe60da716U, 0x0c5b0073U, 0xb43b3d97U, 0xce9974edU, 0x0f691da9U,
+ 0x4b616d60U, 0x8fa9e819U, 0x3f390333U, 0x6f62fad6U, 0x5a32b67cU,
+ 0x3be6f1c3U, 0x05851103U, 0xff28828dU, 0xaa43a56aU, 0x075d7dd5U,
+ 0x248c4b7eU, 0x52fde3ebU, 0xf72e2edaU, 0x5da6f75fU, 0x2f5148d9U,
+ 0xcae2aeaeU, 0xfda6f3e5U, 0xff60d8ffU, 0x2adc02d2U, 0x1dbdbd4cU,
+ 0xd410ad7cU, 0x8c284aaeU, 0x392ef8e0U, 0x37d48b3aU, 0x6792fe9dU,
+ 0xad32ddfaU, 0x1545f24eU, 0x3a260f73U, 0xb724ca36U, 0xc510d751U,
+ 0x4f8df992U, 0x000b8b37U, 0x292e9b3dU, 0xa32f250fU, 0x8263d144U,
+ 0xfcae0516U, 0x1eae2183U, 0xd4af2027U, 0xc64afae3U, 0xe7b34fe4U,
+ 0xdf864aeaU, 0x80cc71c5U, 0x0e814df3U, 0x66cc5f41U, 0x853a497aU,
+ 0xa2886213U, 0x5e34a2eaU, 0x0f53ba47U, 0x718c484aU, 0xfa0f0b12U,
+ 0x33cc59ffU, 0x72b48e07U, 0x8b6f57bcU, 0x29cf886dU, 0x1950955bU,
+ 0xcd52910cU, 0x4cecef65U, 0x05c2cbfeU, 0x49df4f6aU, 0x1f4c3f34U,
+ 0xfadc1a09U, 0xf2d65a24U, 0x117f5594U, 0xde3a84e6U, 0x48db3024U,
+ 0xd10ca9b5U
+};
+
+
+/*
+ * our delta search blocksize
+ *
+ * smaller gives more hits, but increases the hash size
+ *
+ * must be a multiple of 256
+ * must be in range [256,32767]
+ */
+#define DELTA_BSIZE 1024
+
+/* min store block len, smaller blocks are encoded as direct data */
+#define MIN_BSIZE 32
+
+/* min meta block len, must be >= 10 */
+#define MIN_BSIZE_META 32
+
+/* max meta block len, must be <= DELTA_BSIZE */
+#define MAX_BSIZE_META DELTA_BSIZE
+
+/* number of slots per data area */
+#define SLOTS_PER_AREA 4095
+
+
+/* buzhash by Robert C. Uzgalis */
+/* General hash functions. Technical Report TR-92-01, The University
+ of Hong Kong, 1993 */
+
+static unsigned int buzhash(unsigned char *buf)
+{
+ unsigned int x = 0x83d31df4U;
+ int i;
+ for (i = DELTA_BSIZE ; i != 0; i--)
+ x = (x << 1) ^ (x & (1 << 31) ? 1 : 0) ^ buz_noise[*buf++];
+ return x;
+}
+
+static void md5block(unsigned char *buf, int len, unsigned char *out)
+{
+ MD5_CTX ctx;
+ md5_init(&ctx);
+ md5_update(&ctx, buf, (unsigned long)len);
+ md5_final(&ctx, out);
+}
+
+#define HASHCHAIN_START 7
+#define HASHCHAIN_NEXT(h, hh, mask) (((h) + (hh)++) & (mask))
+
+
+struct deltastore {
+ int fd; /* file descriptor */
+ int rdonly; /* store is read only */
+
+ unsigned long long end; /* store file size */
+
+ unsigned long long *offsets; /* the data areas we know about */
+ int noffsets;
+
+ unsigned char *hash; /* our hash */
+ unsigned int hm; /* size of hash */
+ unsigned int hf; /* hash fill */
+ unsigned int hd; /* entries not in hash */
+
+ int freecnt; /* free slots in last slot area */
+ int usedcnt; /* used slots in last slot area */
+ unsigned long long slotsoffset; /* offset of last slot area */
+};
+
+struct deltaout {
+ FILE *fp;
+ struct deltastore *store;
+
+ /* for block coalescence */
+ unsigned long long oldoffset;
+ unsigned long long oldsize;
+
+ /* for offset encoding */
+ unsigned long long lastoffset;
+
+ /* for meta block creation */
+ int outbuf_do_meta; /* create meta blocks */
+ unsigned char outbuf[MAX_BSIZE_META + 16]; /* 16 extra bytes for one encoded block */
+ int outbuf_len;
+ /* offset patching */
+ unsigned long long outbuf_startoffset;
+ int outbuf_startoffset_set;
+ int outbuf_set_len1;
+ int outbuf_set_len2;
+ unsigned long long outbuf_set_offset; /* offset we need to patch in, already encoded */
+};
+
+static inline unsigned long long getu48(unsigned char *d)
+{
+ unsigned long long x = d[0] << 8 | d[1];
+ return (x << 32) | (d[2] << 24 | d[3] << 16 | d[4] << 8 | d[5]);
+}
+
+static inline void putu48(unsigned char *d, unsigned long long x)
+{
+ d[0] = x >> 40;
+ d[1] = x >> 32;
+ d[2] = x >> 24;
+ d[3] = x >> 16;
+ d[4] = x >> 8;
+ d[5] = x;
+}
+
+static inline unsigned int getu32(unsigned char *d)
+{
+ return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+}
+
+static inline void putu32(unsigned char *d, unsigned int x)
+{
+ d[0] = x >> 24;
+ d[1] = x >> 16;
+ d[2] = x >> 8;
+ d[3] = x;
+}
+
+/**
+ ** store handling
+ **/
+
+static int
+finddataarea(struct deltastore *store, unsigned long long offset)
+{
+ int i;
+ for (i = 0; i < store->noffsets; i += 2)
+ if (offset >= store->offsets[i] && offset < store->offsets[i + 1])
+ return i;
+ return -1;
+}
+
+static void
+adddataarea(struct deltastore *store, unsigned long long start, unsigned long long end)
+{
+ unsigned long long *newoffsets;
+ if (store->noffsets && store->offsets[store->noffsets - 1] == start)
+ {
+ store->offsets[store->noffsets - 1] = end;
+ return;
+ }
+ if (store->offsets)
+ newoffsets = realloc(store->offsets, (store->noffsets + 2) * sizeof(unsigned long long));
+ else
+ newoffsets = malloc((store->noffsets + 2) * sizeof(unsigned long long));
+ if (!newoffsets)
+ return;
+ newoffsets[store->noffsets++] = start;
+ newoffsets[store->noffsets++] = end;
+ store->offsets = newoffsets;
+}
+
+static int
+addslotarea(struct deltastore *store, int cnt)
+{
+ unsigned char *slots;
+ if (!cnt || cnt > 65535)
+ return 0;
+ if (store->rdonly)
+ return 0;
+ if ((store->end & 4095) != 0) /* pad to multiple of 4096 */
+ {
+ char pad[4096];
+ int l = 4096 - (store->end & 4095);
+ memset(pad, 0, l);
+ if (pwrite(store->fd, pad, l, store->end) != l)
+ {
+ perror("pwrite pad next slotsarea");
+ return 0;
+ }
+ store->end += l;
+ }
+ if (store->end + (cnt + 1) * 16 >= (1LL << 48))
+ return 0; /* store too big! */
+ slots = calloc(cnt + 1, 16);
+ if (!slots)
+ return 0;
+ memcpy(slots, "OBSDELT", 8);
+ slots[8] = cnt >> 8;
+ slots[9] = cnt;
+ /* write pointer to next slot area */
+ if (store->end)
+ {
+ putu48(slots + 10, store->end);
+ if (pwrite(store->fd, slots, 16, store->slotsoffset) != 16)
+ {
+ perror("pwrite update next slotsarea");
+ free(slots);
+ return 0;
+ }
+ memset(slots + 10, 0, 6);
+ }
+ if (pwrite(store->fd, slots, (cnt + 1) * 16, store->end) != (cnt + 1) * 16)
+ {
+ perror("pwrite new slotarea");
+ free(slots);
+ return 0;
+ }
+ free(slots);
+ store->slotsoffset = store->end;
+ store->end += (cnt + 1) * 16;
+ store->freecnt = cnt;
+ store->usedcnt = 0;
+ return 1;
+}
+
+/*
+ * add a new block to the store.
+ * returns the store offset, 0 on error
+ */
+static unsigned long long
+putinstore(struct deltastore *store, unsigned char *buf, int size, unsigned char *md5, unsigned int hx)
+{
+ unsigned char md5buf[16];
+ unsigned char hp[16];
+ unsigned long long offset;
+
+ unsigned char *hash;
+ unsigned int h, hh, hm;
+
+ if (!size || size > DELTA_BSIZE)
+ return 0;
+
+ if (store->rdonly)
+ return 0;
+ if (store->freecnt == 0 && !addslotarea(store, SLOTS_PER_AREA))
+ return 0;
+
+ /* write data */
+ offset = store->end;
+ if (offset + size >= (1LL << 48))
+ return 0; /* store too big! */
+ if (pwrite(store->fd, buf, size, store->end) != size)
+ {
+ perror("pwrite data");
+ return 0;
+ }
+ adddataarea(store, store->end, store->end + size);
+ store->end += size;
+
+ /* write slot */
+ if (!md5)
+ {
+ md5block(buf, size, md5buf);
+ md5 = md5buf;
+ }
+ hp[0] = size >> 8;
+ hp[1] = size;
+ putu48(hp + 2, offset);
+ if (size == DELTA_BSIZE)
+ {
+ if (!hx)
+ hx = buzhash(buf);
+ putu32(hp + 8, hx);
+ memcpy(hp + 12, md5, 4);
+ }
+ else
+ {
+ hp[0] |= 0x80; /* small block marker */
+ memcpy(hp + 8, md5, 8);
+ hx = getu32(hp + 8); /* needed later */
+ }
+#if 0
+{
+ int j;
+ printf("NEW SLOT");
+ for (j = 0; j < 16; j++)
+ printf(" %02x", hp[j]);
+ printf("\n");
+}
+#endif
+ if (pwrite(store->fd, hp, 16, store->slotsoffset + (store->usedcnt + 1) * 16) != 16)
+ {
+ perror("pwrite slot");
+ return 0;
+ }
+ store->freecnt--;
+ store->usedcnt++;
+
+ /* update hash */
+ hm = store->hm;
+ hash = store->hash;
+ h = hx & hm;
+ hh = HASHCHAIN_START;
+ while (hash[16 * h] != 0)
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ memcpy(hash + 16 * h, hp, 16);
+ store->hf++;
+ return offset;
+}
+
+/* make sure that we found the correct block */
+static int
+checkstore(struct deltastore *store, unsigned long long offset, unsigned char *buf, int size)
+{
+ unsigned char buf2[4096];
+ while (size)
+ {
+ int l = size > 4096 ? 4096 : size;
+ if (pread(store->fd, buf2, l, offset) != l)
+ return 0;
+ if (memcmp(buf2, buf, l) != 0)
+ return 0;
+ size -= l;
+ buf += l;
+ offset += l;
+ }
+ return 1;
+}
+
+/*
+ * try to find a (non-rolling) block in the store. If not found, add it.
+ * returns the store offset, 0 on error
+ */
+static unsigned long long
+reuse_or_add_block(struct deltastore *store, unsigned char *buf, int size)
+{
+ unsigned char md5[16];
+ unsigned int h, hh, hm;
+ unsigned char *hash;
+ unsigned long long offset;
+
+ if (!size || size >= DELTA_BSIZE)
+ return 0; /* only small non-rolling blocks for now */
+ md5block(buf, size, md5);
+ hm = store->hm;
+ hash = store->hash;
+ h = (md5[0] << 24 | md5[1] << 16 | md5[2] << 8 | md5[3]) & hm;
+ hh = HASHCHAIN_START;
+ while (hash[16 * h] != 0)
+ {
+ unsigned char *hp = hash + 16 * h;
+ if (((hp[0] & 0x7f) << 8 | hp[1]) == size && !memcmp(hp + 8, md5, 8))
+ {
+ offset = getu48(hp + 2);
+ if (checkstore(store, offset, buf, size))
+ return offset;
+ }
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ return putinstore(store, buf, size, md5, 0);
+}
+
+/**
+ ** block encoding
+ **/
+
+static int
+encodelonglong(FILE *ofp, unsigned long long x)
+{
+ unsigned long long z = 1;
+ int c;
+ do
+ {
+ z = z << 7 | (x & 127);
+ x >>= 7;
+ }
+ while (x);
+ for (;;)
+ {
+ c = (z & 127) | 128;
+ z >>= 7;
+ if (z == 1)
+ break;
+ if (putc(c, ofp) == EOF)
+ return 0;
+ }
+ if (putc(c ^ 128, ofp) == EOF)
+ return 0;
+ return 1;
+}
+
+static int
+encodelonglong_mem(unsigned char *bp, unsigned long long x)
+{
+ unsigned long long z = 1;
+ int c;
+ int l = 0;
+ do
+ {
+ z = z << 7 | (x & 127);
+ x >>= 7;
+ }
+ while (x);
+ for (;;)
+ {
+ c = (z & 127) | 128;
+ z >>= 7;
+ if (z == 1)
+ break;
+ *bp++ = c;
+ l++;
+ }
+ *bp = c ^ 128;;
+ return l + 1;
+}
+
+
+#if 1
+/* fancy delta conversion, seems to work better than the simple xor */
+static inline unsigned long long
+encodeoffset(unsigned long long oldoffset, unsigned long long offset)
+{
+ if (oldoffset & (1LL << 47))
+ {
+ offset ^= ((1LL << 48) - 1);
+ oldoffset ^= ((1LL << 48) - 1);
+ }
+ if (offset < oldoffset * 2)
+ {
+ if (offset < oldoffset)
+ offset = (oldoffset - offset - 1) << 1 | 1;
+ else
+ offset = (offset - oldoffset) << 1;
+ }
+ return offset;
+}
+
+static inline unsigned long long
+decodeoffset(unsigned long long oldoffset, unsigned long long offset)
+{
+ int neg = oldoffset & (1LL << 47) ? ((1LL << 48) - 1) : 0;
+ oldoffset ^= neg;
+ if (offset < oldoffset * 2)
+ {
+ if (offset & 1)
+ offset = oldoffset - ((offset >> 1) + 1);
+ else
+ offset = oldoffset + (offset >> 1);
+ }
+ return offset ^ neg;
+}
+
+#else
+static inline unsigned long long
+encodeoffset(unsigned long long oldoffset, unsigned long long offset)
+{
+ return oldoffset ^ offset;
+}
+
+static inline unsigned long long
+decodeoffset(unsigned long long oldoffset, unsigned long long offset)
+{
+ return oldoffset ^ offset;
+}
+#endif
+
+static int
+flushoutbuf(struct deltaout *out)
+{
+ if (!out->outbuf_len)
+ return 1;
+ if (out->outbuf_len >= MAX_BSIZE_META)
+ return 0;
+
+ if (out->outbuf_len >= MIN_BSIZE_META)
+ {
+ /* put as meta block into store! */
+ int size = out->outbuf_len;
+ unsigned long long offset;
+#if 0
+ printf("META size %d\n", out->outbuf_len);
+#endif
+ offset = reuse_or_add_block(out->store, out->outbuf, size);
+ if (!offset)
+ return 0;
+ /* encode meta block into outbuf */
+ if (out->outbuf_startoffset_set)
+ out->lastoffset = out->outbuf_startoffset;
+ out->outbuf[0] = 15; /* meta */
+ out->outbuf_len = 1;
+ out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, size);
+ out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, encodeoffset(out->lastoffset, offset));
+ out->lastoffset = offset + size;
+ out->outbuf_startoffset_set = 0;
+ }
+
+ if (out->outbuf_startoffset_set)
+ {
+ /* tricky: fix up first offset! */
+ unsigned char buf[9];
+ int l = encodelonglong_mem(buf, out->outbuf_set_offset);
+ if (fwrite(out->outbuf, out->outbuf_set_len1, 1, out->fp) != 1)
+ return 0;
+ if (fwrite(buf, l, 1, out->fp) != 1)
+ return 0;
+ if (out->outbuf_set_len2 < out->outbuf_len && fwrite(out->outbuf + out->outbuf_set_len2, out->outbuf_len - out->outbuf_set_len2, 1, out->fp) != 1)
+ return 0;
+ }
+ else if (fwrite(out->outbuf, out->outbuf_len, 1, out->fp) != 1)
+ return 0;
+ out->outbuf_len = 0;
+ out->outbuf_startoffset_set = 0;
+ return 1;
+}
+
+static int
+encodestoreblock_real(struct deltaout *out, unsigned long long offset, unsigned long long size)
+{
+#if 0
+ printf("BLOCK %#llx %llu\n", offset, size);
+#endif
+ if (out->outbuf_do_meta)
+ {
+ int lastlen = out->outbuf_len;
+ /* the following code is needed as we want to use a lastoffset of
+ * zero if this ends up in a meta instruction. So we encode with
+ * lastoffset zero but also store the real lastoffset and byte offsets,
+ * in order to fix up the offset in flushbuf */
+ int set = out->outbuf_startoffset_set;
+ if (!set)
+ {
+ out->outbuf_startoffset_set = 1;
+ out->outbuf_startoffset = out->lastoffset;
+ out->outbuf_set_offset = encodeoffset(out->lastoffset, offset);
+ out->lastoffset = 0;
+ }
+ out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, out->oldsize + 256);
+ if (!set)
+ out->outbuf_set_len1 = out->outbuf_len;
+ out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, encodeoffset(out->lastoffset, offset));
+ if (!set)
+ out->outbuf_set_len2 = out->outbuf_len;
+
+ if (out->outbuf_len >= DELTA_BSIZE)
+ {
+ /* buffer too full. revert changes. flush outbuf. retry */
+ out->outbuf_len = lastlen;
+ if (!set)
+ {
+ out->outbuf_startoffset_set = 0;
+ out->lastoffset = out->outbuf_startoffset;
+ }
+ if (!flushoutbuf(out))
+ return 0;
+ return encodestoreblock_real(out, offset, size);
+ }
+ }
+ else
+ {
+ if (!encodelonglong(out->fp, size + 256))
+ return 0;
+ if (!encodelonglong(out->fp, encodeoffset(out->lastoffset, offset)))
+ return 0;
+ }
+ out->lastoffset = offset + size;
+ return 1;
+}
+
+/* encode a store block instruction
+ * we delay the real encoding so that we can join adjacent blocks
+ */
+static int
+encodestoreblock(struct deltaout *out, unsigned long long offset, unsigned long long size)
+{
+ if (out->oldoffset)
+ {
+ if (out->oldoffset + out->oldsize == offset)
+ {
+ out->oldsize += size;
+ return 1;
+ }
+ if (!encodestoreblock_real(out, out->oldoffset, out->oldsize))
+ return 0;
+ }
+ out->oldoffset = offset; /* block not yet written */
+ out->oldsize = size;
+ return 1;
+}
+
+/* encode a direct data instruction
+ */
+static int
+encodedirect(struct deltaout *out, unsigned char *buf, int size)
+{
+ if (!size)
+ return 1;
+ if (size >= 256 - 16)
+ return 0;
+ if (out->oldoffset)
+ {
+ if (!encodestoreblock(out, 0, 0)) /* flush */
+ return 0;
+ }
+#if 0
+ printf("DIRECT %u\n", size);
+#endif
+ if (out->outbuf_do_meta)
+ {
+ if (out->outbuf_len + 1 + size >= DELTA_BSIZE)
+ {
+ /* buffer too full. flush outbuf */
+ if (!flushoutbuf(out))
+ return 0;
+ }
+ out->outbuf[out->outbuf_len++] = 16 + size;
+ memcpy(out->outbuf + out->outbuf_len, buf, size);
+ out->outbuf_len += size;
+ }
+ else
+ {
+ if (putc(16 + size, out->fp) == EOF)
+ return 0;
+ if (fwrite(buf, size, 1, out->fp) != 1)
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ ** the delta algorithm
+ **/
+
+static unsigned long long
+extendblock(struct deltastore *store, FILE *fp, unsigned long long offset, unsigned long long areaend, unsigned long long maxextend)
+{
+ unsigned char buf[1024];
+ int c, i, bufl;
+ unsigned long long extend = 0;
+
+ if (offset >= areaend)
+ return 0;
+ if (areaend - offset < maxextend)
+ maxextend = areaend - offset;
+ if (!maxextend)
+ return 0;
+ i = bufl = 0;
+ for (;;)
+ {
+ if (i == bufl)
+ {
+ bufl = maxextend > 1024 ? 1024 : maxextend;
+ if (bufl == 0)
+ return extend;
+ if (pread(store->fd, buf, bufl, (off_t)offset) != bufl)
+ return extend;
+ offset += bufl;
+ maxextend -= bufl;
+ i = 0;
+ }
+ c = getc(fp);
+ if (c == EOF)
+ return extend;
+ if (buf[i++] != c)
+ {
+ ungetc(c, fp);
+ return extend;
+ }
+ extend++;
+ }
+}
+
+static unsigned long long
+extendblock_back(struct deltastore *store, unsigned char *data, unsigned long long offset, unsigned long long areastart, unsigned long long maxextend)
+{
+ unsigned char buf[1024];
+ unsigned long long extend = 0;
+ int bufl;
+
+ if (offset <= areastart)
+ return 0;
+ if (offset - areastart < maxextend)
+ maxextend = offset - areastart;
+ if (!maxextend)
+ return 0;
+ bufl = 0;
+ for (;;)
+ {
+ if (bufl == 0)
+ {
+ bufl = maxextend > 1024 ? 1024 : maxextend;
+ if (bufl == 0)
+ return extend;
+ offset -= bufl;
+ if (pread(store->fd, buf, bufl, (off_t)offset) != bufl)
+ return extend;
+ maxextend -= bufl;
+ }
+ if (*--data != buf[--bufl])
+ return extend;
+ extend++;
+ }
+}
+
+static int
+dosimple(struct deltastore *store, struct deltaout *out, unsigned char *buf, int size)
+{
+ unsigned long long offset;
+
+ while (size >= DELTA_BSIZE)
+ {
+ offset = putinstore(store, buf, DELTA_BSIZE, 0, 0);
+ if (!offset || !encodestoreblock(out, offset, DELTA_BSIZE))
+ return 0;
+ size -= DELTA_BSIZE;
+ buf += DELTA_BSIZE;
+ }
+ if (size < MIN_BSIZE)
+ return encodedirect(out, buf, size);
+ offset = reuse_or_add_block(store, buf, size);
+ if (!offset)
+ return 0;
+ return encodestoreblock(out, offset, size);
+}
+
+static int
+dodelta(struct deltastore *store, FILE *fp, struct deltaout *out, unsigned long long size)
+{
+ unsigned char buf[DELTA_BSIZE * 16];
+ unsigned char md5[16];
+ unsigned long long offset, extendf, extendb;
+ unsigned int h, hh, hm, hx;
+ unsigned char *hash;
+ int c, foundit, bi;
+
+#if 0
+ printf("DODELTA\n");
+#endif
+ hm = store->hm;
+ hash = store->hash;
+ while (size)
+ {
+ if (size < DELTA_BSIZE)
+ {
+ if (fread(buf, (int)size, 1, fp) != 1)
+ return 0;
+ if (!dosimple(store, out, buf, (int)size))
+ return 0;
+ break;
+ }
+ /* read a block */
+ bi = 0;
+ if (fread(buf, DELTA_BSIZE, 1, fp) != 1)
+ return 0;
+ size -= DELTA_BSIZE;
+
+ hx = buzhash(buf);
+ foundit = 0;
+
+ /* start rolling */
+ for (;;)
+ {
+ int md5set = 0;
+ /* check if the block at (buf + bi) is in the hash */
+#if 0
+ if (hx != buzhash(buf + bi))
+ abort();
+#endif
+ hh = HASHCHAIN_START;
+ for (h = hx & hm; hash[16 * h] != 0; h = HASHCHAIN_NEXT(h, hh, hm))
+ {
+ unsigned char *hp = hash + 16 * h;
+ /* first check block len */
+ if (hp[0] != (DELTA_BSIZE >> 8) || hp[1] != (DELTA_BSIZE & 0xff))
+ continue;
+ /* then check complete hash value */
+ if (hp[8] != (hx >> 24))
+ continue;
+ if ((hp[8] << 24 | hp[9] << 16 | hp[10] << 8 | hp[11]) != hx)
+ continue;
+ /* then check strong hash */
+ if (!md5set)
+ {
+ md5block(buf + bi, DELTA_BSIZE, md5);
+ md5set = 1;
+ }
+ if (memcmp(hp + 12, md5, 4) != 0)
+ continue;
+ /* looks good. check data */
+ offset = getu48(hp + 2);
+ if (!checkstore(store, offset, buf + bi, DELTA_BSIZE))
+ continue;
+ /* yes! found block in store! */
+ /* try to extend found block */
+ c = finddataarea(store, offset);
+ extendf = extendb = 0;
+ if (c >= 0)
+ {
+ extendf = extendblock(store, fp, offset + DELTA_BSIZE, store->offsets[c + 1], size);
+ size -= extendf; /* extended bytes */
+ extendb = extendblock_back(store, buf + bi, offset, store->offsets[c], bi);
+ offset -= extendb;
+ bi -= extendb;
+ }
+ /* encode data before block */
+ if (bi)
+ {
+ if (!dosimple(store, out, buf, bi))
+ return 0;
+ bi = 0;
+ }
+ /* encode */
+ if (!encodestoreblock(out, offset, DELTA_BSIZE + extendf + extendb))
+ return 0;
+ foundit = 1;
+ break;
+ }
+ if (foundit)
+ break;
+
+ /* not found. move block one byte */
+ if (!size)
+ {
+ if (!dosimple(store, out, buf, bi + DELTA_BSIZE))
+ return 0;
+ break;
+ }
+ c = fgetc(fp);
+ if (c == EOF)
+ return 0;
+ size--;
+ buf[DELTA_BSIZE + bi] = c;
+ hx = (hx << 1) ^ (hx & (1 << 31) ? 1 : 0) ^ buz_noise[c];
+ c = buf[bi++];
+ hx ^= buz_noise[c] ^ (0x83d31df4U ^ 0x07a63be9U);
+ if (bi == sizeof(buf) - DELTA_BSIZE)
+ {
+ /* trim down, but leave one block for backward extension */
+ if (!dosimple(store, out, buf, bi - DELTA_BSIZE))
+ return 0;
+ /* no overlap as the buffer is >= 4 * DELTA_BSIZE */
+ memcpy(buf, buf + bi - DELTA_BSIZE, 2 * DELTA_BSIZE);
+ bi = DELTA_BSIZE;
+ }
+ }
+ }
+ if (!encodestoreblock(out, 0, 0)) /* flush */
+ return 0;
+ if (!flushoutbuf(out))
+ return 0;
+ return 1;
+}
+
+static int
+readdeltastore(struct deltastore *store, int fd, int rdonly, unsigned long long xsize)
+{
+ unsigned char *slots;
+ unsigned char oneslot[16];
+ unsigned long long offset, nextoffset, lastgoodoffset;
+ struct stat st;
+ unsigned long long fsize;
+ unsigned int nslots = 0, hslots;
+ unsigned char *hash;
+ unsigned int hm, h, hh, hf, hd;
+ int isbad = 0;
+ int i, lasti, cnt, maxcnt = 0;
+ unsigned int drop = 0;
+
+ memset(store, 0, sizeof(*store));
+ store->fd = fd;
+ store->rdonly = rdonly;
+ if (fstat(fd, &st))
+ {
+ perror("fstat");
+ return 0;
+ }
+ fsize = (unsigned long long)st.st_size;
+ store->end = fsize;
+
+ /* first pass: find number of used entries */
+ offset = 0;
+ lastgoodoffset = -1;
+ for (;;)
+ {
+ if (offset == fsize)
+ break;
+ if (offset + 16 > fsize)
+ {
+ fprintf(stderr, "WARNING: slot area exceeds file size!\n");
+ isbad = 1;
+ break;
+ }
+ if (pread(fd, oneslot, 16, offset) != 16)
+ return 0;
+ if (memcmp(oneslot, "OBSDELT", 8) != 0)
+ {
+ fprintf(stderr, "WARNING: slot magic error!\n");
+ isbad = 1;
+ break;
+ }
+ cnt = oneslot[8] << 8 | oneslot[9];
+ nextoffset = getu48(oneslot + 10);
+ if (!nextoffset)
+ nextoffset = fsize;
+ offset += (cnt + 1) * 16;
+ if (offset > fsize)
+ {
+ fprintf(stderr, "WARNING: slot area exceeds file size!\n");
+ isbad = 1;
+ break;
+ }
+ nslots += cnt;
+ lastgoodoffset = offset - (cnt + 1) * 16;
+ if (cnt > maxcnt)
+ maxcnt = cnt;
+ if (offset > nextoffset)
+ {
+ fprintf(stderr, "WARNING: end of slots bigger than nextoffset!\n");
+ isbad = 1;
+ break;
+ }
+ offset = nextoffset;
+ }
+
+ if (isbad && !store->rdonly)
+ {
+ fprintf(stderr, "WARNING: fixing up bad slots!\n");
+ if (lastgoodoffset == -1)
+ {
+ /* worst case: first slots area is damaged */
+ memset(oneslot, 0, 16);
+ memcpy(oneslot, "OBSDELT", 8);
+ putu48(oneslot + 10, fsize);
+ if (pwrite(store->fd, oneslot, 16, 0) != 16)
+ {
+ perror("pwrite repair first slots area");
+ return 0;
+ }
+ }
+ else
+ {
+ putu48(oneslot + 10, fsize);
+ if (pwrite(store->fd, oneslot + 10, 6, lastgoodoffset + 10) != 6)
+ {
+ perror("pwrite repair bad slots area nextoffset");
+ return 0;
+ }
+ }
+ isbad = 0;
+ }
+
+ slots = calloc(maxcnt + 1, 16);
+ if (!slots)
+ return 0;
+
+ /* find good hash size and allocate hash */
+ hslots = nslots + xsize / 512;
+ while (hslots & (hslots - 1))
+ hslots = hslots & (hslots - 1);
+ if (hslots < 16384)
+ hslots = 16384; /* 1 MByte min mem */
+ while (hslots > 128 * 1024 * 1024) /* 8 GByte max mem */
+ {
+ /* oh no. max size reached. drop half of slots */
+ hslots >>= 1;
+ drop += (drop ? drop : nslots) / 2;
+ }
+ hslots *= 4;
+ store->hm = hm = hslots - 1;
+ store->hash = hash = calloc(hm + 1, 16);
+ if (!hash)
+ {
+ fprintf(stderr, "could not allocate hash (%u MB)\n", (hm + 1) / (1024 * 1024 / 16));
+ free(slots);
+ return 0;
+ }
+
+ /* second pass: fill the hash */
+ offset = 0;
+ hf = hd = 0;
+ for (;;)
+ {
+ int toread = 16 * (maxcnt + 1);
+ if (isbad && lastgoodoffset == -1)
+ break;
+ if (offset >= fsize)
+ break;
+ if (offset + toread > fsize)
+ toread = fsize - offset;
+ if (pread(fd, slots, toread, offset) != toread)
+ {
+ free(slots);
+ return 0;
+ }
+ if (memcmp(slots, "OBSDELT", 8) != 0)
+ break;
+ cnt = oneslot[8] << 8 | oneslot[9];
+ offset += 16 * (cnt + 1);
+ nextoffset = getu48(slots + 10);
+ if (!nextoffset)
+ nextoffset = fsize;
+ if (offset > nextoffset)
+ break;
+ if (offset != nextoffset)
+ adddataarea(store, offset, nextoffset);
+ lasti = 0;
+ for (i = 1; i < cnt + 1; i++)
+ if (slots[16 * i])
+ {
+ unsigned char *hp = slots + 16 * i;
+ int len = (hp[0] & 127) << 8 | hp[1];
+ unsigned long long o = getu48(hp + 2);
+ lasti = i;
+ if (drop)
+ {
+ drop--;
+ hd++;
+ }
+ else if (o >= offset && o + len <= nextoffset)
+ {
+ /* a good one. add to hash. */
+ h = getu32(hp + 8) & hm;
+ hh = HASHCHAIN_START;
+ while (hash[16 * h] != 0)
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ memcpy(hash + 16 * h, hp, 16);
+ hf++;
+ }
+ }
+ store->slotsoffset = offset - 16 * (cnt + 1);
+ store->freecnt = cnt - lasti;
+ store->usedcnt = lasti;
+ if (isbad && lastgoodoffset == store->slotsoffset)
+ break;
+ offset = nextoffset;
+ }
+ store->hf = hf;
+ store->hd = hd;
+#if 0
+ printf("readdeltastore: have %d entries, %d dropped, hash size %d\n", hf, hd, hm + 1);
+#endif
+ free(slots);
+ return 1;
+}
+
+static void
+printdeltastorestats(struct deltastore *store)
+{
+ unsigned int buckets[2048];
+ unsigned int hm, hf, hd;
+ unsigned char *hp;
+ int i, j, bc = 16;
+
+ memset(buckets, 0, sizeof(buckets));
+ hm = store->hm;
+ hf = store->hf;
+ hd = store->hd;
+
+ printf("store size: %llu (%u MB)\n", store->end, (unsigned int)(store->end / (1024 * 1024)));
+ printf("hash mask: 0x%x (%u MB hash mem)\n", hm, (unsigned int)(hm / 65536) + 1);
+ printf("hash entries set: %u (%.2f %%)\n", hf, ((double)hf * 100) / ((double)hm + 1));
+ printf("hash entries dropped: %u (%.2f %%)\n", hd, hd ? ((double)hd * 100) / ((double)hf + (double)hd) : 0.);
+ for (hp = store->hash;; hp += 16)
+ {
+ if (hp[0])
+ buckets[((hp[0] & 0x7f) << 8 | hp[1]) / 16]++;
+ if (!hm--)
+ break;
+ }
+ for (i = 2047; i >= 1; i--)
+ if (buckets[i])
+ break;
+ i++;
+ while (i > 16)
+ {
+ for (j = 0; j < i; j += 2)
+ buckets[j / 2] = buckets[j] + buckets[j + 1];
+ i = (i + 1) / 2;
+ bc *= 2;
+ }
+ printf("block stats:\n");
+ for (j = 0; j < i; j++)
+ printf(" size %#6x - %#6x: %10u\n", j * bc, j * bc + bc - 1, buckets[j]);
+ printf("data areas: %d\n", store->noffsets / 2);
+}
+
+static void
+freedeltastore(struct deltastore *store)
+{
+ if (store->hash)
+ free(store->hash);
+ if (store->offsets)
+ free(store->offsets);
+}
+
+static void
+settimes(int fd, struct stat *st)
+{
+ struct timeval tv[2];
+
+ tv[0].tv_sec = st->st_atime;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = st->st_mtime;
+ tv[1].tv_usec = 0;
+ futimes(fd, tv);
+}
+
+static int
+checkhexcomp(unsigned char *buf)
+{
+ int i, hexcomp = 0;
+ for (i = 0; i < 110; i++)
+ {
+ int c = *buf++;
+ if (c >= '0' && c <= '9')
+ continue;
+ else if (c >= 'A' && c <= 'F')
+ {
+ if (!hexcomp)
+ hexcomp = 1;
+ if (hexcomp != 1)
+ break;
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ if (!hexcomp)
+ hexcomp = 2;
+ if (hexcomp != 2)
+ break;
+ }
+ else
+ break;
+ }
+ if (i < 110)
+ return 0;
+ return hexcomp ? hexcomp : 1;
+}
+
+static unsigned int fromhex(unsigned char *hex)
+{
+ int i;
+ unsigned int x = 0;
+ for (i = 0; i < 8; i++, hex++)
+ {
+ if (*hex >= '0' && *hex <= '9')
+ x = x << 4 | (*hex - '0');
+ else if (*hex >= 'a' && *hex <= 'f')
+ x = x << 4 | (*hex - ('a' - 10));
+ else if (*hex >= 'A' && *hex <= 'F')
+ x = x << 4 | (*hex - ('A' - 10));
+ }
+ return x;
+}
+
+static void
+magic_inode_increment(unsigned char *cpio)
+{
+ unsigned int inode = getu32(cpio + 3);
+ if (inode)
+ putu32(cpio + 3, inode + 1);
+}
+
+static int
+makedelta(struct deltastore *store, FILE *fp, FILE *ofp, unsigned long long fpsize)
+{
+ unsigned char cpiohead[1024 + 16384];
+ unsigned char oldcpio[1024];
+ int trailerseen = 0;
+ int i, j;
+ struct deltaout out;
+
+ if (fpsize >= (1LL << 40))
+ return 0;
+
+ /* init deltaout struct */
+ memset(&out, 0, sizeof(out));
+ out.fp = ofp;
+ out.store = store;
+ out.outbuf_do_meta = 1; /* create meta blocks */
+
+ /* write our header */
+ memset(cpiohead, 0, 32);
+ memcpy(cpiohead, "OBScpio", 8);
+ putu48(cpiohead + 10, fpsize);
+ if (fwrite(cpiohead, 16, 1, ofp) != 1)
+ return 0;
+
+ memset(oldcpio, 0, 1024);
+ while (!trailerseen)
+ {
+ unsigned long long fsize;
+ unsigned int hsize, nsize;
+ int run;
+ int hexcomp;
+ int noff = 110;
+ int zero;
+
+ /* read the header */
+ if (fread(cpiohead, 110, 1, fp) != 1)
+ {
+ fprintf(stderr, "cpio header read error\n");
+ return 0;
+ }
+ if (memcmp(cpiohead, "070701", 6) != 0)
+ {
+ fprintf(stderr, "not a newc cpio archive\n");
+ return 0;
+ }
+ fsize = fromhex(cpiohead + 54);
+ nsize = fromhex(cpiohead + 94);
+ if (nsize > 16384)
+ {
+ fprintf(stderr, "filename size too big\n");
+ return 0;
+ }
+ hsize = noff + nsize;
+ if (hsize & 3)
+ hsize += 4 - (hsize & 3);
+ /* check if we can do hex compression */
+ hexcomp = checkhexcomp(cpiohead);
+ if (hexcomp)
+ {
+ /* do fancy hex compression */
+ cpiohead[0] = 0x07;
+ cpiohead[1] = 0x07;
+ cpiohead[2] = 0x01;
+ for (i = 3; i < 55; i += 4)
+ {
+ unsigned int x = fromhex(cpiohead + i * 2);
+ putu32(cpiohead + i, x);
+ }
+ noff -= 55;
+ hsize -= 55;
+ }
+
+#if 0
+ printf("fsize = %d, nsize = %d, hsize = %d\n", fsize, nsize, hsize);
+#endif
+ if (fread(cpiohead + noff, hsize - noff, 1, fp) != 1)
+ {
+ fprintf(stderr, "cpio header read error\n");
+ return 0;
+ }
+ if (fsize == 0 && nsize == 11 && !memcmp(cpiohead + noff, "TRAILER!!!", 11))
+ {
+ trailerseen = 1;
+ while (hsize < sizeof(cpiohead))
+ {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ cpiohead[hsize++] = c;
+ }
+ if (hsize == sizeof(cpiohead))
+ {
+ fprintf(stderr, "excess trailer data\n");
+ return 0;
+ }
+ }
+ /* check trailing zero */
+ for (zero = 0; zero < 4; zero++)
+ if (cpiohead[hsize - 1 - zero] != 0)
+ break;
+ /* write the header */
+ if (putc(2 + hexcomp, ofp) == EOF)
+ return 0;
+ for (i = 0; i < 1024 && i < hsize; i++)
+ {
+ cpiohead[i] ^= oldcpio[i];
+ oldcpio[i] ^= cpiohead[i];
+ }
+ if (hexcomp)
+ magic_inode_increment(oldcpio);
+ run = 0;
+ hsize -= zero;
+ for (i = 0; i < hsize; i++)
+ {
+ if (cpiohead[i] == 0)
+ {
+ run++;
+ if (i + 1 < hsize)
+ continue;
+ }
+ while (run)
+ {
+ int z = run > 127 ? 127 : run;
+ if (putc(z, ofp) == EOF)
+ return 0;
+ run -= z;
+ }
+ if (cpiohead[i] == 0)
+ break; /* ended in zero */
+ for (j = i; j < hsize - 1; j++)
+ if (cpiohead[j] == 0 && cpiohead[j + 1] == 0)
+ break;
+ if (j == hsize - 1)
+ j = hsize;
+ j -= i;
+ if (j > 123)
+ j = 123;
+ if (putc(j + 128, ofp) == EOF)
+ return 0;
+ while (j-- > 0)
+ {
+ int z = cpiohead[i++];
+ if (putc(z, ofp) == EOF)
+ return 0;
+ }
+ i--;
+ }
+ if (putc(zero ? zero + 251 : 0, ofp) == EOF)
+ return 0;
+ if (fsize)
+ {
+ if (!dodelta(store, fp, &out, fsize))
+ return 0;
+ if ((fsize & 3) != 0)
+ {
+ i = 4 - (fsize & 3);
+ if (putc(4 + i, ofp) == EOF)
+ return 0;
+ while (i--)
+ {
+ if (getc(fp) != 0)
+ {
+ fprintf(stderr, "non-zero padding\n");
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ if (putc(1, ofp) == EOF)
+ return 0;
+ if (fflush(ofp) != 0)
+ return 0;
+ return 1;
+}
+
+static unsigned long long
+expandobscpio_next(FILE *fp)
+{
+ unsigned long long x = 0;
+ int i;
+ for (;;)
+ {
+ i = getc(fp);
+ if (i == EOF)
+ return (unsigned long long)(-1);
+ if ((i & 128) == 0)
+ return x << 7 | i;
+ x = x << 7 | (i ^ 128);
+ }
+}
+
+static unsigned long long
+expandobscpio_next_mem(unsigned char **bp, unsigned int *lp)
+{
+ unsigned long long x = 0;
+ unsigned char *b = *bp;
+ unsigned int l = *lp;
+ int i;
+ for (;;)
+ {
+ if (l == 0)
+ return (unsigned long long)(-1);
+ i = *b++;
+ l--;
+ if ((i & 128) == 0)
+ {
+ *bp = b;
+ *lp = l;
+ return x << 7 | i;
+ }
+ x = x << 7 | (i ^ 128);
+ }
+}
+
+static int
+expandobscpio_direct_mem(unsigned char **bp, unsigned int *lp, void *out, unsigned int outlen)
+{
+ if (*lp < outlen)
+ return 0;
+ if (outlen)
+ memcpy(out, *bp, outlen);
+ *bp += outlen;
+ *lp -= outlen;
+ return 1;
+}
+
+static int
+expandcpiohead(FILE *fp, FILE *ofp, unsigned char *cpio, int hexcomp)
+{
+ int l = 0;
+ int zero;
+ for (;;)
+ {
+ int c = getc(fp);
+ if (c == EOF)
+ return 0;
+ if (c == 0)
+ break;
+ if (c < 128)
+ zero = 1;
+ else if (c >= 252)
+ {
+ zero = -1;
+ c -= 251;
+ }
+ else
+ {
+ zero = 0;
+ c -= 128;
+ }
+ while (c-- > 0)
+ {
+ int x = zero ? 0 : getc(fp);
+ if (x == EOF)
+ return 0;
+ if (l < 1024)
+ {
+ if (zero >= 0)
+ x ^= cpio[l];
+ cpio[l++] = x;
+ }
+ if (hexcomp && l <= 55)
+ {
+ int lettershift = (hexcomp == 1 ? 'A' : 'a') - ('0' + 10);
+ int x1 = (x >> 4) + '0';
+ int x2 = (x & 15) + '0';
+ if (x1 > '9')
+ x1 += lettershift;
+ if (x2 > '9')
+ x2 += lettershift;
+ if (putc(x1, ofp) == EOF || putc(x2, ofp) == EOF)
+ return 0;
+ }
+ else if (putc(x, ofp) == EOF)
+ return 0;
+ }
+ if (zero < 0)
+ break;
+ }
+ if (hexcomp)
+ magic_inode_increment(cpio);
+ return 1;
+}
+
+static int
+expandobscpio(FILE *fp, int fdstore, FILE *ofp)
+{
+ unsigned char magic[16];
+ unsigned char metabuf[16384];
+ unsigned char cpio[1024];
+ unsigned long long o, l;
+ struct stat st;
+ unsigned long long oldoffset = 0;
+ unsigned char *meta = 0;
+ unsigned int metal = 0;
+ unsigned long long oldoffset_meta = 0;
+
+ if (!fp || !ofp || fdstore == -1)
+ return 0;
+ if (fstat(fileno(fp), &st))
+ return 0;
+ if (fread(magic, 16, 1, fp) != 1 || memcmp(magic, "OBScpio", 7) != 0)
+ return 0;
+ memset(cpio, 0, sizeof(cpio));
+ for (;;)
+ {
+ if (meta && !metal)
+ {
+ meta = 0;
+ oldoffset = oldoffset_meta;
+ }
+ if (meta)
+ l = expandobscpio_next_mem(&meta, &metal);
+ else
+ l = expandobscpio_next(fp);
+ if (l == (unsigned long long)(-1))
+ return 0;
+#if 0
+printf("NEXT %d\n", l);
+#endif
+ if (l < 16)
+ {
+ /* first 16 reserved as instructions */
+ if (meta)
+ return 0;
+ if (l == 1) /* EOF */
+ break;
+ if (l == 2 || l == 3 || l == 4) /* CPIO */
+ {
+ if (!expandcpiohead(fp, ofp, cpio, l - 2))
+ return 0;
+ }
+ else if (l == 5 || l == 6 || l == 7) /* ZERO */
+ {
+ l -= 4;
+ while (l--)
+ if (putc(0, ofp) == EOF)
+ return 0;
+ }
+ else if (l == 15) /* META */
+ {
+ l = expandobscpio_next(fp);
+ if (l == (unsigned long long)(-1))
+ return 0;
+ if (l < 16 || l > sizeof(metabuf))
+ return 0;
+ o = expandobscpio_next(fp);
+ if (o == (unsigned long long)(-1))
+ return 0;
+ o = decodeoffset(oldoffset, o);
+ oldoffset_meta = o + l;
+ oldoffset = 0;
+ if (pread(fdstore, metabuf, (size_t)l, (off_t)o) != (size_t)l)
+ return 0;
+ metal = (unsigned int)l;
+ meta = metabuf;
+ }
+ else
+ return 0;
+ }
+ else if (l < 256)
+ {
+ /* direct bytes */
+ l -= 16;
+ if (l)
+ {
+ char buf[256];
+ if (meta)
+ {
+ if (expandobscpio_direct_mem(&meta, &metal, buf, l) != 1)
+ return 0;
+ }
+ else if (fread(buf, (int)l, 1, fp) != 1)
+ return 0;
+ if (fwrite(buf, (int)l, 1, ofp) != 1)
+ return 0;
+ }
+ }
+ else
+ {
+ /* bytes from the store */
+ l -= 256;
+ if (meta)
+ o = expandobscpio_next_mem(&meta, &metal);
+ else
+ o = expandobscpio_next(fp);
+ if (o == (unsigned long long)(-1))
+ return 0;
+ o = decodeoffset(oldoffset, o);
+ oldoffset = o + l;
+ while (l)
+ {
+ char buf[8192];
+ size_t count = l > 8192 ? 8192 : l;
+ if (pread(fdstore, buf, count, (off_t)o) != count)
+ return 0;
+ if (fwrite(buf, count, 1, ofp) != 1)
+ return 0;
+ o += count;
+ l -= count;
+ }
+ }
+ }
+ if (fflush(ofp) != 0)
+ return 0;
+ settimes(fileno(ofp), &st);
+ return 1;
+}
+
+static void
+printobscpioinstr(FILE *fp, int fdstore, int withmeta)
+{
+ unsigned char magic[16];
+ unsigned long long oldoffset = 0, o, l;
+ unsigned char metabuf[16384];
+ unsigned char *meta = 0;
+ unsigned int metal = 0;
+ unsigned long long oldoffset_meta = 0;
+
+ unsigned int stats_cpio = 0;
+ unsigned long long stats_cpio_len = 0;
+ unsigned int stats_direct = 0;
+ unsigned long long stats_direct_len = 0;
+ unsigned int stats_store = 0;
+ unsigned long long stats_store_len = 0;
+ unsigned int stats_zero = 0;
+ unsigned long long stats_zero_len = 0;
+ unsigned int stats_meta = 0;
+ unsigned long long stats_meta_len = 0;
+ unsigned int stats_meta_store = 0;
+ unsigned long long stats_meta_store_len = 0;
+ unsigned int stats_meta_direct = 0;
+ unsigned long long stats_meta_direct_len = 0;
+
+ if (fread(magic, 16, 1, fp) != 1 || memcmp(magic, "OBScpio", 7) != 0)
+ return;
+ for (;;)
+ {
+ if (meta && !metal)
+ {
+ meta = 0;
+ oldoffset = oldoffset_meta;
+ }
+ if (meta)
+ l = expandobscpio_next_mem(&meta, &metal);
+ else
+ l = expandobscpio_next(fp);
+ if (l == (unsigned long long)(-1))
+ return;
+ if (l < 16)
+ {
+ if (meta)
+ return;
+ if (l == 1)
+ {
+ printf("end\n");
+ break;
+ }
+ if (l == 2 || l == 3 || l == 4) /* CPIO HEADER */
+ {
+ printf("cpio%d", (int)l);
+ stats_cpio++;
+ for (;;)
+ {
+ int c = getc(fp);
+ if (c == EOF)
+ return;
+ stats_cpio_len++;
+ if (c == 0)
+ {
+ printf(" (0)");
+ break;
+ }
+ if (c < 128)
+ printf(" [%d]", c);
+ else if (c >= 252)
+ {
+ printf(" (%d)", c - 251);
+ break;
+ }
+ else
+ {
+ c -= 128;
+ printf(" %d", c);
+ stats_cpio_len += c;
+ while (c--)
+ if (getc(fp) == EOF)
+ return;
+ }
+ }
+ printf("\n");
+ }
+ else if (l == 5 || l == 6 || l == 7) /* ZERO */
+ {
+ printf("zero %d\n", (int)l - 4);
+ stats_zero++;
+ stats_zero_len += (int)l - 4;
+ }
+ else if (l == 15) /* META */
+ {
+ l = expandobscpio_next(fp);
+ if (l == (unsigned long long)(-1))
+ return;
+ if (l < 16 || l > sizeof(metabuf))
+ return;
+ o = expandobscpio_next(fp);
+ if (o == (unsigned long long)(-1))
+ return;
+ o = decodeoffset(oldoffset, o);
+ oldoffset = o + l;
+ printf("meta %#llx %llu\n", o, l);
+ stats_meta++;
+ stats_meta_len += l;
+ if (withmeta)
+ {
+ oldoffset_meta = o + l;
+ oldoffset = 0;
+ if (pread(fdstore, metabuf, (size_t)l, (off_t)o) != (size_t)l)
+ return;
+ metal = (unsigned int)l;
+ meta = metabuf;
+ }
+ }
+ else
+ return;
+ continue;
+ }
+ if (meta)
+ printf(" ");
+ if (l < 256)
+ {
+ l -= 16;
+ printf("direct %d\n", (int)l);
+ if (meta)
+ {
+ stats_meta_direct++;
+ stats_meta_direct_len += l;
+ }
+ else
+ {
+ stats_direct++;
+ stats_direct_len += l;
+ }
+ if (meta)
+ {
+ if (l > metal)
+ return;
+ metal -= l;
+ meta += l;
+ }
+ else
+ {
+ while (l--)
+ if (getc(fp) == EOF)
+ return;
+ }
+ continue;
+ }
+ l -= 256;
+ if (meta)
+ o = expandobscpio_next_mem(&meta, &metal);
+ else
+ o = expandobscpio_next(fp);
+ if (o == (unsigned long long)(-1))
+ return;
+ o = decodeoffset(oldoffset, o);
+ oldoffset = o + l;
+ printf("store %#llx %llu\n", o, l);
+ if (meta)
+ {
+ stats_meta_store++;
+ stats_meta_store_len += l;
+ }
+ else
+ {
+ stats_store++;
+ stats_store_len += l;
+ }
+ }
+ printf("stats cpio %u len %llu\n", stats_cpio, stats_cpio_len);
+ printf("stats direct %u len %llu\n", stats_direct, stats_direct_len);
+ if (withmeta)
+ printf("stats meta_direct %u len %llu\n", stats_meta_direct, stats_meta_direct_len);
+ printf("stats store %u len %llu\n", stats_store, stats_store_len);
+ if (withmeta)
+ printf("stats meta_store %u len %llu\n", stats_meta_store, stats_meta_store_len);
+ printf("stats zero %u len %llu\n", stats_zero, stats_zero_len);
+ printf("stats meta %u len %llu\n", stats_meta, stats_meta_len);
+ if (withmeta)
+ printf("stats instr %u\n", stats_cpio + stats_direct + stats_store + stats_zero + stats_meta + 1 + stats_meta_direct + stats_meta_store);
+ printf("stats file_instr %u\n", stats_cpio + stats_direct + stats_store + stats_zero + stats_meta + 1);
+ printf("stats file_data %lld\n", stats_cpio_len + stats_direct_len);
+ printf("stats file_size %lld\n", (unsigned long long)ftell(fp));
+}
+
+MODULE = BSSolv PACKAGE = BSSolv
+
+void
+depsort(HV *deps, SV *mapp, SV *cycp, ...)
+ PPCODE:
+ {
+ int i, j, k, cy, cycstart, nv;
+ SV *sv, **svp;
+ Id id, *e;
+ Id *mark;
+ char **names;
+ Hashtable ht;
+ Hashval h, hh, hm;
+ HV *mhv = 0;
+
+ Queue edata;
+ Queue vedge;
+ Queue todo;
+ Queue cycles;
+
+ if (items == 3)
+ XSRETURN_EMPTY; /* nothing to sort */
+ if (items == 4)
+ {
+ /* only one item */
+ char *s = SvPV_nolen(ST(3));
+ EXTEND(SP, 1);
+ sv = newSVpv(s, 0);
+ PUSHs(sv_2mortal(sv));
+ XSRETURN(1); /* nothing to sort */
+ }
+
+ if (mapp && SvROK(mapp) && SvTYPE(SvRV(mapp)) == SVt_PVHV)
+ mhv = (HV *)SvRV(mapp);
+
+ queue_init(&edata);
+ queue_init(&vedge);
+ queue_init(&todo);
+ queue_init(&cycles);
+
+ hm = mkmask(items);
+ ht = solv_calloc(hm + 1, sizeof(*ht));
+ names = solv_calloc(items, sizeof(char *));
+ nv = 1;
+ for (i = 3; i < items; i++)
+ {
+ char *s = SvPV_nolen(ST(i));
+ h = strhash(s) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ if (!strcmp(names[id], s))
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ if (id)
+ continue; /* had that one before, ignore */
+ id = nv++;
+ ht[h] = id;
+ names[id] = s;
+ }
+
+ /* we now know all vertices, create edges */
+ queue_push(&vedge, 0);
+ queue_push(&edata, 0);
+ for (i = 1; i < nv; i++)
+ {
+ svp = hv_fetch(deps, names[i], strlen(names[i]), 0);
+ sv = svp ? *svp : 0;
+ queue_push(&vedge, edata.count);
+ if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
+ {
+ AV *av = (AV *)SvRV(sv);
+ for (j = 0; j <= av_len(av); j++)
+ {
+ char *s;
+ STRLEN slen;
+
+ svp = av_fetch(av, j, 0);
+ if (!svp)
+ continue;
+ sv = *svp;
+ s = SvPV(sv, slen);
+ if (!s)
+ continue;
+ if (mhv)
+ {
+ /* look up in dep map */
+ svp = hv_fetch(mhv, s, slen, 0);
+ if (svp)
+ {
+ s = SvPV(*svp, slen);
+ if (!s)
+ continue;
+ }
+ }
+ /* look up in hash */
+ h = strhash(s) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ if (!strcmp(names[id], s))
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ if (!id)
+ continue; /* not known, ignore */
+ if (id == i)
+ continue; /* no self edge */
+ queue_push(&edata, id);
+ }
+ }
+ queue_push(&edata, 0);
+ }
+ solv_free(ht);
+
+ if (0)
+ {
+ printf("vertexes: %d\n", vedge.count - 1);
+ for (i = 1; i < vedge.count; i++)
+ {
+ printf("%d %s:", i, names[i]);
+ Id *e = edata.elements + vedge.elements[i];
+ for (; *e; e++)
+ printf(" %d", *e);
+ printf("\n");
+ }
+ }
+
+
+ /* now everything is set up, sort em! */
+ mark = solv_calloc(vedge.count, sizeof(Id));
+ for (i = vedge.count - 1; i; i--)
+ queue_push(&todo, i);
+ EXTEND(SP, vedge.count - 1);
+ while (todo.count)
+ {
+ i = queue_pop(&todo);
+ // printf("check %d\n", i);
+ if (i < 0)
+ {
+ i = -i;
+ mark[i] = 2;
+ sv = newSVpv(names[i], 0);
+ PUSHs(sv_2mortal(sv));
+ continue;
+ }
+ if (mark[i] == 2)
+ continue;
+ if (mark[i] == 0)
+ {
+ int edgestovisit = 0;
+ Id *e = edata.elements + vedge.elements[i];
+ for (; *e; e++)
+ {
+ if (*e == -1)
+ continue; /* broken */
+ if (mark[*e] == 2)
+ continue;
+ if (!edgestovisit++)
+ queue_push(&todo, -i);
+ queue_push(&todo, *e);
+ }
+ if (!edgestovisit)
+ {
+ mark[i] = 2;
+ sv = newSVpv(names[i], 0);
+ PUSHs(sv_2mortal(sv));
+ }
+ else
+ mark[i] = 1;
+ continue;
+ }
+ /* oh no, we found a cycle, record and break it */
+ cy = cycles.count;
+ for (j = todo.count - 1; j >= 0; j--)
+ if (todo.elements[j] == -i)
+ break;
+ cycstart = j;
+ // printf("cycle:\n");
+ for (j = cycstart; j < todo.count; j++)
+ if (todo.elements[j] < 0)
+ {
+ k = -todo.elements[j];
+ mark[k] = 0;
+ queue_push(&cycles, k);
+ // printf(" %d\n", k);
+ }
+ queue_push(&cycles, 0);
+ todo.elements[cycstart] = i;
+ /* break it */
+ for (k = cy; cycles.elements[k]; k++)
+ ;
+ if (!cycles.elements[k])
+ k = cy;
+ j = cycles.elements[k + 1] ? cycles.elements[k + 1] : cycles.elements[cy];
+ k = cycles.elements[k];
+ /* breaking edge from k -> j */
+ // printf("break %d -> %d\n", k, j);
+ e = edata.elements + vedge.elements[k];
+ for (; *e; e++)
+ if (*e == j)
+ break;
+ if (!*e)
+ abort();
+ *e = -1;
+ todo.count = cycstart + 1;
+ }
+
+ /* recored cycles */
+ if (cycles.count && cycp && SvROK(cycp) && SvTYPE(SvRV(cycp)) == SVt_PVAV)
+ {
+ AV *av = (AV *)SvRV(cycp);
+ for (i = 0; i < cycles.count;)
+ {
+ AV *av2 = newAV();
+ for (; cycles.elements[i]; i++)
+ {
+ SV *sv = newSVpv(names[cycles.elements[i]], 0);
+ av_push(av2, sv);
+ }
+ av_push(av, newRV_noinc((SV*)av2));
+ i++;
+ }
+ }
+ queue_free(&cycles);
+
+ queue_free(&edata);
+ queue_free(&vedge);
+ queue_free(&todo);
+ solv_free(mark);
+ solv_free(names);
+ }
+
+void
+gen_meta(AV *subp, ...)
+ PPCODE:
+ {
+ Hashtable ht;
+ Hashval h, hh, hm;
+ char **subpacks;
+ struct metaline *lines, *lp;
+ int nlines;
+ int i, j, cycle, ns;
+ char *s, *s2, *lo;
+ Id id;
+ Queue cycles;
+ Id cycles_buf[64];
+
+ if (items == 1)
+ XSRETURN_EMPTY; /* nothing to generate */
+
+ queue_init_buffer(&cycles, cycles_buf, sizeof(cycles_buf)/sizeof(*cycles_buf));
+ hm = mkmask(av_len(subp) + 2);
+ ht = solv_calloc(hm + 1, sizeof(*ht));
+ subpacks = solv_calloc(av_len(subp) + 2, sizeof(char *));
+ for (j = 0; j <= av_len(subp); j++)
+ {
+ SV **svp = av_fetch(subp, j, 0);
+ if (!svp)
+ continue;
+ s = SvPV_nolen(*svp);
+ h = strhash(s) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ ht[h] = j + 1;
+ subpacks[j + 1] = s;
+ }
+
+ lines = solv_calloc(items - 1, sizeof(*lines));
+ nlines = items - 1;
+ /* lines are of the form "md5sum pkg/pkg..." */
+ for (i = 0, lp = lines; i < nlines; i++, lp++)
+ {
+ s = SvPV_nolen(ST(i + 1));
+ if (strlen(s) < 35 || s[32] != ' ' || s[33] != ' ')
+ croak("gen_meta: bad line %s\n", s);
+ /* count '/' */
+ lp->l = s;
+ ns = 0;
+ cycle = 0;
+ lo = s + 34;
+ for (s2 = lo; *s2; s2++)
+ if (*s2 == '/')
+ {
+ if (!cycle)
+ {
+ *s2 = 0;
+ h = strhash(lo) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ if (!strcmp(lo, subpacks[id]))
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ *s2 = '/';
+ if (id)
+ cycle = 1 + ns;
+ }
+ ns++;
+ lo = s2 + 1;
+ }
+ if (!cycle)
+ {
+ h = strhash(lo) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ if (!strcmp(lo, subpacks[id]))
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ if (id)
+ cycle = 1 + ns;
+ }
+ if (cycle)
+ {
+ lp->killed = 1;
+ if (cycle > 1) /* ignore self cycles */
+ queue_push(&cycles, i);
+ }
+ lp->nslash = ns;
+ lp->lastoff = lo - s;
+ }
+ solv_free(ht);
+ solv_free(subpacks);
+
+ /* if we found cycles, prune em */
+ if (cycles.count)
+ {
+ char *cycledata = 0;
+ int cycledatalen = 0;
+
+ cycledata = solv_extend(cycledata, cycledatalen, 1, 1, 255);
+ cycledata[0] = 0;
+ cycledatalen += 1;
+ hm = mkmask(cycles.count);
+ ht = solv_calloc(hm + 1, sizeof(*ht));
+ for (i = 0; i < cycles.count; i++)
+ {
+ char *se;
+ s = lines[cycles.elements[i]].l + 34;
+ se = strchr(s, '/');
+ if (se)
+ *se = 0;
+ h = strhash(s) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ if (!strcmp(s, cycledata + id))
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ if (id)
+ continue;
+ cycledata = solv_extend(cycledata, cycledatalen, strlen(s) + 1, 1, 255);
+ ht[h] = cycledatalen;
+ strcpy(cycledata + cycledatalen, s);
+ cycledatalen += strlen(s) + 1;
+ if (se)
+ *se = '/';
+ }
+ for (i = 0, lp = lines; i < nlines; i++, lp++)
+ {
+ if (lp->killed || !lp->nslash)
+ continue;
+ lo = strchr(lp->l + 34, '/') + 1;
+ for (s2 = lo; *s2; s2++)
+ if (*s2 == '/')
+ {
+ *s2 = 0;
+ h = strhash(lo) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ if (!strcmp(lo, cycledata + id))
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ *s2 = '/';
+ if (id)
+ {
+ lp->killed = 1;
+ break;
+ }
+ lo = s2 + 1;
+ }
+ if (lp->killed)
+ continue;
+ h = strhash(lo) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ if (!strcmp(lo, cycledata + id))
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ if (id)
+ {
+ lp->killed = 1;
+ }
+ }
+ solv_free(ht);
+ cycledata = solv_free(cycledata);
+ queue_free(&cycles);
+ }
+
+ /* cycles are pruned, now sort array */
+ if (nlines > 1)
+ qsort(lines, nlines, sizeof(*lines), metacmp);
+
+ hm = mkmask(nlines);
+ ht = solv_calloc(hm + 1, sizeof(*ht));
+ for (i = 0, lp = lines; i < nlines; i++, lp++)
+ {
+ if (lp->killed)
+ continue;
+ s = lp->l;
+ h = strnhash(s, 10);
+ h = strhash_cont(s + lp->lastoff, h) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ struct metaline *lp2 = lines + (id - 1);
+ if (!strncmp(lp->l, lp2->l, 32) && !strcmp(lp->l + lp->lastoff, lp2->l + lp2->lastoff))
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ if (id)
+ lp->killed = 1;
+ else
+ ht[h] = i + 1;
+ }
+ solv_free(ht);
+ j = 0;
+ for (i = 0, lp = lines; i < nlines; i++, lp++)
+ if (!lp->killed)
+ j++;
+ EXTEND(SP, j);
+ for (i = 0, lp = lines; i < nlines; i++, lp++)
+ {
+ SV *sv;
+ if (lp->killed)
+ continue;
+ sv = newSVpv(lp->l, 0);
+ PUSHs(sv_2mortal(sv));
+ }
+ solv_free(lines);
+ }
+
+SV *
+thawcache(SV *sv)
+ CODE:
+ unsigned char *src;
+ STRLEN srcl;
+ if (!SvPOK(sv)) {
+ croak("thaw: argument is not a string\n");
+ XSRETURN_UNDEF;
+ }
+ src = (unsigned char *)SvPV(sv, srcl);
+ if (srcl < 7 || src[0] != 'p' || src[1] != 's' || src[2] != 't' || src[3] != '0') {
+ croak("thaw: argument is not a perl storable\n");
+ XSRETURN_UNDEF;
+ }
+ if ((src[4] & 1) != 1) {
+ croak("thaw: argument is not a perl storable in network order\n");
+ XSRETURN_UNDEF;
+ }
+ if (src[4] < 5) {
+ croak("thaw: argument is a perl storable with a too old version\n");
+ XSRETURN_UNDEF;
+ }
+ src += 6;
+ srcl -= 6;
+ sv = retrieve(&src, &srcl, 0);
+ if (sv == 0 || srcl) {
+ croak("thaw: corrupt storable\n");
+ XSRETURN_UNDEF;
+ }
+ RETVAL = newRV_noinc(sv);
+ OUTPUT:
+ RETVAL
+
+int
+isobscpio(const char *file)
+ CODE:
+ int fd;
+ RETVAL = 0;
+ if ((fd = open(file, O_RDONLY)) != -1) {
+ unsigned char magic[16];
+ if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7))
+ RETVAL = 1;
+ close(fd);
+ }
+ OUTPUT:
+ RETVAL
+
+
+void
+obscpiostat(const char *file)
+ PPCODE:
+ {
+ int fd;
+ struct stat st;
+ if ((fd = open(file, O_RDONLY)) != -1) {
+ if (!fstat(fd, &st)) {
+ unsigned char magic[16];
+ if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
+ st.st_size = getu48(magic + 10);
+ }
+ EXTEND(SP, 10);
+ PUSHs(&PL_sv_undef);
+ PUSHs(&PL_sv_undef);
+ PUSHs(sv_2mortal(newSVuv((UV)st.st_mode)));
+ PUSHs(sv_2mortal(newSVuv((UV)st.st_nlink)));
+ PUSHs(&PL_sv_undef);
+ PUSHs(&PL_sv_undef);
+ PUSHs(&PL_sv_undef);
+#if IVSIZE > 4
+ PUSHs(sv_2mortal(newSVuv((UV)st.st_size)));
+#else
+ PUSHs(sv_2mortal(newSVnv((double)st.st_size)));
+#endif
+ PUSHs(sv_2mortal(newSVuv((UV)st.st_atime)));
+ PUSHs(sv_2mortal(newSVuv((UV)st.st_mtime)));
+ PUSHs(sv_2mortal(newSVuv((UV)st.st_ctime)));
+ }
+ close(fd);
+ }
+ }
+
+int
+obscpioopen(const char *file, const char *store, SV *gvrv, const char *tmpdir = 0)
+ CODE:
+ int fd;
+ GV *gv;
+ if (!SvROK(gvrv) || SvTYPE(SvRV(gvrv)) != SVt_PVGV) {
+ croak("obscpioopen needs a GV reference\n");
+ }
+ if (tmpdir && strlen(tmpdir) > 200) {
+ croak("tmpdir too long\n");
+ }
+ gv = (GV *)SvRV(gvrv);
+ RETVAL = 0;
+ if ((fd = open(file, O_RDONLY)) != -1) {
+ unsigned char magic[16];
+ if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
+ char template[256];
+ int nfd = -1;
+ int sfd;
+ if ((sfd = open(store, O_RDONLY)) != -1) {
+ if (tmpdir) {
+ strcpy(template, tmpdir);
+ strcat(template, "/obscpioopen-XXXXXX");
+ } else {
+ strcpy(template, "/var/tmp/obscpioopen-XXXXXX");
+ }
+ nfd = mkstemp(template);
+ if (nfd != -1) {
+ FILE *fp = 0, *nfp = 0;
+ unlink(template);
+ lseek(fd, 0, SEEK_SET);
+ if ((fp = fdopen(fd, "r")) == 0)
+ close(fd);
+ if ((nfp = fdopen(nfd, "w+")) == 0)
+ close(nfd);
+ if (fp && nfp && expandobscpio(fp, sfd, nfp)) {
+ nfd = dup(nfd);
+ if (fclose(nfp)) {
+ close(nfd);
+ nfd = -1;
+ }
+ nfp = 0;
+ } else {
+ nfd = -1;
+ }
+ if (fp)
+ fclose(fp);
+ if (nfp)
+ fclose(nfp);
+ fd = -1;
+ }
+ close(sfd);
+ }
+ if (fd != -1)
+ close(fd);
+ fd = nfd;
+ }
+ if (fd != -1) {
+ IO * io = GvIOn(gv);
+ PerlIO *fp;
+
+ lseek(fd, 0, SEEK_SET);
+ fp = PerlIO_fdopen(fd, "rb");
+ if (fp) {
+ IoIFP(io) = fp;
+ RETVAL = 1;
+ }
+ }
+ }
+
+ OUTPUT:
+ RETVAL
+
+int
+expandobscpio(const char *file, const char *store, const char *tmpfile)
+ CODE:
+ {
+ int fd, nfd, sfd;
+ RETVAL = 0;
+
+ unlink(tmpfile);
+ if ((fd = open(file, O_RDONLY)) != -1) {
+ unsigned char magic[16];
+ if (!(read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7))) {
+ close(fd);
+ fd = -1;
+ if (link(file, tmpfile) == 0 && (fd = open(tmpfile, O_RDONLY)) != -1) {
+ if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
+ unlink(tmpfile);
+ } else {
+ close(fd);
+ fd = -1;
+ RETVAL = 1;
+ }
+ }
+ }
+ if (fd != -1) {
+ if ((sfd = open(store, O_RDONLY)) != -1) {
+ FILE *fp;
+ lseek(fd, 0, SEEK_SET);
+ if ((fp = fdopen(fd, "r")) == 0)
+ close(fd);
+ if (fp && (nfd = open(tmpfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0666)) != -1) {
+ FILE *nfp;
+ if ((nfp = fdopen(nfd, "w")) == 0)
+ close(nfd);
+ if (nfp && expandobscpio(fp, sfd, nfp)) {
+ if (!fclose(nfp))
+ RETVAL = 1;
+ else
+ unlink(tmpfile);
+ nfp = 0;
+ } else
+ unlink(tmpfile);
+ if (nfp)
+ fclose(nfp);
+ }
+ if (fp)
+ fclose(fp);
+ close(sfd);
+ } else {
+ close(fd);
+ }
+ }
+ }
+ }
+ OUTPUT:
+ RETVAL
+
+
+int
+makeobscpio(const char *in, const char *store, const char *out)
+ CODE:
+ {
+ FILE *fpin, *fpout;
+ struct stat st;
+ int fdstore;
+ RETVAL = 0;
+ if ((fpin = fopen(in, "r")) == 0) {
+ perror(in);
+ } else if (fstat(fileno(fpin), &st) != 0) {
+ perror(in);
+ fclose(fpin);
+ } else if ((fpout = fopen(out, "w")) == 0) {
+ perror(out);
+ fclose(fpin);
+ } else if ((fdstore = open(store, O_RDWR|O_CREAT, 0666)) == -1) {
+ perror(store);
+ fclose(fpin);
+ fclose(fpout);
+ } else {
+ int gotlock = 0;
+ while (!gotlock) {
+ if (flock(fdstore, LOCK_EX) == 0)
+ gotlock = 1;
+ else if (errno != EINTR)
+ break;
+ }
+ if (gotlock) {
+ struct deltastore store;
+ if (readdeltastore(&store, fdstore, 0, (unsigned long long)st.st_size)) {
+ int r = makedelta(&store, fpin, fpout, (unsigned long long)st.st_size);
+#if 0
+ printf("after makedelta: have %d entries, hash size %d\n", store.hf, store.hm + 1);
+#endif
+ if (fsync(store.fd))
+ r = 0;
+ freedeltastore(&store);
+ if (r) {
+ settimes(fileno(fpout), &st);
+ RETVAL = 1;
+ }
+ }
+ }
+ close(fdstore);
+ fclose(fpin);
+ fclose(fpout);
+ }
+ }
+ OUTPUT:
+ RETVAL
+
+void
+obscpiostorestats(const char *store)
+ CODE:
+ {
+ int fdstore;
+
+ if ((fdstore = open(store, O_RDONLY)) == -1)
+ perror(store);
+ else {
+ int gotlock = 0;
+ while (!gotlock) {
+ if (flock(fdstore, LOCK_EX) == 0)
+ gotlock = 1;
+ else if (errno != EINTR)
+ break;
+ }
+ if (gotlock) {
+ struct deltastore store;
+ if (readdeltastore(&store, fdstore, 1, (unsigned long long)0)) {
+ printdeltastorestats(&store);
+ fsync(store.fd);
+ freedeltastore(&store);
+ }
+ }
+ close(fdstore);
+ }
+ }
+
+void
+obscpioinstr(const char *file, const char *store = 0)
+ CODE:
+ {
+ FILE *fp;
+ int fdstore = -1;
+ if ((fp = fopen(file, "r")) == 0)
+ perror(file);
+ else {
+ if (store) {
+ fdstore = open(store, O_RDONLY);
+ if (fdstore == -1)
+ perror(store);
+ }
+ printobscpioinstr(fp, fdstore, fdstore == -1 ? 0 : 1);
+ fclose(fp);
+ if (fdstore != -1)
+ close(fdstore);
+ }
+ }
+
+
+MODULE = BSSolv PACKAGE = BSSolv::pool PREFIX = pool
+
+PROTOTYPES: ENABLE
+
+BSSolv::pool
+new(char *packname = "BSSolv::pool")
+ CODE:
+ {
+ Pool *pool = pool_create();
+ set_disttype(pool, DISTTYPE_RPM);
+ buildservice_id = pool_str2id(pool, "buildservice:id", 1);
+ buildservice_repocookie= pool_str2id(pool, "buildservice:repocookie", 1);
+ buildservice_external = pool_str2id(pool, "buildservice:external", 1);
+ buildservice_dodurl = pool_str2id(pool, "buildservice:dodurl", 1);
+ expander_directdepsend = pool_str2id(pool, "-directdepsend--", 1);
+ buildservice_dodcookie = pool_str2id(pool, "buildservice:dodcookie", 1);
+ pool_freeidhashes(pool);
+ RETVAL = pool;
+ }
+ OUTPUT:
+ RETVAL
+
+void
+settype(BSSolv::pool pool, char *type)
+ CODE:
+ if (!strcmp(type, "rpm"))
+ set_disttype(pool, DISTTYPE_RPM);
+#ifdef DISTTYPE_DEB
+ else if (!strcmp(type, "deb"))
+ set_disttype(pool, DISTTYPE_DEB);
+#endif
+#ifdef DISTTYPE_ARCH
+ else if (!strcmp(type, "arch"))
+ set_disttype(pool, DISTTYPE_ARCH);
+#endif
+ else
+ croak("settype: unknown type '%s'\n", type);
+
+
+BSSolv::repo
+repofromfile(BSSolv::pool pool, char *name, char *filename)
+ CODE:
+ FILE *fp;
+ fp = fopen(filename, "r");
+ if (!fp) {
+ croak("%s: %s\n", filename, Strerror(errno));
+ XSRETURN_UNDEF;
+ }
+ RETVAL = repo_create(pool, name);
+ repo_add_solv(RETVAL, fp, 0);
+ fclose(fp);
+ OUTPUT:
+ RETVAL
+
+BSSolv::repo
+repofromstr(BSSolv::pool pool, char *name, SV *sv)
+ CODE:
+ FILE *fp;
+ STRLEN len;
+ char *buf;
+ buf = SvPV(sv, len);
+ if (!buf)
+ croak("repofromstr: undef string\n");
+ fp = fmemopen(buf, len, "r");
+ if (!fp) {
+ croak("fmemopen failed\n");
+ XSRETURN_UNDEF;
+ }
+ RETVAL = repo_create(pool, name);
+ repo_add_solv(RETVAL, fp, 0);
+ fclose(fp);
+ OUTPUT:
+ RETVAL
+
+BSSolv::repo
+repofrombins(BSSolv::pool pool, char *name, char *dir, ...)
+ CODE:
+ {
+ int i;
+ Repo *repo;
+ Repodata *data;
+ repo = repo_create(pool, name);
+ data = repo_add_repodata(repo, 0);
+ for (i = 3; i + 1 < items; i += 2)
+ {
+ STRLEN sl;
+ char *s = SvPV(ST(i), sl);
+ char *sid = SvPV_nolen(ST(i + 1));
+ if (sl < 4)
+ continue;
+ if (strcmp(s + sl - 4, ".rpm")
+ && strcmp(s + sl - 4, ".deb")
+#ifdef ARCH_ADD_WITH_PKGID
+ && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
+ && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
+#endif
+ )
+ continue;
+ if (sl >= 10 && !strcmp(s + sl - 10, ".patch.rpm"))
+ continue;
+ if (sl >= 10 && !strcmp(s + sl - 10, ".nosrc.rpm"))
+ continue;
+ if (sl >= 8 && !strcmp(s + sl - 8, ".src.rpm"))
+ continue;
+ repodata_addbin(data, dir, s, (int)sl, sid);
+ }
+ repo_set_str(repo, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
+ repo_internalize(repo);
+ RETVAL = repo;
+ }
+ OUTPUT:
+ RETVAL
+
+BSSolv::repo
+repofromdata(BSSolv::pool pool, char *name, HV *rhv)
+ CODE:
+ {
+ Repo *repo;
+ Repodata *data;
+ repo = repo_create(pool, name);
+ data = repo_add_repodata(repo, 0);
+ data2solvables(repo, data, rhv);
+ if (name && !strcmp(name, "/external/"))
+ repodata_set_void(data, SOLVID_META, buildservice_external);
+ repo_internalize(repo);
+ RETVAL = repo;
+ }
+ OUTPUT:
+ RETVAL
+
+void
+createwhatprovides(BSSolv::pool pool)
+ CODE:
+ if (pool->considered)
+ {
+ map_free(pool->considered);
+ solv_free(pool->considered);
+ }
+ pool->considered = solv_calloc(sizeof(Map), 1);
+ create_considered(pool, 0, pool->considered);
+ pool_createwhatprovides(pool);
+
+void
+setdebuglevel(BSSolv::pool pool, int level)
+ CODE:
+ pool_setdebuglevel(pool, level);
+
+void
+whatprovides(BSSolv::pool pool, char *str)
+ PPCODE:
+ {
+ Id p, pp, id;
+ id = dep2id(pool, str);
+ if (id)
+ FOR_PROVIDES(p, pp, id)
+ XPUSHs(sv_2mortal(newSViv((IV)p)));
+ }
+
+void
+whatrequires(BSSolv::pool pool, char *str)
+ PPCODE:
+ {
+ Id p, id;
+ Id *pp;
+ Solvable *s;
+ id = dep2id(pool, str);
+ if (id)
+ {
+ for (p = 2; p < pool->nsolvables; p++)
+ {
+ if (!MAPTST(pool->considered, p))
+ continue;
+ s = pool->solvables + p;
+ if (!s->requires)
+ continue;
+ for (pp = s->repo->idarraydata + s->requires; *pp; pp++)
+ if (pool_match_dep(pool, id, *pp))
+ break;
+ if (*pp)
+ XPUSHs(sv_2mortal(newSViv((IV)p)));
+ }
+ }
+ }
+
+void
+consideredpackages(BSSolv::pool pool)
+ PPCODE:
+ {
+ int p, nsolv = 0;
+ for (p = 2; p < pool->nsolvables; p++)
+ if (MAPTST(pool->considered, p))
+ nsolv++;
+ EXTEND(SP, nsolv);
+ for (p = 2; p < pool->nsolvables; p++)
+ if (MAPTST(pool->considered, p))
+ PUSHs(sv_2mortal(newSViv((IV)p)));
+ }
+
+void
+allpackages(BSSolv::pool pool)
+ PPCODE:
+ {
+ int p, nsolv = 0;
+ for (p = 2; p < pool->nsolvables; p++)
+ if (pool->solvables[p].repo)
+ nsolv++;
+ EXTEND(SP, nsolv);
+ for (p = 2; p < pool->nsolvables; p++)
+ if (pool->solvables[p].repo)
+ PUSHs(sv_2mortal(newSViv((IV)p)));
+ }
+
+const char *
+pkg2name(BSSolv::pool pool, int p)
+ CODE:
+ RETVAL = pool_id2str(pool, pool->solvables[p].name);
+ OUTPUT:
+ RETVAL
+
+const char *
+pkg2srcname(BSSolv::pool pool, int p)
+ CODE:
+ if (solvable_lookup_void(pool->solvables + p, SOLVABLE_SOURCENAME))
+ RETVAL = pool_id2str(pool, pool->solvables[p].name);
+ else
+ RETVAL = solvable_lookup_str(pool->solvables + p, SOLVABLE_SOURCENAME);
+ OUTPUT:
+ RETVAL
+
+const char *
+pkg2pkgid(BSSolv::pool pool, int p)
+ CODE:
+ {
+ Id type;
+ const char *s = solvable_lookup_checksum(pool->solvables + p, SOLVABLE_PKGID, &type);
+ RETVAL = s;
+ }
+ OUTPUT:
+ RETVAL
+
+const char *
+pkg2bsid(BSSolv::pool pool, int p)
+ CODE:
+ RETVAL = solvable_lookup_str(pool->solvables + p, buildservice_id);
+ OUTPUT:
+ RETVAL
+
+const char *
+pkg2reponame(BSSolv::pool pool, int p)
+ CODE:
+ {
+ Repo *repo = pool->solvables[p].repo;
+ RETVAL = repo ? repo->name : 0;
+ }
+ OUTPUT:
+ RETVAL
+
+const char *
+pkg2path(BSSolv::pool pool, int p)
+ CODE:
+ {
+ unsigned int medianr;
+ RETVAL = solvable_get_location(pool->solvables + p, &medianr);
+ }
+ OUTPUT:
+ RETVAL
+
+const char *
+pkg2fullpath(BSSolv::pool pool, int p, char *myarch)
+ CODE:
+ {
+ unsigned int medianr;
+ const char *s = solvable_get_location(pool->solvables + p, &medianr);
+ Repo *repo = pool->solvables[p].repo;
+ s = pool_tmpjoin(pool, myarch, "/:full/", s);
+ RETVAL = pool_tmpjoin(pool, repo->name, "/", s);
+ }
+ OUTPUT:
+ RETVAL
+
+int
+pkg2sizek(BSSolv::pool pool, int p)
+ CODE:
+#ifdef SOLV_KV_NUM64
+ RETVAL = solvable_lookup_sizek(pool->solvables + p, SOLVABLE_DOWNLOADSIZE, 0);
+#else
+ RETVAL = solvable_lookup_num(pool->solvables + p, SOLVABLE_DOWNLOADSIZE, 0);
+#endif
+ OUTPUT:
+ RETVAL
+
+const char *
+pkg2checksum(BSSolv::pool pool, int p)
+ CODE:
+ {
+ Id type;
+ const char *s = solvable_lookup_checksum(pool->solvables + p, SOLVABLE_CHECKSUM, &type);
+ if (s)
+ s = pool_tmpjoin(pool, solv_chksum_type2str(type), ":", s);
+ RETVAL = s;
+ }
+ OUTPUT:
+ RETVAL
+
+int
+verifypkgchecksum(BSSolv::pool pool, int p, char *path)
+ CODE:
+ {
+ Id type;
+ const unsigned char *cin, *cout;
+ FILE *fp;
+ void *cs;
+ int cslen;
+ char buf[4096];
+ size_t len;
+ int res = 0;
+
+ if ((cin = solvable_lookup_bin_checksum(pool->solvables + p, SOLVABLE_CHECKSUM, &type)) != 0) {
+ if ((fp = fopen(path, "r")) != 0) {
+ if ((cs = solv_chksum_create(type)) != 0) {
+ while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
+ solv_chksum_add(cs, buf, len);
+ if ((cout = solv_chksum_get(cs, &cslen)) != 0 && cslen && !memcmp(cin, cout, cslen))
+ res = 1;
+ solv_chksum_free(cs, 0);
+ }
+ fclose(fp);
+ }
+ }
+ RETVAL = res;
+ }
+ OUTPUT:
+ RETVAL
+
+HV *
+pkg2data(BSSolv::pool pool, int p)
+ CODE:
+ {
+ Solvable *s = pool->solvables + p;
+ Id id;
+ const char *ss, *se;
+ unsigned int medianr;
+
+ if (!s->repo)
+ XSRETURN_EMPTY;
+ RETVAL = newHV();
+ sv_2mortal((SV*)RETVAL);
+ (void)hv_store(RETVAL, "name", 4, newSVpv(pool_id2str(pool, s->name), 0), 0);
+ ss = pool_id2str(pool, s->evr);
+ se = ss;
+ while (*se >= '0' && *se <= '9')
+ se++;
+ if (se != ss && *se == ':' && se[1])
+ {
+ (void)hv_store(RETVAL, "epoch", 5, newSVpvn(ss, se - ss), 0);
+ ss = se + 1;
+ }
+ se = strrchr(ss, '-');
+ if (se)
+ {
+ (void)hv_store(RETVAL, "version", 7, newSVpvn(ss, se - ss), 0);
+ (void)hv_store(RETVAL, "release", 7, newSVpv(se + 1, 0), 0);
+ }
+ else
+ (void)hv_store(RETVAL, "version", 7, newSVpv(ss, 0), 0);
+ (void)hv_store(RETVAL, "arch", 4, newSVpv(pool_id2str(pool, s->arch), 0), 0);
+ exportdeps(RETVAL, "provides", 8, s->repo, s->provides, SOLVABLE_PROVIDES);
+ exportdeps(RETVAL, "obsoletes", 9, s->repo, s->obsoletes, SOLVABLE_OBSOLETES);
+ exportdeps(RETVAL, "conflicts", 9, s->repo, s->conflicts, SOLVABLE_CONFLICTS);
+ exportdeps(RETVAL, "requires", 8, s->repo, s->requires, SOLVABLE_REQUIRES);
+ exportdeps(RETVAL, "recommends", 10, s->repo, s->recommends, SOLVABLE_RECOMMENDS);
+ exportdeps(RETVAL, "suggests", 8, s->repo, s->suggests, SOLVABLE_SUGGESTS);
+ exportdeps(RETVAL, "supplements", 11, s->repo, s->supplements, SOLVABLE_SUPPLEMENTS);
+ exportdeps(RETVAL, "enhances", 8, s->repo, s->enhances, SOLVABLE_ENHANCES);
+ if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
+ ss = pool_id2str(pool, s->name);
+ else
+ ss = solvable_lookup_str(s, SOLVABLE_SOURCENAME);
+ if (ss)
+ (void)hv_store(RETVAL, "source", 6, newSVpv(ss, 0), 0);
+ ss = solvable_get_location(s, &medianr);
+ if (ss)
+ (void)hv_store(RETVAL, "path", 4, newSVpv(ss, 0), 0);
+ ss = solvable_lookup_checksum(s, SOLVABLE_PKGID, &id);
+ if (ss && id == REPOKEY_TYPE_MD5)
+ (void)hv_store(RETVAL, "hdrmd5", 6, newSVpv(ss, 0), 0);
+ ss = solvable_lookup_str(s, buildservice_id);
+ if (ss)
+ (void)hv_store(RETVAL, "id", 2, newSVpv(ss, 0), 0);
+ }
+ OUTPUT:
+ RETVAL
+
+void
+repos(BSSolv::pool pool)
+ PPCODE:
+ {
+ int ridx;
+ Repo *repo;
+
+ EXTEND(SP, pool->nrepos);
+ FOR_REPOS(ridx, repo)
+ {
+ SV *sv = sv_newmortal();
+ sv_setref_pv(sv, "BSSolv::repo", (void *)repo);
+ PUSHs(sv);
+ }
+ }
+
+void
+preparehashes(BSSolv::pool pool, char *prp, SV *gctxprpnotreadysv = 0)
+ PPCODE:
+ {
+ HV *gctxprpnotready = 0;
+ int ridx;
+ Repo *repo;
+ /* generated: */
+ HV *depislocal = newHV();
+ HV *dep2pkg = newHV();
+ HV *dep2src = newHV();
+ HV *notready = newHV();
+ HV *subpacks = newHV();
+ const char *srcstr;
+ const char *str;
+ Queue subq;
+ Id lastsrc, srcname, srctype;
+ int i, j;
+ Id p;
+ Solvable *s;
+ SV *sv, **svp;
+
+ if (gctxprpnotreadysv && SvROK(gctxprpnotreadysv) && SvTYPE(SvRV(gctxprpnotreadysv)) == SVt_PVHV)
+ gctxprpnotready = (HV *)SvRV(gctxprpnotreadysv);
+ queue_init(&subq);
+ FOR_REPOS(ridx, repo)
+ {
+ HV *prpnotready = 0;
+ int islocal = repo->name && !strcmp(repo->name, prp);
+ svp = 0;
+ if (repo->name && !islocal && gctxprpnotready)
+ svp = hv_fetch(gctxprpnotready, repo->name, strlen(repo->name), 0);
+ if (svp && *svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV)
+ prpnotready = (HV *)SvRV(*svp);
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ if (!MAPTST(pool->considered, p))
+ continue;
+ srctype = solvable_lookup_type(pool->solvables + p, SOLVABLE_SOURCENAME);
+ if (srctype == REPOKEY_TYPE_VOID)
+ srcname = s->name;
+ else if (srctype == REPOKEY_TYPE_ID)
+ srcname = solvable_lookup_id(pool->solvables + p, SOLVABLE_SOURCENAME);
+ else
+ {
+ srcstr = solvable_lookup_str(pool->solvables + p, SOLVABLE_SOURCENAME);
+ srcname = srcstr ? pool_str2id(pool, srcstr, 1) : 0;
+ }
+ if (!srcname || srcname == 1)
+ srcname = s->name;
+ queue_push2(&subq, s->name, srcname);
+
+ str = pool_id2str(pool, s->name);
+ (void)hv_store(dep2pkg, str, strlen(str), newSViv((IV)p), 0);
+ if (islocal)
+ (void)hv_store(depislocal, str, strlen(str), newSViv((IV)1), 0);
+ srcstr = pool_id2str(pool, srcname);
+ (void)hv_store(dep2src, str, strlen(str), newSVpv(srcstr, 0), 0);
+ if (!islocal && prpnotready)
+ {
+ svp = hv_fetch(prpnotready, srcstr, strlen(srcstr), 0);
+ if (svp && *svp && SvTRUE(*svp))
+ (void)hv_store(notready, srcstr, strlen((char *)srcstr), newSViv((IV)2), 0);
+ }
+ }
+ }
+ solv_sort(subq.elements, subq.count / 2, sizeof(Id) * 2, subpack_sort_cmp, pool);
+ queue_push2(&subq, 0, 0);
+ lastsrc = 0;
+ for (i = j = 0; i < subq.count; i += 2)
+ {
+ if (subq.elements[i + 1] != lastsrc)
+ {
+ if (j < i)
+ {
+ AV *subs = newAV();
+ for (; j < i; j += 2)
+ {
+ str = pool_id2str(pool, subq.elements[j]);
+ av_push(subs, newSVpv(str, 0));
+ }
+ str = pool_id2str(pool, lastsrc);
+ (void)hv_store(subpacks, str, strlen(str), newRV_noinc((SV *)subs), 0);
+ }
+ lastsrc = subq.elements[i + 1];
+ }
+ }
+ queue_free(&subq);
+ EXTEND(SP, 5);
+ sv = newRV_noinc((SV *)dep2pkg);
+ PUSHs(sv_2mortal(sv));
+ sv = newRV_noinc((SV *)dep2src);
+ PUSHs(sv_2mortal(sv));
+ sv = newRV_noinc((SV *)depislocal);
+ PUSHs(sv_2mortal(sv));
+ sv = newRV_noinc((SV *)notready);
+ PUSHs(sv_2mortal(sv));
+ sv = newRV_noinc((SV *)subpacks);
+ PUSHs(sv_2mortal(sv));
+ }
+
+void
+DESTROY(BSSolv::pool pool)
+ CODE:
+ if (pool->considered)
+ {
+ map_free(pool->considered);
+ pool->considered = solv_free(pool->considered);
+ }
+ pool_free(pool);
+
+
+
+
+MODULE = BSSolv PACKAGE = BSSolv::repo PREFIX = repo
+
+void
+pkgnames(BSSolv::repo repo)
+ PPCODE:
+ {
+ Pool *pool = repo->pool;
+ Id p;
+ Solvable *s;
+ Map c;
+
+ create_considered(pool, repo, &c);
+ EXTEND(SP, 2 * repo->nsolvables);
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ if (!MAPTST(&c, p))
+ continue;
+ PUSHs(sv_2mortal(newSVpv(pool_id2str(pool, s->name), 0)));
+ PUSHs(sv_2mortal(newSViv(p)));
+ }
+ map_free(&c);
+ }
+
+void
+pkgpaths(BSSolv::repo repo)
+ PPCODE:
+ {
+ Pool *pool = repo->pool;
+ Id p;
+ Solvable *s;
+ Map c;
+ const char *str;
+ unsigned int medianr;
+
+ create_considered(pool, repo, &c);
+ EXTEND(SP, 2 * repo->nsolvables);
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ if (!MAPTST(&c, p))
+ continue;
+ /* ignore dod packages */
+ str = solvable_lookup_str(s, buildservice_id);
+ if (str && !strcmp(str, "dod"))
+ continue;
+ str = solvable_get_location(pool->solvables + p, &medianr);
+ if (!str)
+ continue;
+ PUSHs(sv_2mortal(newSVpv(str, 0)));
+ PUSHs(sv_2mortal(newSViv(p)));
+ }
+ map_free(&c);
+ }
+
+void
+tofile(BSSolv::repo repo, char *filename)
+ CODE:
+ {
+ FILE *fp;
+ fp = fopen(filename, "w");
+ if (fp == 0)
+ croak("%s: %s\n", filename, Strerror(errno));
+ repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
+ if (fclose(fp))
+ croak("fclose: %s\n", Strerror(errno));
+ }
+
+void
+tofile_fd(BSSolv::repo repo, int fd)
+ CODE:
+ {
+ FILE *fp;
+ int fd2;
+ fd2 = dup(fd);
+ if (fd2 == -1)
+ croak("dup: %s\n", Strerror(errno));
+ fp = fdopen(fd2, "w");
+ if (fp == 0)
+ {
+ int e = errno;
+ close(fd2);
+ croak("fdopen: %s\n", Strerror(e));
+ }
+ repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
+ if (fclose(fp))
+ {
+ int e = errno;
+ close(fd2);
+ croak("fclose: %s\n", Strerror(e));
+ }
+ }
+
+SV *
+tostr(BSSolv::repo repo)
+ CODE:
+ {
+ FILE *fp;
+ char *buf;
+ size_t len;
+ fp = open_memstream(&buf, &len);
+ if (fp == 0)
+ croak("open_memstream: %s\n", Strerror(errno));
+ repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
+ if (fclose(fp))
+ croak("fclose: %s\n", Strerror(errno));
+ RETVAL = newSVpvn(buf, len);
+ free(buf);
+ }
+ OUTPUT:
+ RETVAL
+
+int
+updatefrombins(BSSolv::repo repo, char *dir, ...)
+ CODE:
+ {
+ Pool *pool = repo->pool;
+ int i;
+ Repodata *data = 0;
+ Hashtable ht;
+ Hashval h, hh, hm;
+ int dirty = 0;
+ Map reused;
+ int oldend = 0;
+ Id p, id;
+ Solvable *s;
+ STRLEN sl;
+ const char *oldcookie;
+
+ map_init(&reused, repo->end - repo->start);
+ if (repo_lookup_str(repo, SOLVID_META, buildservice_dodurl))
+ {
+ /* this is a dod repo. keep all dod packages. */
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ const char *str = solvable_lookup_str(s, buildservice_id);
+ if (str && !strcmp(str, "dod"))
+ MAPSET(&reused, p - repo->start);
+ }
+ }
+ hm = mkmask(2 * repo->nsolvables + 1);
+ ht = solv_calloc(hm + 1, sizeof(*ht));
+ oldcookie = repo_lookup_str(repo, SOLVID_META, buildservice_repocookie);
+ if (oldcookie && !strcmp(oldcookie, REPOCOOKIE))
+ {
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ const char *str = solvable_lookup_str(s, buildservice_id);
+ if (!str || !strcmp(str, "dod"))
+ continue;
+ h = strhash(str) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ ht[h] = p;
+ }
+ }
+ if (repo->end != repo->start)
+ oldend = repo->end;
+
+ for (i = 2; i + 1 < items; i += 2)
+ {
+ char *s = SvPV(ST(i), sl);
+ char *sid = SvPV_nolen(ST(i + 1));
+ if (sl < 4)
+ continue;
+ if (strcmp(s + sl - 4, ".rpm")
+ && strcmp(s + sl - 4, ".deb")
+#ifdef ARCH_ADD_WITH_PKGID
+ && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
+ && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
+#endif
+ )
+ if (sl > 10 && !strcmp(s + sl - 10, ".patch.rpm"))
+ continue;
+ if (sl > 10 && !strcmp(s + sl - 10, ".nosrc.rpm"))
+ continue;
+ if (sl > 8 && !strcmp(s + sl - 8, ".src.rpm"))
+ continue;
+ h = strhash(sid) & hm;
+ hh = HASHCHAIN_START;
+ while ((id = ht[h]) != 0)
+ {
+ const char *str = solvable_lookup_str(pool->solvables + id, buildservice_id);
+ if (!strcmp(str, sid))
+ {
+ /* check location */
+ unsigned int medianr;
+ str = solvable_get_location(pool->solvables + id, &medianr);
+ if (str[0] == '.' && str[1] == '/')
+ str += 2;
+ if (!strcmp(str, s))
+ break;
+ }
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ if (id)
+ {
+ /* same id and location, reuse old entry */
+ MAPSET(&reused, id - repo->start);
+ }
+ else
+ {
+ /* add new entry */
+ dirty++;
+ if (!data)
+ data = repo_add_repodata(repo, 0);
+ repodata_addbin(data, dir, s, (int)sl, sid);
+ }
+ }
+ solv_free(ht);
+ if (oldcookie)
+ {
+ if (strcmp(oldcookie, REPOCOOKIE) != 0)
+ {
+ Repodata *firstrepodata = repo_id2repodata(repo, 1);
+ if (data && data != firstrepodata)
+ repodata_internalize(data);
+ data = firstrepodata;
+ repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
+ }
+ }
+ else
+ {
+ if (!data)
+ data = repo_add_repodata(repo, 0);
+ repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
+ }
+ if (data)
+ repodata_internalize(data);
+ if (oldend)
+ {
+ for (i = repo->start; i < oldend; i++)
+ {
+ if (pool->solvables[i].repo != repo)
+ continue;
+ if (MAPTST(&reused, i - repo->start))
+ continue;
+ if (dirty <= 0)
+ dirty--;
+ repo_free_solvable_block(repo, i, 1, 0);
+ }
+ }
+ map_free(&reused);
+ RETVAL = dirty;
+ }
+ OUTPUT:
+ RETVAL
+
+
+void
+getpathid(BSSolv::repo repo)
+ PPCODE:
+ {
+ Id p;
+ Solvable *s;
+ EXTEND(SP, repo->nsolvables * 2);
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ unsigned int medianr;
+ const char *str;
+ str = solvable_get_location(s, &medianr);
+ PUSHs(sv_2mortal(newSVpv(str, 0)));
+ str = solvable_lookup_str(s, buildservice_id);
+ PUSHs(sv_2mortal(newSVpv(str, 0)));
+ }
+ }
+
+const char *
+name(BSSolv::repo repo)
+ CODE:
+ RETVAL = repo->name;
+ OUTPUT:
+ RETVAL
+
+int
+isexternal(BSSolv::repo repo)
+ CODE:
+ RETVAL = repo_lookup_void(repo, SOLVID_META, buildservice_external) ? 1 : 0;
+ OUTPUT:
+ RETVAL
+
+const char *
+dodurl(BSSolv::repo repo)
+ CODE:
+ RETVAL = repo_lookup_str(repo, SOLVID_META, buildservice_dodurl);
+ OUTPUT:
+ RETVAL
+
+const char *
+dodcookie(BSSolv::repo repo)
+ CODE:
+ RETVAL = repo_lookup_str(repo, SOLVID_META, buildservice_dodcookie);
+ OUTPUT:
+ RETVAL
+
+void
+updatedoddata(BSSolv::repo repo, HV *rhv = 0)
+ CODE:
+ {
+ Id p;
+ Solvable *s;
+ Repodata *data;
+ /* delete old dod data */
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ const char *str = solvable_lookup_str(s, buildservice_id);
+ if (!str || !strcmp(str, "dod"))
+ repo_free_solvable(repo, p, 1);
+ }
+ data = repo_add_repodata(repo, REPO_REUSE_REPODATA);
+ repodata_unset(data, SOLVID_META, buildservice_dodurl);
+ repodata_unset(data, SOLVID_META, buildservice_dodcookie);
+ /* add new data */
+ if (rhv)
+ data2solvables(repo, data, rhv);
+ repo_internalize(repo);
+ }
+
+
+MODULE = BSSolv PACKAGE = BSSolv::expander PREFIX = expander
+
+
+BSSolv::expander
+new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
+ CODE:
+ {
+ SV *sv, **svp;
+ char *str;
+ int i, neg;
+ Id id, id2;
+ Expander *xp;
+
+ xp = calloc(sizeof(Expander), 1);
+ xp->pool = pool;
+ svp = hv_fetch(config, "prefer", 6, 0);
+ sv = svp ? *svp : 0;
+ if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
+ {
+ AV *av = (AV *)SvRV(sv);
+ for (i = 0; i <= av_len(av); i++)
+ {
+ svp = av_fetch(av, i, 0);
+ if (!svp)
+ continue;
+ sv = *svp;
+ str = SvPV_nolen(sv);
+ if (!str)
+ continue;
+ neg = 0;
+ if (*str == '-')
+ {
+ neg = 1;
+ str++;
+ }
+ id = pool_str2id(pool, str, 1);
+ id2 = 0;
+ if ((str = strchr(str, ':')) != 0)
+ id2 = pool_str2id(pool, str + 1, 1);
+ if (neg)
+ {
+ MAPEXP(&xp->preferneg, id);
+ MAPSET(&xp->preferneg, id);
+ if (id2)
+ {
+ MAPEXP(&xp->prefernegx, id2);
+ MAPSET(&xp->prefernegx, id2);
+ }
+ }
+ else
+ {
+ queue_push(&xp->preferposq, id);
+ MAPEXP(&xp->preferpos, id);
+ MAPSET(&xp->preferpos, id);
+ if (id2)
+ {
+ MAPEXP(&xp->preferposx, id2);
+ MAPSET(&xp->preferposx, id2);
+ }
+ }
+ }
+ }
+ svp = hv_fetch(config, "ignoreh", 7, 0);
+ sv = svp ? *svp : 0;
+ if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)
+ {
+ HV *hv = (HV *)SvRV(sv);
+ HE *he;
+
+ hv_iterinit(hv);
+ while ((he = hv_iternext(hv)) != 0)
+ {
+ I32 strl;
+ str = hv_iterkey(he, &strl);
+ if (!str)
+ continue;
+
+ id = pool_str2id(pool, str, 1);
+ id2 = 0;
+ if ((str = strchr(str, ':')) != 0)
+ id2 = pool_str2id(pool, str + 1, 1);
+ MAPEXP(&xp->ignored, id);
+ MAPSET(&xp->ignored, id);
+ if (id2)
+ {
+ MAPEXP(&xp->ignoredx, id2);
+ MAPSET(&xp->ignoredx, id2);
+ }
+ }
+ }
+ svp = hv_fetch(config, "conflict", 8, 0);
+ sv = svp ? *svp : 0;
+ if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
+ {
+ AV *av = (AV *)SvRV(sv);
+ for (i = 0; i <= av_len(av); i++)
+ {
+ char *p;
+ Id id2;
+
+ svp = av_fetch(av, i, 0);
+ if (!svp)
+ continue;
+ sv = *svp;
+ str = SvPV_nolen(sv);
+ if (!str)
+ continue;
+ p = strchr(str, ':');
+ if (!p)
+ continue;
+ id = pool_strn2id(pool, str, p - str, 1);
+ str = p + 1;
+ while ((p = strchr(str, ',')) != 0)
+ {
+ id2 = pool_strn2id(pool, str, p - str, 1);
+ queue_push2(&xp->conflictsq, id, id2);
+ MAPEXP(&xp->conflicts, id);
+ MAPSET(&xp->conflicts, id);
+ MAPEXP(&xp->conflicts, id2);
+ MAPSET(&xp->conflicts, id2);
+ str = p + 1;
+ }
+ id2 = pool_str2id(pool, str, 1);
+ queue_push2(&xp->conflictsq, id, id2);
+ MAPEXP(&xp->conflicts, id);
+ MAPSET(&xp->conflicts, id);
+ MAPEXP(&xp->conflicts, id2);
+ MAPSET(&xp->conflicts, id2);
+ }
+ }
+ /* XXX: this modifies the pool, which is a bit unclean! */
+ svp = hv_fetch(config, "fileprovides", 12, 0);
+ sv = svp ? *svp : 0;
+ if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)
+ {
+ HV *hv = (HV *)SvRV(sv);
+ I32 strl;
+ Queue q;
+
+ xp->havefileprovides = 1;
+ hv_iterinit(hv);
+ queue_init(&q);
+ while ((sv = hv_iternextsv(hv, &str, &strl)) != 0)
+ {
+ AV *av;
+ Id p, pp;
+ int havenew = 0;
+
+ if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV)
+ continue;
+ id = pool_str2id(pool, str, 1);
+ queue_empty(&q);
+ FOR_PROVIDES(p, pp, id)
+ queue_push(&q, p);
+ av = (AV *)SvRV(sv);
+ for (i = 0; i <= av_len(av); i++)
+ {
+ svp = av_fetch(av, i, 0);
+ if (!svp)
+ continue;
+ sv = *svp;
+ str = SvPV_nolen(sv);
+ if (!str)
+ continue;
+ id2 = pool_str2id(pool, str, 0);
+ FOR_PROVIDES(p, pp, id2)
+ {
+ int j;
+ for (j = 0; j < q.count; j++)
+ {
+ if (q.elements[j] == p)
+ break;
+ if (q.elements[j] > p)
+ {
+ queue_insert(&q, j, p);
+ havenew = 1;
+ break;
+ }
+ }
+ if (j == q.count)
+ {
+ queue_push(&q, p);
+ havenew = 1;
+ }
+ }
+ }
+ if (havenew)
+ pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
+ }
+ queue_free(&q);
+ }
+ svp = hv_fetch(config, "expandflags:ignoreconflicts", 27, 0);
+ sv = svp ? *svp : 0;
+ if (sv && SvTRUE(sv))
+ xp->ignoreconflicts = 1;
+ svp = hv_fetch(config, "expand_dbg", 10, 0);
+ sv = svp ? *svp : 0;
+ if (sv && SvTRUE(sv))
+ xp->debug = 1;
+ sv = get_sv("Build::expand_dbg", FALSE);
+ if (sv && SvTRUE(sv))
+ xp->debug = 1;
+ RETVAL = xp;
+ }
+ OUTPUT:
+ RETVAL
+
+
+void
+expand(BSSolv::expander xp, ...)
+ PPCODE:
+ {
+ Pool *pool;
+ int i, nerrors;
+ Id id, who, conflbuf[16];
+ Queue revertignore, in, out, confl;
+ int oldignoreignore = xp->ignoreignore;
+ int ignoreignore = 0;
+ Map oldignored, oldignoredx;
+ int ignoremapssaved = 0;
+
+ queue_init(&revertignore);
+ queue_init(&in);
+ queue_init(&out);
+ queue_init_buffer(&confl, conflbuf, sizeof(conflbuf)/sizeof(*conflbuf));
+ pool = xp->pool;
+ if (xp->debug)
+ expander_dbg(xp, "expand args:");
+ for (i = 1; i < items; i++)
+ {
+ char *s = SvPV_nolen(ST(i));
+ if (xp->debug)
+ expander_dbg(xp, " %s", s);
+ if (*s == '-')
+ {
+ Id id;
+ if (s[1] == '-' && !strcmp(s, "--ignoreignore--"))
+ {
+ ignoreignore = 1;
+ continue;
+ }
+ id = pool_str2id(pool, s + 1, 1);
+ if (id == expander_directdepsend)
+ {
+ queue_push(&in, id);
+ continue;
+ }
+ queue_push(&revertignore, id);
+ }
+ else if (*s == '!')
+ {
+ Id id = dep2id(pool, s + (s[1] == '!' ? 2 : 1));
+ queue_push2(&confl, id, s[1] == '!' ? 1 : 0);
+ }
+ else
+ {
+ Id id = dep2id(pool, s);
+ queue_push(&in, id);
+ }
+ }
+ if (xp->debug)
+ expander_dbg(xp, "\n");
+
+ if (ignoreignore && revertignore.count)
+ {
+ /* bad: have direct ignores and project config ignores */
+ oldignored = xp->ignored;
+ oldignoredx = xp->ignoredx;
+ ignoremapssaved = 1;
+ /* clear project config maps */
+ memset(&xp->ignored, 0, sizeof(xp->ignored));
+ memset(&xp->ignoredx, 0, sizeof(xp->ignoredx));
+ }
+
+ if (revertignore.count)
+ {
+ /* mix direct ignores with ignores from project config */
+ int revertcnt = revertignore.count;
+ for (i = 0; i < revertcnt; i++)
+ {
+ const char *ss;
+ id = revertignore.elements[i];
+ MAPEXP(&xp->ignored, id);
+ if (MAPTST(&xp->ignored, id))
+ continue;
+ MAPSET(&xp->ignored, id);
+ queue_push(&revertignore, id);
+ if ((ss = strchr(pool_id2str(pool, id), ':')) != 0)
+ {
+ id = pool_str2id(pool, ss + 1, 1);
+ MAPEXP(&xp->ignoredx, id);
+ if (MAPTST(&xp->ignoredx, id))
+ continue;
+ MAPSET(&xp->ignoredx, id);
+ queue_push(&revertignore, -id);
+ }
+ }
+ queue_deleten(&revertignore, 0, revertcnt);
+ }
+ else if (ignoreignore)
+ {
+ /* no direct ignores, disable ignore processing */
+ xp->ignoreignore = 1;
+ }
+
+ MAPEXP(&xp->ignored, pool->ss.nstrings);
+ MAPEXP(&xp->ignoredx, pool->ss.nstrings);
+ MAPEXP(&xp->preferpos, pool->ss.nstrings);
+ MAPEXP(&xp->preferposx, pool->ss.nstrings);
+ MAPEXP(&xp->preferneg, pool->ss.nstrings);
+ MAPEXP(&xp->prefernegx, pool->ss.nstrings);
+ MAPEXP(&xp->conflicts, pool->ss.nstrings);
+
+ nerrors = expander_expand(xp, &in, &out, &confl);
+
+ /* revert ignores */
+ xp->ignoreignore = oldignoreignore;
+ if (ignoremapssaved)
+ {
+ map_free(&xp->ignored);
+ map_free(&xp->ignoredx);
+ xp->ignored = oldignored;
+ xp->ignoredx = oldignoredx;
+ }
+ else
+ {
+ for (i = 0; i < revertignore.count; i++)
+ {
+ id = revertignore.elements[i];
+ if (id > 0)
+ MAPCLR(&xp->ignored, id);
+ else
+ MAPCLR(&xp->ignoredx, -id);
+ }
+ }
+ queue_free(&revertignore);
+
+ queue_free(&in);
+ queue_free(&confl);
+
+ if (nerrors)
+ {
+ EXTEND(SP, nerrors + 1);
+ PUSHs(sv_2mortal(newSV(0)));
+ for (i = 0; i < out.count; )
+ {
+ SV *sv;
+ Id type = out.elements[i];
+ if (type == ERROR_NOPROVIDER)
+ {
+ id = out.elements[i + 1];
+ who = out.elements[i + 2];
+ if (who)
+ sv = newSVpvf("nothing provides %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
+ else
+ sv = newSVpvf("nothing provides %s", pool_dep2str(pool, id));
+ i += 3;
+ }
+ else if (type == ERROR_CONFLICTINGPROVIDER)
+ {
+ id = out.elements[i + 1];
+ who = out.elements[i + 2];
+ if (who)
+ sv = newSVpvf("conflict for provider of %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
+ else
+ sv = newSVpvf("conflict for provider of %s", pool_dep2str(pool, id));
+ i += 3;
+ }
+ else if (type == ERROR_CONFLICTINGPROVIDERS)
+ {
+ id = out.elements[i + 1];
+ who = out.elements[i + 2];
+ if (who)
+ sv = newSVpvf("conflict for all providers of %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
+ else
+ sv = newSVpvf("conflict for all providers of %s", pool_dep2str(pool, id));
+ i += 3;
+ }
+ else if (type == ERROR_PROVIDERINFO)
+ {
+ Id who2 = out.elements[i + 2];
+ who = out.elements[i + 1];
+ if (who2 < 0)
+ sv = newSVpvf("(provider %s obsoletes installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[-who2].name));
+ else
+ sv = newSVpvf("(provider %s conflicts with installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[who2].name));
+ i += 3;
+ }
+ else if (type == ERROR_PROVIDERINFO2)
+ {
+ Id who2 = out.elements[i + 2];
+ who = out.elements[i + 1];
+ if (who2 < 0)
+ sv = newSVpvf("(provider %s is obsoleted by installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[-who2].name));
+ else if (who2 > 0)
+ sv = newSVpvf("(provider %s is conflicted by installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[who2].name));
+ else
+ sv = newSVpvf("(provider %s is conflicted by the build config)", pool_id2str(pool, pool->solvables[who].name));
+ i += 3;
+ }
+ else if (type == ERROR_CHOICE)
+ {
+ int j;
+ char *str = "";
+ for (j = i + 3; out.elements[j]; j++)
+ {
+ Solvable *s = pool->solvables + out.elements[j];
+ str = pool_tmpjoin(pool, str, " ", pool_id2str(pool, s->name));
+ }
+ if (*str)
+ str++; /* skip starting ' ' */
+ id = out.elements[i + 1];
+ who = out.elements[i + 2];
+ if (who)
+ sv = newSVpvf("have choice for %s needed by %s: %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name), str);
+ else
+ sv = newSVpvf("have choice for %s: %s", pool_dep2str(pool, id), str);
+ i = j + 1;
+ }
+ else
+ croak("expander: bad error type\n");
+ PUSHs(sv_2mortal(sv));
+ }
+ }
+ else
+ {
+ EXTEND(SP, out.count + 1);
+ PUSHs(sv_2mortal(newSViv((IV)1)));
+ for (i = 0; i < out.count; i++)
+ {
+ Solvable *s = pool->solvables + out.elements[i];
+ PUSHs(sv_2mortal(newSVpv(pool_id2str(pool, s->name), 0)));
+ }
+ }
+ queue_free(&out);
+ }
+
+const char *
+debugstr(BSSolv::expander xp)
+ CODE:
+ if (!xp->debugstr)
+ xp->debugstr = calloc(1, 1);
+ RETVAL = xp->debugstr;
+ OUTPUT:
+ RETVAL
+
+
+void
+DESTROY(BSSolv::expander xp)
+ CODE:
+ map_free(&xp->ignored);
+ map_free(&xp->ignoredx);
+ queue_free(&xp->preferposq);
+ map_free(&xp->preferpos);
+ map_free(&xp->preferposx);
+ map_free(&xp->preferneg);
+ map_free(&xp->prefernegx);
+ queue_free(&xp->conflictsq);
+ map_free(&xp->conflicts);
+ solv_free(xp->debugstr);
+ solv_free(xp);
+++ /dev/null
-
-Having the same key in multiple repodatas of a repo does not work
-- searching will return both entries
-- repo_write will write a broken solv file
-Fixing the search so that it only returns the last entry will also
-fix repo_write.
-
+++ /dev/null
-PROJECT (libsolv)
-
-CMAKE_MINIMUM_REQUIRED (VERSION 2.4)
-
-OPTION (ENABLE_STATIC "Build a static version of the libraries?" OFF)
-OPTION (DISABLE_SHARED "Do not build a shared version of the libraries?" OFF)
-
-OPTION (ENABLE_PERL "Build the perl bindings?" OFF)
-OPTION (ENABLE_PYTHON "Build the python bindings?" OFF)
-OPTION (ENABLE_RUBY "Build the ruby bindings?" OFF)
-OPTION (ENABLE_TCL "Build the Tcl bindings?" OFF)
-
-OPTION (USE_VENDORDIRS "Install the bindings in vendor directories?" OFF)
-
-OPTION (ENABLE_RPMDB "Build with rpm database support?" OFF)
-OPTION (ENABLE_PUBKEY "Build with pubkey support?" OFF)
-OPTION (ENABLE_RPMDB_BYRPMHEADER "Build with rpmdb Header support?" OFF)
-OPTION (ENABLE_RPMMD "Build with rpmmd repository support?" OFF)
-OPTION (ENABLE_SUSEREPO "Build with suse repository support?" OFF)
-OPTION (ENABLE_COMPS "Build with fedora comps support?" OFF)
-OPTION (ENABLE_HELIXREPO "Build with helix repository support?" OFF)
-OPTION (ENABLE_DEBIAN "Build with debian database/repository support?" OFF)
-OPTION (ENABLE_MDKREPO "Build with mandriva/mageia repository support?" OFF)
-OPTION (ENABLE_ARCHREPO "Build with archlinux repository support?" OFF)
-OPTION (ENABLE_CUDFREPO "Build with cudf repository support?" OFF)
-OPTION (ENABLE_HAIKU "Build with Haiku package support?" OFF)
-OPTION (ENABLE_APPDATA "Build with AppStream appdata support?" OFF)
-
-OPTION (MULTI_SEMANTICS "Build with support for multiple distribution types?" OFF)
-
-OPTION (ENABLE_LZMA_COMPRESSION "Build with lzma/xz compression support?" OFF)
-OPTION (ENABLE_BZIP2_COMPRESSION "Build with bzip2 compression support?" OFF)
-
-#IF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERISION} GREATER 2.4)
-#ENDIF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERISION} GREATER 2.4)
-
-IF (COMMAND cmake_policy)
- # escape preprocessor, see -DVERSION below
- CMAKE_POLICY (SET CMP0005 OLD)
-ENDIF (COMMAND cmake_policy)
-
-# Library
-IF (DEFINED LIB)
- SET (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB}")
-ELSE (DEFINED LIB)
- IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
- SET (LIB_SUFFIX "64")
- ENDIF (CMAKE_SIZEOF_VOID_P MATCHES "8")
- SET (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}")
-ENDIF (DEFINED LIB)
-MESSAGE (STATUS "Libraries will be installed in ${LIB_INSTALL_DIR}")
-# Library
-IF (DEFINED INCLUDE)
- SET (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${INCLUDE}")
-else (DEFINED INCLUDE)
- SET (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include")
-ENDIF (DEFINED INCLUDE)
-MESSAGE (STATUS "Header files will be installed in ${INCLUDE_INSTALL_DIR}")
-SET (BIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/bin")
-SET (MAN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/man")
-IF (IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/man" AND NOT IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/man")
- SET (MAN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/man")
-ENDIF (IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/man" AND NOT IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/man")
-MESSAGE(STATUS "Man pages will be installed in ${MAN_INSTALL_DIR}")
-
-####################################################################
-# CONFIGURATION #
-####################################################################
-
-# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
-SET (CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules)
-INSTALL( FILES ${CMAKE_MODULE_PATH}/FindLibSolv.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/share/cmake/Modules )
-
-INCLUDE (${CMAKE_SOURCE_DIR}/VERSION.cmake)
-
-SET (have_system x)
-
-IF (FEDORA)
-MESSAGE(STATUS "Building for Fedora")
-ADD_DEFINITIONS (-DFEDORA)
-SET (ENABLE_RPMDB ON)
-SET (ENABLE_RPMMD ON)
-SET (have_system ${have_system}x)
-ENDIF (FEDORA)
-
-IF (DEBIAN)
-MESSAGE (STATUS "Building for Debian")
-ADD_DEFINITIONS (-DDEBIAN)
-SET (ENABLE_DEBIAN ON)
-SET (have_system ${have_system}x)
-ENDIF (DEBIAN)
-
-IF (SUSE)
-MESSAGE (STATUS "Building for SUSE")
-ADD_DEFINITIONS (-DSUSE)
-SET (ENABLE_RPMDB ON)
-SET (ENABLE_PUBKEY ON)
-SET (ENABLE_RPMDB_BYRPMHEADER ON)
-SET (ENABLE_RPMMD ON)
-SET (ENABLE_SUSEREPO ON)
-SET (ENABLE_HELIXREPO ON)
-SET (ENABLE_LINKED_PKGS ON)
-SET (have_system ${have_system}x)
-ENDIF (SUSE)
-
-IF (ARCHLINUX)
-MESSAGE (STATUS "Building for Archlinux")
-ADD_DEFINITIONS (-DARCHLINUX)
-SET (ENABLE_ARCHREPO ON)
-SET (have_system ${have_system}x)
-ENDIF (ARCHLINUX)
-
-IF (MANDRIVA)
-MESSAGE (STATUS "Building for Mandriva")
-ADD_DEFINITIONS (-DMANDRIVA)
-SET (ENABLE_MDKREPO ON)
-SET (ENABLE_RPMDB ON)
-SET (have_system ${have_system}x)
-ENDIF (MANDRIVA)
-
-IF (MAGEIA)
-MESSAGE (STATUS "Building for Mageia")
-ADD_DEFINITIONS (-DMAGEIA)
-SET (ENABLE_MDKREPO ON)
-SET (ENABLE_RPMDB ON)
-SET (ENABLE_RPMMD ON)
-SET (ENABLE_LZMA_COMPRESSION ON)
-SET (have_system ${have_system}x)
-ENDIF (MAGEIA)
-
-IF (HAIKU)
-MESSAGE(STATUS "Building for Haiku")
-FIND_LIBRARY(HAIKU_BE_LIBRARY NAMES be)
-FIND_LIBRARY(HAIKU_NETWORK_LIBRARY NAMES network)
-FIND_LIBRARY(HAIKU_PACKAGE_LIBRARY NAMES package)
-SET (HAIKU_SYSTEM_LIBRARIES
- ${HAIKU_BE_LIBRARY} ${HAIKU_NETWORK_LIBRARY} ${HAIKU_PACKAGE_LIBRARY})
-ADD_DEFINITIONS (-DHAIKU)
-SET (ENABLE_HAIKU ON)
-SET (have_system ${have_system}x)
-ENDIF (HAIKU)
-
-IF (${have_system} STREQUAL x)
- MESSAGE (STATUS "Building for no system")
-ENDIF (${have_system} STREQUAL x)
-IF (${have_system} STRGREATER xx)
- MESSAGE (FATAL_ERROR "Can only compile for one system type.")
-ENDIF (${have_system} STRGREATER xx)
-
-IF (ENABLE_ARCHREPO)
-SET (ENABLE_LZMA_COMPRESSION ON)
-ENDIF (ENABLE_ARCHREPO)
-
-FIND_PACKAGE (EXPAT REQUIRED)
-FIND_PACKAGE (ZLIB REQUIRED)
-IF (ENABLE_LZMA_COMPRESSION)
-FIND_PACKAGE (LZMA REQUIRED)
-ENDIF (ENABLE_LZMA_COMPRESSION)
-IF (ENABLE_BZIP2_COMPRESSION)
-FIND_PACKAGE (BZip2 REQUIRED)
-ENDIF (ENABLE_BZIP2_COMPRESSION)
-
-IF (RPM5)
-MESSAGE (STATUS "Enabling RPM 5 support")
-ADD_DEFINITIONS (-DRPM5)
-SET (ENABLE_RPMDB ON)
-SET (ENABLE_RPMMD ON)
-FIND_PACKAGE (PkgConfig REQUIRED)
-PKG_CHECK_MODULES (RPM REQUIRED rpm)
-INCLUDE_DIRECTORIES (${RPM_INCLUDE_DIRS})
-ENDIF (RPM5)
-
-IF (MULTI_SEMANTICS)
-MESSAGE (STATUS "Enabling multi dist support")
-ADD_DEFINITIONS (-DMULTI_SEMANTICS)
-ENDIF (MULTI_SEMANTICS)
-
-INCLUDE (CheckIncludeFile)
-IF (ENABLE_RPMDB)
- FIND_LIBRARY (RPMDB_LIBRARY NAMES rpmdb)
-
- IF (NOT RPMDB_LIBRARY)
- FIND_LIBRARY (RPMDB_LIBRARY NAMES rpm)
- ENDIF (NOT RPMDB_LIBRARY)
-
- FIND_LIBRARY (RPMIO_LIBRARY NAMES rpmio)
- IF (RPMIO_LIBRARY)
- SET(RPMDB_LIBRARY ${RPMIO_LIBRARY} ${RPMDB_LIBRARY})
- ENDIF (RPMIO_LIBRARY)
-
- IF (RPM5)
- FIND_LIBRARY (RPMMISC_LIBRARY NAMES rpmmisc)
- IF (RPMMISC_LIBRARY)
- SET (RPMDB_LIBRARY ${RPMMISC_LIBRARY} ${RPMDB_LIBRARY})
- ENDIF (RPMMISC_LIBRARY)
- ENDIF (RPM5)
-
- # check if rpm contains a bundled berkeley db
- CHECK_INCLUDE_FILE(rpm/db.h HAVE_RPM_DB_H)
- IF (NOT HAVE_RPM_DB_H)
- FIND_LIBRARY (DB_LIBRARY NAMES db)
- IF (DB_LIBRARY)
- SET (RPMDB_LIBRARY ${DB_LIBRARY} ${RPMDB_LIBRARY})
- ENDIF (DB_LIBRARY)
- ENDIF (NOT HAVE_RPM_DB_H)
- INCLUDE (CheckLibraryExists)
- CHECK_LIBRARY_EXISTS(rpmio pgpDigGetParams "" HAVE_PGPDIGGETPARAMS)
-ENDIF (ENABLE_RPMDB)
-
-IF (ENABLE_PUBKEY)
- SET (ENABLE_PGPVRFY ON)
-ENDIF (ENABLE_PUBKEY)
-
-INCLUDE (CheckFunctionExists)
-INCLUDE (TestBigEndian)
-
-CHECK_FUNCTION_EXISTS (strchrnul HAVE_STRCHRNUL)
-CHECK_FUNCTION_EXISTS (fopencookie HAVE_FOPENCOOKIE)
-CHECK_FUNCTION_EXISTS (funopen HAVE_FUNOPEN)
-TEST_BIG_ENDIAN (WORDS_BIGENDIAN)
-
-IF (${CMAKE_MAJOR_VERSION} GREATER 2)
-INCLUDE (CMakePushCheckState)
-INCLUDE (CheckCCompilerFlag)
-MACRO (check_linker_flag FLAG VAR)
- CMAKE_PUSH_CHECK_STATE (RESET)
- SET (CMAKE_REQUIRED_FLAGS "${FLAG}")
- CHECK_C_COMPILER_FLAG ("" "${VAR}")
- CMAKE_POP_CHECK_STATE ()
-ENDMACRO (check_linker_flag)
-check_linker_flag("-Wl,--as-needed" HAVE_LINKER_AS_NEEDED)
-check_linker_flag("-Wl,--version-script=${CMAKE_SOURCE_DIR}/src/libsolv.ver" HAVE_LINKER_VERSION_SCRIPT)
-ELSE (${CMAKE_MAJOR_VERSION} GREATER 2)
-SET (HAVE_LINKER_AS_NEEDED 1)
-SET (HAVE_LINKER_VERSION_SCRIPT 1)
-ENDIF (${CMAKE_MAJOR_VERSION} GREATER 2)
-
-# should create config.h with #cmakedefine instead...
-FOREACH (VAR HAVE_STRCHRNUL HAVE_FOPENCOOKIE HAVE_FUNOPEN WORDS_BIGENDIAN
- HAVE_RPM_DB_H HAVE_PGPDIGGETPARAMS
- ENABLE_RPMDB ENABLE_PUBKEY ENABLE_RPMMD ENABLE_RPMDB_BYRPMHEADER ENABLE_SUSEREPO ENABLE_COMPS
- ENABLE_HELIXREPO ENABLE_MDKREPO ENABLE_ARCHREPO ENABLE_DEBIAN ENABLE_HAIKU
- ENABLE_LZMA_COMPRESSION ENABLE_BZIP2_COMPRESSION ENABLE_PGPVRFY ENABLE_APPDATA
- ENABLE_LINKED_PKGS ENABLE_COMPLEX_DEPS)
- IF(${VAR})
- ADD_DEFINITIONS (-D${VAR}=1)
- SET (SWIG_FLAGS ${SWIG_FLAGS} -D${VAR})
- ENDIF (${VAR})
-ENDFOREACH (VAR)
-
-SET (PACKAGE "libsolv")
-SET (VERSION "${LIBSOLV_MAJOR}.${LIBSOLV_MINOR}.${LIBSOLV_PATCH}")
-
-ADD_DEFINITIONS (-D_FILE_OFFSET_BITS=64)
-ADD_DEFINITIONS (-DVERSION=\\\"${VERSION}\\\")
-CONFIGURE_FILE (src/solvversion.h.in src/solvversion.h)
-
-SET (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Package dependency solver library")
-SET (CPACK_PACKAGE_VENDOR "SUSE")
-SET (CPACK_PACKAGE_VERSION_MAJOR ${LIBSOLV_MAJOR})
-SET (CPACK_PACKAGE_VERSION_MINOR ${LIBSOLV_MINOR})
-SET (CPACK_PACKAGE_VERSION_PATCH ${LIBSOLV_PATCH})
-SET (CPACK_GENERATOR "TBZ2")
-SET (CPACK_SOURCE_GENERATOR "TBZ2")
-SET (CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE}-${VERSION}")
-SET (CPACK_SOURCE_TOPLEVEL_TAG "Linux-Source:")
-SET (CPACK_TOPLEVEL_TAG "Linux-Source:")
-
-# The following components are regex's to match anywhere (unless anchored)
-# in absolute path + filename to find files or directories to be excluded
-# from source tarball.
-SET (CPACK_SOURCE_IGNORE_FILES
-# temporary files
-"\\\\.swp$"
-# backup files
-"~$"
-# eclipse files
-"\\\\.cdtproject$"
-"\\\\.cproject$"
-"\\\\.project$"
-"\\\\.settings/"
-# others
-"\\\\.#"
-"/#"
-"/build/"
-"/_build/"
-"/\\\\.git/"
-# used before
-"/\\\\.libs/"
-"/\\\\.deps/"
-"\\\\.o$"
-"\\\\.lo$"
-"\\\\.la$"
-"Makefile$"
-"Makefile\\\\.in$"
-# cmake cache files
-"DartConfiguration.tcl$"
-"CMakeCache.txt"
-"CMakeFiles"
-"cmake_install.cmake$"
-"CMakeLists.txt.auto$"
-"CTestTestfile.cmake"
-"CPackConfig.cmake$"
-"CPackSourceConfig.cmake$"
-"libsolv.spec$"
-)
-
-INCLUDE(CPack)
-
-####################################################################
-# INCLUDES #
-####################################################################
-
-#SET (CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)
-INCLUDE_DIRECTORIES (${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/ext ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR}/src SYSTEM )
-
-####################################################################
-
-MESSAGE (STATUS "Looking for modules in ${CMAKE_MODULE_PATH}")
-
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
-set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3")
-set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -g -O3")
-set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g3 -O0")
-
-SET (SYSTEM_LIBRARIES ${EXPAT_LIBRARY} ${ZLIB_LIBRARY})
-IF (ENABLE_LZMA_COMPRESSION)
-SET (SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} ${LZMA_LIBRARY})
-ENDIF (ENABLE_LZMA_COMPRESSION)
-IF (ENABLE_BZIP2_COMPRESSION)
-SET (SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} ${BZIP2_LIBRARIES})
-ENDIF (ENABLE_BZIP2_COMPRESSION)
-IF (ENABLE_RPMDB)
-SET (SYSTEM_LIBRARIES ${RPMDB_LIBRARY} ${SYSTEM_LIBRARIES})
-ENDIF (ENABLE_RPMDB)
-IF (ENABLE_HAIKU)
-SET (SYSTEM_LIBRARIES ${HAIKU_SYSTEM_LIBRARIES} ${SYSTEM_LIBRARIES})
-ENDIF (ENABLE_HAIKU)
-IF (HAVE_LINKER_AS_NEEDED)
-SET (SYSTEM_LIBRARIES "-Wl,--as-needed" ${SYSTEM_LIBRARIES})
-ENDIF (HAVE_LINKER_AS_NEEDED)
-
-ADD_SUBDIRECTORY (src)
-ADD_SUBDIRECTORY (ext)
-ADD_SUBDIRECTORY (tools)
-IF (ENABLE_PERL OR ENABLE_PYTHON OR ENABLE_RUBY OR ENABLE_TCL)
- ADD_SUBDIRECTORY (bindings)
-ENDIF (ENABLE_PERL OR ENABLE_PYTHON OR ENABLE_RUBY OR ENABLE_TCL)
-ADD_SUBDIRECTORY (examples)
-ADD_SUBDIRECTORY (doc)
-
-MESSAGE (STATUS "Version: ${VERSION}")
-
-####################################################################
-# RPM SPEC #
-####################################################################
-
-MACRO (SPECFILE)
- MESSAGE (STATUS "Writing spec file...")
- CONFIGURE_FILE (${CMAKE_SOURCE_DIR}/package/libsolv.spec.in ${CMAKE_BINARY_DIR}/package/libsolv.spec @ONLY)
-ENDMACRO (SPECFILE)
-
-MACRO (PCFILE)
- MESSAGE (STATUS "Writing pkg-config file...")
- CONFIGURE_FILE (${CMAKE_SOURCE_DIR}/libsolv.pc.in ${CMAKE_BINARY_DIR}/libsolv.pc @ONLY)
- INSTALL( FILES ${CMAKE_BINARY_DIR}/libsolv.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
-ENDMACRO (PCFILE)
-
-SPECFILE ()
-PCFILE ()
-
-SET (AUTOBUILD_COMMAND
- COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/package/*.tar.bz2
- COMMAND mkdir -p _CPack_Packages/${CPACK_TOPLEVEL_TAG}
- COMMAND ${CMAKE_MAKE_PROGRAM} package_source
- COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2 ${CMAKE_BINARY_DIR}/package
- COMMAND ${CMAKE_COMMAND} -E remove ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2
- COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/package/libsolv.changes" "${CMAKE_BINARY_DIR}/package/libsolv.changes"
-)
-
-ADD_CUSTOM_TARGET (srcpackage
- ${AUTOBUILD_COMMAND}
-)
-
-ADD_CUSTOM_TARGET (srcpackage_local
- ${AUTOBUILD_COMMAND}
-)
-
-ENABLE_TESTING()
-ADD_SUBDIRECTORY (test)
+++ /dev/null
-
-Klaus Kaempf
- - old language bindings
-
-Duncan Mac-Vicar Prett
- - cmake support
- - old ruby bindings
- - many of the xml parsers
-
-Michael Matz
- - repodata storage
- - repopage compression
- - dataiterator code
-
-Michael Schroeder
- - overall design
- - pool & solver implementation
- - new language
- - debian support
- - mandriva/mageia support
- - archlinux support
-
-Ingo Weinhold
- - haiku support
-
+++ /dev/null
-Compiling and installing the software
-
-Requirements:
-
-- C compiler
-- cmake
-- make
-- expat
-
-Steps to compile/install:
-
-1. mkdir build
-2. cd build
-3. cmake ..
-4. make
-
-5. make install
-
+++ /dev/null
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-3. Neither the name of Novell nor the names of its contributors may
- be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--- /dev/null
+use ExtUtils::MakeMaker;
+
+my $solvprefix = '/usr';
+
+my $inc = "-I$solvprefix/include/solv";
+my $lib = '';
+
+if (grep {$_ eq '--bundled-libsolv'} @ARGV) {
+ my $builddir = 'libsolv';
+ $inc = "-I$builddir/src -I$builddir/ext";
+ $lib = "-L$builddir/src -L$builddir/ext";
+}
+
+$lib = ($lib ? "$lib " : '') . '-lsolvext -lsolv -lz -llzma';
+
+WriteMakefile(
+ NAME => 'BSSolv',
+ VERSION_FROM => 'BSSolv.pm',
+ INC => $inc,
+ LIBS => [ $lib ],
+)
+++ /dev/null
-
-This file contains the major changes between
-libsolv versions:
-
-Version 0.6.12:
-- new features:
- * tcl bindings
-- new functions:
- * solv_chksum_cmp
-
-Version 0.6.11:
-- new functions:
- * pool_ids2whatprovides
-
-Version 0.6.9:
-- new features:
- * much improved package choosing code
- * new testcase dependency format
- * alternatives introspection
-- new functions:
- * pool_deb_get_autoinstalled
- * solver_alternative2str
- * solver_alternatives_count
- * solver_get_alternative
- * solver_rule2pkgrule
- * testcase_dep2str
-
-Version 0.6.5:
-- new features:
- * support yum style obsolete handling
-
-Version 0.6.1:
-- API change:
- repodata_stringify() now returns the string
-- new features:
- * add BREAK_ORPHANS and KEEP_ORPHANS solver flags
-
-Version 0.6.0:
-- ABI change: cleaned up and reordered knownid.h
-- added support for sha224/sha384/sha512
-- API change in the bindings:
- * dropped solvid arg from most Dataiterator
- constructors
- * changed Datamatch results from methods to
- attributes
- * automatically delete the pool if the owner
- object is freed (use the disown method to
- get the old behavior).
-- new functions:
- * pool_add_userinstalled_jobs
- * solver_get_userinstalled
-
-This is libsolv, a free package dependency solver using a satisfiability
-algorithm.
-
-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 satisfiability, a well known and researched topic, for
- resolving package dependencies.
-
-The sat-solver code has been written to aim for the newest packages,
-record the decision tree to provide introspection, and also allows to
-provide the user with suggestions on how to deal with unsolvable
-problems. It also takes advantage of the repository storage to
-minimize memory usage.
-
-Supported package formats:
- - rpm/rpm5
- - deb
- - arch linux
- - haiku
-
-Supported repository formats:
- - rpmmd (primary, filelists, comps, deltainfo/presto, updateinfo)
- - susetags, suse product formats
- - mandriva/mageia (synthesis, info, files)
- - arch linux
- - red carpet helix format
- - haiku
-
-Requires: cmake 2.4.x
-
-mkdir build
-cd build
-cmake ..
-make
-
-To create a package:
-make srcpackage
-see package/
+++ /dev/null
-# ==================================================
-# Versioning
-# ==========
-#
-# MAJOR Major number for this branch.
-#
-# MINOR The most recent interface number this
-# library implements.
-#
-# COMPATMINOR The latest binary compatible minor number
-# this library implements.
-#
-# PATCH The implementation number of the current interface.
-#
-#
-# - The package VERSION will be MAJOR.MINOR.PATCH.
-#
-# - Libtool's -version-info will be derived from MAJOR, MINOR, PATCH
-# and COMPATMINOR (see configure.ac).
-#
-# - Changing MAJOR always breaks binary compatibility.
-#
-# - Changing MINOR doesn't break binary compatibility by default.
-# Only if COMPATMINOR is changed as well.
-#
-#
-# 1) After branching from TRUNK increment TRUNKs MAJOR and
-# start with version `MAJOR.0.0' and also set COMPATMINOR to 0.
-#
-# 2) Update the version information only immediately before a public release
-# of your software. More frequent updates are unnecessary, and only guarantee
-# that the current interface number gets larger faster.
-#
-# 3) If the library source code has changed at all since the last update,
-# then increment PATCH.
-#
-# 4) If any interfaces have been added, removed, or changed since the last
-# update, increment MINOR, and set PATCH to 0.
-#
-# 5) If any interfaces have been added since the last public release, then
-# leave COMPATMINOR unchanged. (binary compatible change)
-#
-# 6) If any interfaces have been removed since the last public release, then
-# set COMPATMINOR to MINOR. (binary incompatible change)
-#
-
-SET(LIBSOLV_SOVERSION "0")
-SET(LIBSOLVEXT_SOVERSION "0")
-
-SET(LIBSOLV_MAJOR "0")
-SET(LIBSOLV_MINOR "6")
-SET(LIBSOLV_PATCH "15")
-
+++ /dev/null
-SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
-
-FIND_PACKAGE (SWIG)
-
-MESSAGE (STATUS "Found SWIG version ${SWIG_VERSION}")
-SET (SWIG_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/solv.i")
-
-IF (ENABLE_PYTHON)
- ADD_SUBDIRECTORY (python)
-ENDIF (ENABLE_PYTHON)
-IF (ENABLE_PERL)
- ADD_SUBDIRECTORY (perl)
-ENDIF (ENABLE_PERL)
-IF (ENABLE_RUBY)
- ADD_SUBDIRECTORY (ruby)
-ENDIF (ENABLE_RUBY)
-IF (ENABLE_TCL)
- ADD_SUBDIRECTORY (tcl)
-ENDIF (ENABLE_TCL)
+++ /dev/null
-FIND_PACKAGE (Perl)
-
-EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{ccflags}" OUTPUT_VARIABLE PERL_CCFLAGS)
-EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{archlib}.\"/CORE\"" OUTPUT_VARIABLE PERL_CORE_DIR)
-EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{ccldflags}" OUTPUT_VARIABLE PERL_CCLDFLAGS)
-EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{installsitearch}" OUTPUT_VARIABLE PERL_SITEARCHDIR)
-EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{installvendorarch}" OUTPUT_VARIABLE PERL_VENDORARCHDIR)
-
-IF (USE_VENDORDIRS)
- SET (PERL_INSTALL_DIR ${PERL_VENDORARCHDIR})
-ELSE (USE_VENDORDIRS)
- SET (PERL_INSTALL_DIR ${PERL_SITEARCHDIR})
-ENDIF (USE_VENDORDIRS)
-
-MESSAGE (STATUS "Perl executable: ${PERL_EXECUTABLE}")
-MESSAGE (STATUS "Perl installation dir: ${PERL_INSTALL_DIR}")
-
-ADD_CUSTOM_COMMAND (
- OUTPUT solv_perl.c
- COMMAND ${SWIG_EXECUTABLE} -perl ${SWIG_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_perl.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
- COMMAND sed -i -e "s/SvTYPE(tsv) == SVt_PVHV/SvTYPE(tsv) == SVt_PVHV || SvTYPE(tsv) == SVt_PVAV/" solv_perl.c
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
- VERBATIM
-)
-
-ADD_DEFINITIONS(${PERL_CCFLAGS} -Wno-unused -Wno-nonnull)
-LINK_DIRECTORIES (${PERL_CORE_DIR})
-INCLUDE_DIRECTORIES (${PERL_INCLUDE_PATH} ${PERL_CORE_DIR})
-
-ADD_LIBRARY (bindings_perl MODULE solv_perl.c)
-SET_TARGET_PROPERTIES (bindings_perl PROPERTIES PREFIX "" OUTPUT_NAME "solv")
-SET_TARGET_PROPERTIES (bindings_perl PROPERTIES LINK_FLAGS "${PERL_CCLDFLAGS}")
-TARGET_LINK_LIBRARIES (bindings_perl libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-INSTALL (TARGETS bindings_perl LIBRARY DESTINATION ${PERL_INSTALL_DIR})
-INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/solv.pm DESTINATION ${PERL_INSTALL_DIR})
+++ /dev/null
-#SET (PythonLibs_FIND_VERSION 3)
-
-FIND_PACKAGE (PythonLibs)
-FIND_PACKAGE (PythonInterp ${PYTHONLIBS_VERSION_STRING} REQUIRED)
-EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from sys import stdout; from distutils import sysconfig; stdout.write(sysconfig.get_python_lib(True))" OUTPUT_VARIABLE PYTHON_INSTALL_DIR)
-
-IF (NOT DEFINED PYTHON_VERSION_MAJOR)
- SET (PYTHON_VERSION_MAJOR 2)
-ENDIF (NOT DEFINED PYTHON_VERSION_MAJOR)
-IF (${PYTHON_VERSION_MAJOR} GREATER 2)
- SET (SWIG_PY_FLAGS -DPYTHON3=1)
-ENDIF (${PYTHON_VERSION_MAJOR} GREATER 2)
-
-MESSAGE (STATUS "Python executable: ${PYTHON_EXECUTABLE}")
-MESSAGE (STATUS "Python installation dir: ${PYTHON_INSTALL_DIR}")
-
-ADD_CUSTOM_COMMAND (
- OUTPUT solv_python.c
- COMMAND ${SWIG_EXECUTABLE} ${SWIG_FLAGS} -python ${SWIG_PY_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_python.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
-)
-
-ADD_DEFINITIONS(-Wno-unused)
-INCLUDE_DIRECTORIES (${PYTHON_INCLUDE_PATH})
-
-ADD_LIBRARY (bindings_python MODULE solv_python.c)
-SET_TARGET_PROPERTIES (bindings_python PROPERTIES PREFIX "" OUTPUT_NAME "_solv")
-TARGET_LINK_LIBRARIES (bindings_python libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-INSTALL (TARGETS bindings_python LIBRARY DESTINATION ${PYTHON_INSTALL_DIR})
-INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/solv.py DESTINATION ${PYTHON_INSTALL_DIR})
-
+++ /dev/null
-FIND_PACKAGE (Ruby)
-
-IF (USE_VENDORDIRS AND RUBY_VENDORARCH_DIR)
- SET (RUBY_INSTALL_DIR ${RUBY_VENDORARCH_DIR})
-ELSE (USE_VENDORDIRS AND RUBY_VENDORARCH_DIR)
- SET (RUBY_INSTALL_DIR ${RUBY_SITEARCH_DIR})
-ENDIF (USE_VENDORDIRS AND RUBY_VENDORARCH_DIR)
-
-MESSAGE (STATUS "Ruby executable: ${RUBY_EXECUTABLE}")
-MESSAGE (STATUS "Ruby installation dir: ${RUBY_INSTALL_DIR}")
-
-ADD_CUSTOM_COMMAND (
- OUTPUT solv_ruby.c
- COMMAND ${SWIG_EXECUTABLE} -ruby ${SWIG_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_ruby.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
-)
-
-ADD_DEFINITIONS(-Wno-unused)
-INCLUDE_DIRECTORIES (${RUBY_INCLUDE_PATH})
-
-ADD_LIBRARY (bindings_ruby MODULE solv_ruby.c)
-SET_TARGET_PROPERTIES (bindings_ruby PROPERTIES PREFIX "" OUTPUT_NAME "solv")
-TARGET_LINK_LIBRARIES (bindings_ruby libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-INSTALL (TARGETS bindings_ruby LIBRARY DESTINATION ${RUBY_INSTALL_DIR})
+++ /dev/null
-/*
- * WARNING: for perl iterator/array support you need to run
- * sed -i -e 's/SvTYPE(tsv) == SVt_PVHV/SvTYPE(tsv) == SVt_PVHV || SvTYPE(tsv) == SVt_PVAV/'
- * on the generated c code
- */
-
-%module solv
-
-#ifdef SWIGRUBY
-%markfunc Pool "mark_Pool";
-#endif
-
-/**
- ** binaryblob handling
- **/
-
-%{
-typedef struct {
- const void *data;
- size_t len;
-} BinaryBlob;
-%}
-
-%typemap(in,noblock=1,fragment="SWIG_AsCharPtrAndSize") (const unsigned char *str, size_t len) (int res, char *buf = 0, size_t size = 0, int alloc = 0) {
-#if defined(SWIGTCL)
- {
- int bal;
- unsigned char *ba;
- res = SWIG_TypeError;
- ba = Tcl_GetByteArrayFromObj($input, &bal);
- if (ba) {
- buf = (char *)ba;
- size = bal;
- res = SWIG_OK;
- alloc = SWIG_OLDOBJ;
- }
- }
-#else
- res = SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc);
- if (buf && size)
- size--;
-#endif
- if (!SWIG_IsOK(res)) {
-#if defined(SWIGPYTHON)
- const void *pybuf = 0;
- Py_ssize_t pysize = 0;
- res = PyObject_AsReadBuffer($input, &pybuf, &pysize);
- if (res < 0) {
- %argument_fail(res, "BinaryBlob", $symname, $argnum);
- } else {
- buf = (void *)pybuf;
- size = pysize;
- }
-#else
- %argument_fail(res, "const char *", $symname, $argnum);
-#endif
- }
- $1 = (unsigned char *)buf;
- $2 = size;
-}
-
-%typemap(freearg,noblock=1,match="in") (const unsigned char *str, int len) {
- if (alloc$argnum == SWIG_NEWOBJ) %delete_array(buf$argnum);
-}
-
-%typemap(out,noblock=1,fragment="SWIG_FromCharPtrAndSize") BinaryBlob {
-#if defined(SWIGPYTHON) && defined(PYTHON3)
- $result = $1.data ? Py_BuildValue("y#", $1.data, $1.len) : SWIG_Py_Void();
-#elif defined(SWIGTCL)
- Tcl_SetObjResult(interp, $1.data ? Tcl_NewByteArrayObj($1.data, $1.len) : NULL);
-#else
- $result = SWIG_FromCharPtrAndSize($1.data, $1.len);
-#if defined(SWIGPERL)
- argvi++;
-#endif
-#endif
-}
-
-/**
- ** Queue handling
- **/
-
-%typemap(arginit) Queue {
- queue_init(&$1);
-}
-%typemap(freearg) Queue {
- queue_free(&$1);
-}
-
-#if defined(SWIGPYTHON)
-%typemap(in) Queue {
- /* Check if is a list */
- if (PyList_Check($input)) {
- int size = PyList_Size($input);
- int i = 0;
- for (i = 0; i < size; i++) {
- PyObject *o = PyList_GetItem($input,i);
- int v;
- int e = SWIG_AsVal_int(o, &v);
- if (!SWIG_IsOK(e))
- SWIG_exception_fail(SWIG_ArgError(e), "list must contain only integers");
- queue_push(&$1, v);
- }
- } else {
- SWIG_exception_fail(SWIG_TypeError, "list must contain only integers");
- }
-}
-
-%typemap(out) Queue {
- int i;
- PyObject *o = PyList_New($1.count);
- for (i = 0; i < $1.count; i++)
- PyList_SetItem(o, i, SWIG_From_int($1.elements[i]));
- queue_free(&$1);
- $result = o;
-}
-
-%define Queue2Array(type, step, con) %{
- int i;
- int cnt = $1.count / step;
- Id *idp = $1.elements;
- PyObject *o = PyList_New(cnt);
- for (i = 0; i < cnt; i++, idp += step)
- {
- Id id = *idp;
-#define result resultx
- type result = con;
- $typemap(out, type)
- PyList_SetItem(o, i, $result);
-#undef result
- }
- queue_free(&$1);
- $result = o;
-%}
-
-%enddef
-
-#endif /* SWIGPYTHON */
-
-#if defined(SWIGPERL)
-%typemap(in) Queue {
- AV *av;
- int i, size;
- if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV)
- SWIG_croak("Argument $argnum is not an array reference.");
- av = (AV*)SvRV($input);
- size = av_len(av);
- for (i = 0; i <= size; i++) {
- SV **sv = av_fetch(av, i, 0);
- int v;
- int e = SWIG_AsVal_int(*sv, &v);
- if (!SWIG_IsOK(e)) {
- SWIG_croak("list must contain only integers");
- }
- queue_push(&$1, v);
- }
-}
-/* AV *o = newAV();
- * av_push(o, SvREFCNT_inc(SWIG_From_int($1.elements[i])));
- * $result = newRV_noinc((SV*)o); argvi++;
- */
-%typemap(out) Queue {
- int i;
- if (argvi + $1.count + 1 >= items) {
- EXTEND(sp, (argvi + $1.count + 1) - items + 1);
- }
- for (i = 0; i < $1.count; i++)
- ST(argvi++) = SvREFCNT_inc(SWIG_From_int($1.elements[i]));
- queue_free(&$1);
- $result = 0;
-}
-
-%define Queue2Array(type, step, con) %{
- int i;
- int cnt = $1.count / step;
- Id *idp = $1.elements;
- if (argvi + cnt + 1 >= items) {
- EXTEND(sp, (argvi + cnt + 1) - items + 1);
- }
- for (i = 0; i < cnt; i++, idp += step)
- {
- Id id = *idp;
-#define result resultx
- type result = con;
- $typemap(out, type)
- SvREFCNT_inc(ST(argvi - 1));
-#undef result
- }
- queue_free(&$1);
- $result = 0;
-%}
-%enddef
-
-#endif /* SWIGPERL */
-
-
-#if defined(SWIGRUBY)
-%typemap(in) Queue {
- int size, i;
- VALUE *o, ary;
- ary = rb_Array($input);
- size = RARRAY_LEN(ary);
- i = 0;
- o = RARRAY_PTR(ary);
- for (i = 0; i < size; i++, o++) {
- int v;
- int e = SWIG_AsVal_int(*o, &v);
- if (!SWIG_IsOK(e))
- SWIG_exception_fail(SWIG_TypeError, "list must contain only integers");
- queue_push(&$1, v);
- }
-}
-%typemap(out) Queue {
- int i;
- VALUE o = rb_ary_new2($1.count);
- for (i = 0; i < $1.count; i++)
- rb_ary_store(o, i, SWIG_From_int($1.elements[i]));
- queue_free(&$1);
- $result = o;
-}
-%typemap(arginit) Queue {
- queue_init(&$1);
-}
-%typemap(freearg) Queue {
- queue_free(&$1);
-}
-%define Queue2Array(type, step, con) %{
- int i;
- int cnt = $1.count / step;
- Id *idp = $1.elements;
- VALUE o = rb_ary_new2(cnt);
- for (i = 0; i < cnt; i++, idp += step)
- {
- Id id = *idp;
-#define result resultx
- type result = con;
- $typemap(out, type)
- rb_ary_store(o, i, $result);
-#undef result
- }
- queue_free(&$1);
- $result = o;
-%}
-%enddef
-
-#endif /* SWIGRUBY */
-
-#if defined(SWIGTCL)
-%typemap(in) Queue {
- /* Check if is a list */
- int size = 0;
- int i = 0;
-
- if (TCL_OK != Tcl_ListObjLength(interp, $input, &size))
- SWIG_exception_fail(SWIG_TypeError, "argument is not a list");
- for (i = 0; i < size; i++) {
- Tcl_Obj *o = NULL;
- int e, v;
-
- if (TCL_OK != Tcl_ListObjIndex(interp, $input, i, &o))
- SWIG_exception_fail(SWIG_IndexError, "failed to retrieve a list member");
- e = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(o, &v);
- if (!SWIG_IsOK(e))
- SWIG_exception_fail(SWIG_ArgError(e), "list must contain only integers");
- queue_push(&$1, v);
- }
-}
-
-%typemap(out) Queue {
- Tcl_Obj *objvx[$1.count];
- int i;
-
- for (i = 0; i < $1.count; i++) {
- objvx[i] = SWIG_From_int($1.elements[i]);
- }
- Tcl_SetObjResult(interp, Tcl_NewListObj($1.count, objvx));
- queue_free(&$1);
-}
-
-%define Queue2Array(type, step, con) %{
- { /* scope is needed to make the goto of SWIG_exception_fail work */
- int i;
- int cnt = $1.count / step;
- Id *idp = $1.elements;
- Tcl_Obj *objvx[cnt];
-
- for (i = 0; i < cnt; i++, idp += step) {
- Id id = *idp;
-#define result resultx
-#define Tcl_SetObjResult(i, x) resultobj = x
- type result = con;
- Tcl_Obj *resultobj;
- $typemap(out, type)
- objvx[i] = resultobj;
-#undef Tcl_SetObjResult
-#undef result
- }
- queue_free(&$1);
- Tcl_SetObjResult(interp, Tcl_NewListObj(cnt, objvx));
- }
-%}
-
-%enddef
-
-%typemap(in) Queue solvejobs {
- /* Check if is a list */
- int size = 0;
- int i = 0;
-
- if (TCL_OK != Tcl_ListObjLength(interp, $input, &size))
- SWIG_exception_fail(SWIG_TypeError, "argument is not a list");
- for (i = 0; i < size; i++) {
- Tcl_Obj *o = NULL;
- void *jp;
- Job *j;
- int e;
-
- if (TCL_OK != Tcl_ListObjIndex(interp, $input, i, &o))
- SWIG_exception_fail(SWIG_IndexError, "failed to retrieve a list member");
- e = SWIG_ConvertPtr(o, &jp ,SWIGTYPE_p_Job, 0 | 0 );
- if (!SWIG_IsOK(e))
- SWIG_exception_fail(SWIG_ArgError(e), "list member is not a Job");
- j = (Job *)jp;
- queue_push2(&$1, j->how, j->what);
- }
-}
-
-#endif /* SWIGTCL */
-
-
-#if defined(SWIGPERL)
-
-/* work around a swig bug */
-%{
-#undef SWIG_CALLXS
-#ifdef PERL_OBJECT
-# define SWIG_CALLXS(_name) TOPMARK=MARK-PL_stack_base;_name(cv,pPerl)
-#else
-# ifndef MULTIPLICITY
-# define SWIG_CALLXS(_name) TOPMARK=MARK-PL_stack_base;_name(cv)
-# else
-# define SWIG_CALLXS(_name) TOPMARK=MARK-PL_stack_base;_name(PERL_GET_THX, cv)
-# endif
-#endif
-%}
-
-
-%define perliter(class)
- %perlcode {
- sub class##::FETCH {
- my $i = ${##class##::ITERATORS}{$_[0]};
- if ($i) {
- $_[1] == $i->[0] - 1 ? $i->[1] : undef;
- } else {
- $_[0]->__getitem__($_[1]);
- }
- }
- sub class##::FETCHSIZE {
- my $i = ${##class##::ITERATORS}{$_[0]};
- if ($i) {
- ($i->[1] = $_[0]->__next__()) ? ++$i->[0] : 0;
- } else {
- $_[0]->__len__();
- }
- }
- }
-%enddef
-
-%{
-
-#define SWIG_PERL_ITERATOR 0x80
-
-SWIGRUNTIMEINLINE SV *
-SWIG_Perl_NewArrayObj(SWIG_MAYBE_PERL_OBJECT void *ptr, swig_type_info *t, int flags) {
- SV *result = sv_newmortal();
- if (ptr && (flags & (SWIG_SHADOW | SWIG_POINTER_OWN))) {
- SV *self;
- SV *obj=newSV(0);
- AV *array=newAV();
- HV *stash;
- sv_setref_pv(obj, (char *) SWIG_Perl_TypeProxyName(t), ptr);
- stash=SvSTASH(SvRV(obj));
- if (flags & SWIG_POINTER_OWN) {
- HV *hv;
- GV *gv=*(GV**)hv_fetch(stash, "OWNER", 5, TRUE);
- if (!isGV(gv))
- gv_init(gv, stash, "OWNER", 5, FALSE);
- hv=GvHVn(gv);
- hv_store_ent(hv, obj, newSViv(1), 0);
- }
- if (flags & SWIG_PERL_ITERATOR) {
- HV *hv;
- GV *gv=*(GV**)hv_fetch(stash, "ITERATORS", 9, TRUE);
- AV *av=newAV();
- if (!isGV(gv))
- gv_init(gv, stash, "ITERATORS", 9, FALSE);
- hv=GvHVn(gv);
- hv_store_ent(hv, obj, newRV_inc((SV *)av), 0);
- }
- sv_magic((SV *)array, (SV *)obj, 'P', Nullch, 0);
- SvREFCNT_dec(obj);
- self=newRV_noinc((SV *)array);
- sv_setsv(result, self);
- SvREFCNT_dec((SV *)self);
- sv_bless(result, stash);
- } else {
- sv_setref_pv(result, (char *) SWIG_Perl_TypeProxyName(t), ptr);
- }
- return result;
-}
-
-%}
-
-%typemap(out) Perlarray {
- ST(argvi) = SWIG_Perl_NewArrayObj(SWIG_PERL_OBJECT_CALL SWIG_as_voidptr(result), $1_descriptor, $owner | $shadow); argvi++;
-}
-%typemap(out) Perliterator {
- ST(argvi) = SWIG_Perl_NewArrayObj(SWIG_PERL_OBJECT_CALL SWIG_as_voidptr(result), $1_descriptor, $owner | $shadow | SWIG_PERL_ITERATOR); argvi++;
-}
-
-%typemap(out) Pool_solvable_iterator * = Perlarray;
-%typemap(out) Pool_solvable_iterator * solvables_iter = Perliterator;
-%typemap(out) Pool_repo_iterator * = Perlarray;
-%typemap(out) Pool_repo_iterator * repos_iter = Perliterator;
-%typemap(out) Repo_solvable_iterator * = Perlarray;
-%typemap(out) Repo_solvable_iterator * solvables_iter = Perliterator;
-%typemap(out) Dataiterator * = Perliterator;
-
-#endif /* SWIGPERL */
-
-
-/**
- ** appdata handling
- **/
-
-#if defined(SWIGPYTHON)
-typedef PyObject *AppObjectPtr;
-%typemap(in) AppObjectPtr {
- if ($input)
- Py_INCREF($input);
- $1 = $input;
-}
-%typemap(out) AppObjectPtr {
- $result = $1 ? $1 : Py_None;
- Py_INCREF($result);
-}
-#elif defined(SWIGPERL)
-typedef SV *AppObjectPtr;
-%typemap(in) AppObjectPtr {
- if ($input) {
- $1 = newSV(0);
- sv_setsv((SV *)$1, $input);
- } else
- $1 = (void *)0;
-}
-%typemap(out) AppObjectPtr {
- $result = sv_2mortal($1 ? SvREFCNT_inc($1) : newSV(0));
- argvi++;
-}
-#elif defined(SWIGRUBY)
-typedef VALUE AppObjectPtr;
-%typemap(in) AppObjectPtr {
- $1 = (void *)$input;
-}
-%typemap(out) AppObjectPtr {
- $result = (VALUE)$1;
-}
-#elif defined(SWIGTCL)
-typedef Tcl_Obj *AppObjectPtr;
-%typemap(in) AppObjectPtr {
- if ($input)
- Tcl_IncrRefCount($input);
- $1 = (void *)$input;
-}
-%typemap(out) AppObjectPtr {
- Tcl_SetObjResult(interp, $1 ? $1 : Tcl_NewObj());
-}
-#else
-#warning AppObjectPtr not defined for this language!
-#endif
-
-/**
- ** FILE handling
- **/
-
-#ifdef SWIGPYTHON
-%include "file.i"
-#else
-%fragment("SWIG_AsValFilePtr","header") {}
-#endif
-
-
-%fragment("SWIG_AsValSolvFpPtr","header", fragment="SWIG_AsValFilePtr") {
-
-SWIGINTERN int
-#ifdef SWIGRUBY
-SWIG_AsValSolvFpPtr(VALUE obj, FILE **val) {
-#elif defined(SWIGTCL)
-SWIG_AsValSolvFpPtr SWIG_TCL_DECL_ARGS_2(void *obj, FILE **val) {
-#else
-SWIG_AsValSolvFpPtr(void *obj, FILE **val) {
-#endif
- static swig_type_info* desc = 0;
- void *vptr = 0;
- int ecode;
-
- if (!desc) desc = SWIG_TypeQuery("SolvFp *");
- if ((SWIG_ConvertPtr(obj, &vptr, desc, 0)) == SWIG_OK) {
- if (val)
- *val = vptr ? ((SolvFp *)vptr)->fp : 0;
- return SWIG_OK;
- }
-#ifdef SWIGPYTHON
- ecode = SWIG_AsValFilePtr(obj, val);
- if (ecode == SWIG_OK)
- return ecode;
-#endif
- return SWIG_TypeError;
-}
-
-#if defined(SWIGTCL)
-#define SWIG_AsValSolvFpPtr(x, y) SWIG_AsValSolvFpPtr SWIG_TCL_CALL_ARGS_2(x, y)
-#endif
-
-}
-
-
-/**
- ** DepId handling
- **/
-
-%fragment("SWIG_AsValDepId","header") {
-
-SWIGINTERN int
-#ifdef SWIGRUBY
-SWIG_AsValDepId(VALUE obj, int *val) {
-#elif defined(SWIGTCL)
-SWIG_AsValDepId SWIG_TCL_DECL_ARGS_2(void *obj, int *val) {
-#else
-SWIG_AsValDepId(void *obj, int *val) {
-#endif
- static swig_type_info* desc = 0;
- void *vptr = 0;
- int ecode;
- if (!desc) desc = SWIG_TypeQuery("Dep *");
-#ifdef SWIGTCL
- ecode = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(obj, val);
-#else
- ecode = SWIG_AsVal_int(obj, val);
-#endif
- if (SWIG_IsOK(ecode))
- return ecode;
- if ((SWIG_ConvertPtr(obj, &vptr, desc, 0)) == SWIG_OK) {
- if (val)
- *val = vptr ? ((Dep *)vptr)->id : 0;
- return SWIG_OK;
- }
- return SWIG_TypeError;
-}
-
-#ifdef SWIGTCL
-#define SWIG_AsValDepId(x, y) SWIG_AsValDepId SWIG_TCL_CALL_ARGS_2(x, y)
-#endif
-}
-
-/**
- ** Pool disown helper
- **/
-
-%typemap(out) disown_helper {
-#if defined(SWIGRUBY)
- SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
-#elif defined(SWIGPYTHON)
- SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
-#elif defined(SWIGPERL)
- SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
-#elif defined(SWIGTCL)
- SWIG_ConvertPtr(objv[1], &argp1, SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0);
-#else
-#warning disown_helper not implemented for this language, this is likely going to leak memory
-#endif
-
-#ifdef SWIGTCL
- Tcl_SetObjResult(interp, SWIG_From_int((int)(0)));
-#else
- $result = SWIG_From_int((int)(0));
-#endif
-}
-
-
-/**
- ** misc stuff
- **/
-
-%include "typemaps.i"
-
-%typemap(in,numinputs=0,noblock=1) XRule **OUTPUT ($*1_ltype temp) {
- $1 = &temp;
-}
-%typemap(argout,noblock=1) XRule **OUTPUT {
- %append_output(SWIG_NewPointerObj((void*)(*$1), SWIGTYPE_p_XRule, SWIG_POINTER_OWN | %newpointer_flags));
-}
-
-%typemaps_asval(%checkcode(POINTER), SWIG_AsValSolvFpPtr, "SWIG_AsValSolvFpPtr", FILE*);
-%typemaps_asval(%checkcode(INT32), SWIG_AsValDepId, "SWIG_AsValDepId", DepId);
-
-
-/**
- ** the C declarations
- **/
-
-%{
-#include <stdbool.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/utsname.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-/* argh, swig undefs bool for perl */
-#ifndef bool
-typedef int bool;
-#endif
-
-#include "pool.h"
-#include "poolarch.h"
-#include "evr.h"
-#include "solver.h"
-#include "policy.h"
-#include "solverdebug.h"
-#include "repo_solv.h"
-#include "chksum.h"
-#include "selection.h"
-
-#include "repo_write.h"
-#ifdef ENABLE_RPMDB
-#include "repo_rpmdb.h"
-#endif
-#ifdef ENABLE_PUBKEY
-#include "repo_pubkey.h"
-#endif
-#ifdef ENABLE_DEBIAN
-#include "repo_deb.h"
-#endif
-#ifdef ENABLE_RPMMD
-#include "repo_rpmmd.h"
-#include "repo_updateinfoxml.h"
-#include "repo_deltainfoxml.h"
-#include "repo_repomdxml.h"
-#endif
-#ifdef ENABLE_SUSEREPO
-#include "repo_products.h"
-#include "repo_susetags.h"
-#include "repo_content.h"
-#endif
-#ifdef ENABLE_MDKREPO
-#include "repo_mdk.h"
-#endif
-#ifdef ENABLE_ARCHREPO
-#include "repo_arch.h"
-#endif
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-#include "solv_xfopen.h"
-#include "testcase.h"
-
-/* for old ruby versions */
-#ifndef RARRAY_PTR
-#define RARRAY_PTR(ary) (RARRAY(ary)->ptr)
-#endif
-#ifndef RARRAY_LEN
-#define RARRAY_LEN(ary) (RARRAY(ary)->len)
-#endif
-
-#define SOLVER_SOLUTION_ERASE -100
-#define SOLVER_SOLUTION_REPLACE -101
-#define SOLVER_SOLUTION_REPLACE_DOWNGRADE -102
-#define SOLVER_SOLUTION_REPLACE_ARCHCHANGE -103
-#define SOLVER_SOLUTION_REPLACE_VENDORCHANGE -104
-#define SOLVER_SOLUTION_REPLACE_NAMECHANGE -105
-
-typedef void *AppObjectPtr;
-typedef Id DepId;
-
-typedef struct {
- Pool *pool;
- Id id;
-} Dep;
-
-typedef struct {
- Pool *pool;
- Id id;
-} XSolvable;
-
-typedef struct {
- Solver *solv;
- Id id;
-} XRule;
-
-typedef struct {
- Repo *repo;
- Id id;
-} XRepodata;
-
-typedef struct {
- Pool *pool;
- Id id;
-} Pool_solvable_iterator;
-
-typedef struct {
- Pool *pool;
- Id id;
-} Pool_repo_iterator;
-
-typedef struct {
- Repo *repo;
- Id id;
-} Repo_solvable_iterator;
-
-typedef struct {
- Pool *pool;
- int how;
- Id what;
-} Job;
-
-typedef struct {
- Solver *solv;
- Id id;
-} Problem;
-
-typedef struct {
- Solver *solv;
- Id problemid;
- Id id;
-} Solution;
-
-typedef struct {
- Solver *solv;
- Id problemid;
- Id solutionid;
- Id id;
-
- Id type;
- Id p;
- Id rp;
-} Solutionelement;
-
-typedef struct {
- Solver *solv;
- Id rid;
- Id type;
- Id source;
- Id target;
- Id dep_id;
-} Ruleinfo;
-
-typedef struct {
- Solver *solv;
- Id type;
- Id rid;
- Id from_id;
- Id dep_id;
- Id chosen_id;
- Queue choices;
- int level;
-} Alternative;
-
-typedef struct {
- Transaction *transaction;
- int mode;
- Id type;
- int count;
- Id fromid;
- Id toid;
-} TransactionClass;
-
-typedef struct {
- Pool *pool;
- Queue q;
- int flags;
-} Selection;
-
-typedef struct {
- FILE *fp;
-} SolvFp;
-
-typedef Dataiterator Datamatch;
-
-typedef int disown_helper;
-
-struct myappdata {
- void *appdata;
- int disowned;
-};
-
-
-%}
-
-/**
- ** appdata helpers
- **/
-
-#ifdef SWIGRUBY
-
-%{
-SWIGINTERN void appdata_disown_helper(void *appdata) {
-}
-SWIGINTERN void appdata_clr_helper(void **appdatap) {
- *appdatap = 0;
-}
-SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
- *appdatap = appdata;
-}
-SWIGINTERN void *appdata_get_helper(void *appdata) {
- return appdata;
-}
-%}
-
-#elif defined(SWIGTCL)
-
-%{
-SWIGINTERN void appdata_disown_helper(void *appdata) {
-}
-SWIGINTERN void appdata_clr_helper(void **appdatap) {
- if (*appdatap)
- Tcl_DecrRefCount((Tcl_Obj *)(*appdatap));
- *appdatap = 0;
-}
-SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
- appdata_clr_helper(appdatap);
- *appdatap = appdata;
-}
-SWIGINTERN void *appdata_get_helper(void *appdata) {
- return appdata;
-}
-%}
-
-#elif defined(SWIGPYTHON)
-
-%{
-SWIGINTERN void appdata_disown_helper(void *appdata) {
- struct myappdata *myappdata = appdata;
- if (!myappdata || !myappdata->appdata || myappdata->disowned)
- return;
- myappdata->disowned = 1;
- Py_DECREF((PyObject *)myappdata->appdata);
-}
-SWIGINTERN void appdata_clr_helper(void **appdatap) {
- struct myappdata *myappdata = *(struct myappdata **)appdatap;
- if (myappdata && myappdata->appdata && !myappdata->disowned) {
- Py_DECREF((PyObject *)myappdata->appdata);
- }
- *appdatap = solv_free(myappdata);
-}
-SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
- appdata_clr_helper(appdatap);
- if (appdata) {
- struct myappdata *myappdata = *appdatap = solv_calloc(sizeof(struct myappdata), 1);
- myappdata->appdata = appdata;
- }
-}
-SWIGINTERN void *appdata_get_helper(void *appdata) {
- return appdata ? ((struct myappdata *)appdata)->appdata : 0;
-}
-
-%}
-
-#elif defined(SWIGPERL)
-
-%{
-SWIGINTERN void appdata_disown_helper(void *appdata) {
- struct myappdata *myappdata = appdata;
- SV *rsv;
- if (!myappdata || !myappdata->appdata || myappdata->disowned)
- return;
- rsv = myappdata->appdata;
- if (!SvROK(rsv))
- return;
- myappdata->appdata = SvRV(rsv);
- myappdata->disowned = 1;
- SvREFCNT_dec(rsv);
-}
-SWIGINTERN void appdata_clr_helper(void **appdatap) {
- struct myappdata *myappdata = *(struct myappdata **)appdatap;
- if (myappdata && myappdata->appdata && !myappdata->disowned) {
- SvREFCNT_dec((SV *)myappdata->appdata);
- }
- *appdatap = solv_free(myappdata);
-}
-SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
- appdata_clr_helper(appdatap);
- if (appdata) {
- struct myappdata *myappdata = *appdatap = solv_calloc(sizeof(struct myappdata), 1);
- myappdata->appdata = appdata;
- }
-}
-SWIGINTERN void *appdata_get_helper(void *appdata) {
- struct myappdata *myappdata = appdata;
- if (!myappdata || !myappdata->appdata)
- return 0;
- return myappdata->disowned ? newRV_noinc((SV *)myappdata->appdata) : myappdata->appdata;
-}
-
-%}
-
-#else
-#warning appdata helpers not implemented for this language
-#endif
-
-
-/**
- ** the SWIG declarations defining the API
- **/
-
-#ifdef SWIGRUBY
-%mixin Dataiterator "Enumerable";
-%mixin Pool_solvable_iterator "Enumerable";
-%mixin Pool_repo_iterator "Enumerable";
-%mixin Repo_solvable_iterator "Enumerable";
-#endif
-
-typedef int Id;
-
-%include "knownid.h"
-
-/* from repodata.h */
-%constant Id SOLVID_META;
-%constant Id SOLVID_POS;
-
-%constant int REL_EQ;
-%constant int REL_GT;
-%constant int REL_LT;
-%constant int REL_ARCH;
-%constant int REL_AND;
-%constant int REL_OR;
-%constant int REL_WITH;
-%constant int REL_COND;
-%constant int REL_ELSE;
-
-typedef struct {
- Pool* const pool;
-} Selection;
-
-typedef struct {
- Pool* const pool;
- Id const id;
-} Dep;
-
-/* put before pool/repo so we can access the constructor */
-%nodefaultdtor Dataiterator;
-typedef struct {} Dataiterator;
-
-typedef struct {
- Pool* const pool;
- Id const id;
-} XSolvable;
-
-typedef struct {
- Solver* const solv;
- Id const type;
- Id const dep_id;
-} Ruleinfo;
-
-typedef struct {
- Solver* const solv;
- Id const id;
-} XRule;
-
-typedef struct {
- Repo* const repo;
- Id const id;
-} XRepodata;
-
-typedef struct {} Pool_solvable_iterator;
-typedef struct {} Pool_repo_iterator;
-typedef struct {} Repo_solvable_iterator;
-
-%nodefaultctor Datamatch;
-%nodefaultdtor Datamatch;
-typedef struct {
- Pool * const pool;
- Repo * const repo;
- Id const solvid;
-} Datamatch;
-
-%nodefaultctor Datapos;
-typedef struct {
- Repo * const repo;
-} Datapos;
-
-typedef struct {
- Pool * const pool;
- int how;
- Id what;
-} Job;
-
-%nodefaultctor Pool;
-%nodefaultdtor Pool;
-typedef struct {
-} Pool;
-
-%nodefaultctor Repo;
-%nodefaultdtor Repo;
-typedef struct {
- Pool * const pool;
- const char * const name;
- int priority;
- int subpriority;
- int const nsolvables;
-} Repo;
-
-%nodefaultctor Solver;
-%nodefaultdtor Solver;
-typedef struct {
- Pool * const pool;
-} Solver;
-
-typedef struct {
-} Chksum;
-
-#ifdef ENABLE_PUBKEY
-typedef struct {
- Id const htype;
- unsigned int const created;
- unsigned int const expires;
- const char * const keyid;
-} Solvsig;
-#endif
-
-%rename(xfopen) solvfp_xfopen;
-%rename(xfopen_fd) solvfp_xfopen_fd;
-
-%nodefaultctor SolvFp;
-typedef struct {
-} SolvFp;
-
-%newobject solvfp_xfopen;
-%newobject solvfp_xfopen_fd;
-
-SolvFp *solvfp_xfopen(const char *fn, const char *mode = 0);
-SolvFp *solvfp_xfopen_fd(const char *fn, int fd, const char *mode = 0);
-
-%{
- SWIGINTERN SolvFp *solvfp_xfopen_fd(const char *fn, int fd, const char *mode) {
- SolvFp *sfp;
- FILE *fp;
- fd = dup(fd);
- if (fd == -1)
- return 0;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fp = solv_xfopen_fd(fn, fd, mode);
- if (!fp) {
- close(fd);
- return 0;
- }
- sfp = solv_calloc(1, sizeof(SolvFp));
- sfp->fp = fp;
- return sfp;
- }
- SWIGINTERN SolvFp *solvfp_xfopen(const char *fn, const char *mode) {
- SolvFp *sfp;
- FILE *fp;
- fp = solv_xfopen(fn, mode);
- if (!fp)
- return 0;
- if (fileno(fp) != -1)
- fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
- sfp = solv_calloc(1, sizeof(SolvFp));
- sfp->fp = fp;
- return sfp;
- }
-%}
-
-typedef struct {
- Solver * const solv;
- Id const id;
-} Problem;
-
-typedef struct {
- Solver * const solv;
- Id const problemid;
- Id const id;
-} Solution;
-
-typedef struct {
- Solver *const solv;
- Id const problemid;
- Id const solutionid;
- Id const id;
- Id const type;
-} Solutionelement;
-
-%nodefaultctor Alternative;
-typedef struct {
- Solver *const solv;
- Id const type;
- Id const rid;
- Id const from_id;
- Id const dep_id;
- Id const chosen_id;
- int level;
-} Alternative;
-
-%nodefaultctor Transaction;
-%nodefaultdtor Transaction;
-typedef struct {
- Pool * const pool;
-} Transaction;
-
-typedef struct {
- Transaction * const transaction;
- Id const type;
- Id const fromid;
- Id const toid;
- int const count;
-} TransactionClass;
-
-%extend SolvFp {
- ~SolvFp() {
- if ($self->fp)
- fclose($self->fp);
- free($self);
- }
- int fileno() {
- return $self->fp ? fileno($self->fp) : -1;
- }
- int dup() {
- return $self->fp ? dup(fileno($self->fp)) : -1;
- }
- bool write(const unsigned char *str, size_t len) {
- return fwrite(str, len, 1, $self->fp) == 1;
- }
- bool flush() {
- if (!$self->fp)
- return 1;
- return fflush($self->fp) == 0;
- }
- bool close() {
- bool ret;
- if (!$self->fp)
- return 1;
- ret = fclose($self->fp) == 0;
- $self->fp = 0;
- return ret;
- }
- void cloexec(bool state) {
- if (!$self->fp || fileno($self->fp) == -1)
- return;
- fcntl(fileno($self->fp), F_SETFD, state ? FD_CLOEXEC : 0);
- }
-}
-
-%extend Job {
- static const Id SOLVER_SOLVABLE = SOLVER_SOLVABLE;
- static const Id SOLVER_SOLVABLE_NAME = SOLVER_SOLVABLE_NAME;
- static const Id SOLVER_SOLVABLE_PROVIDES = SOLVER_SOLVABLE_PROVIDES;
- static const Id SOLVER_SOLVABLE_ONE_OF = SOLVER_SOLVABLE_ONE_OF;
- static const Id SOLVER_SOLVABLE_REPO = SOLVER_SOLVABLE_REPO;
- static const Id SOLVER_SOLVABLE_ALL = SOLVER_SOLVABLE_ALL;
- static const Id SOLVER_SELECTMASK = SOLVER_SELECTMASK;
- static const Id SOLVER_NOOP = SOLVER_NOOP;
- static const Id SOLVER_INSTALL = SOLVER_INSTALL;
- static const Id SOLVER_ERASE = SOLVER_ERASE;
- static const Id SOLVER_UPDATE = SOLVER_UPDATE;
- static const Id SOLVER_WEAKENDEPS = SOLVER_WEAKENDEPS;
- static const Id SOLVER_MULTIVERSION = SOLVER_MULTIVERSION;
- static const Id SOLVER_LOCK = SOLVER_LOCK;
- static const Id SOLVER_DISTUPGRADE = SOLVER_DISTUPGRADE;
- static const Id SOLVER_VERIFY = SOLVER_VERIFY;
- static const Id SOLVER_DROP_ORPHANED = SOLVER_DROP_ORPHANED;
- static const Id SOLVER_USERINSTALLED = SOLVER_USERINSTALLED;
- static const Id SOLVER_ALLOWUNINSTALL = SOLVER_ALLOWUNINSTALL;
- static const Id SOLVER_JOBMASK = SOLVER_JOBMASK;
- static const Id SOLVER_WEAK = SOLVER_WEAK;
- static const Id SOLVER_ESSENTIAL = SOLVER_ESSENTIAL;
- static const Id SOLVER_CLEANDEPS = SOLVER_CLEANDEPS;
- static const Id SOLVER_FORCEBEST = SOLVER_FORCEBEST;
- static const Id SOLVER_TARGETED = SOLVER_TARGETED;
- static const Id SOLVER_NOTBYUSER = SOLVER_NOTBYUSER;
- static const Id SOLVER_SETEV = SOLVER_SETEV;
- static const Id SOLVER_SETEVR = SOLVER_SETEVR;
- static const Id SOLVER_SETARCH = SOLVER_SETARCH;
- static const Id SOLVER_SETVENDOR = SOLVER_SETVENDOR;
- static const Id SOLVER_SETREPO = SOLVER_SETREPO;
- static const Id SOLVER_SETNAME = SOLVER_SETNAME;
- static const Id SOLVER_NOAUTOSET = SOLVER_NOAUTOSET;
- static const Id SOLVER_SETMASK = SOLVER_SETMASK;
-
- Job(Pool *pool, int how, Id what) {
- Job *job = solv_calloc(1, sizeof(*job));
- job->pool = pool;
- job->how = how;
- job->what = what;
- return job;
- }
-
- %typemap(out) Queue solvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
- %newobject solvables;
- Queue solvables() {
- Queue q;
- queue_init(&q);
- pool_job2solvables($self->pool, &q, $self->how, $self->what);
- return q;
- }
-#ifdef SWIGRUBY
- %rename("isemptyupdate?") isemptyupdate;
-#endif
- bool isemptyupdate() {
- return pool_isemptyupdatejob($self->pool, $self->how, $self->what);
- }
-
-#if defined(SWIGTCL)
- %rename("==") __eq__;
-#endif
- bool __eq__(Job *j) {
- return $self->pool == j->pool && $self->how == j->how && $self->what == j->what;
- }
-#if defined(SWIGTCL)
- %rename("!=") __ne__;
-#endif
- bool __ne__(Job *j) {
- return !Job___eq__($self, j);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("str") __str__;
-#endif
- const char *__str__() {
- return pool_job2str($self->pool, $self->how, $self->what, 0);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("repr") __repr__;
-#endif
- const char *__repr__() {
- const char *str = pool_job2str($self->pool, $self->how, $self->what, ~0);
- return pool_tmpjoin($self->pool, "<Job ", str, ">");
- }
-}
-
-%extend Selection {
- static const Id SELECTION_NAME = SELECTION_NAME;
- static const Id SELECTION_PROVIDES = SELECTION_PROVIDES;
- static const Id SELECTION_FILELIST = SELECTION_FILELIST;
- static const Id SELECTION_CANON = SELECTION_CANON;
- static const Id SELECTION_DOTARCH = SELECTION_DOTARCH;
- static const Id SELECTION_REL = SELECTION_REL;
- static const Id SELECTION_INSTALLED_ONLY = SELECTION_INSTALLED_ONLY;
- static const Id SELECTION_GLOB = SELECTION_GLOB;
- static const Id SELECTION_FLAT = SELECTION_FLAT;
- static const Id SELECTION_NOCASE = SELECTION_NOCASE;
- static const Id SELECTION_SOURCE_ONLY = SELECTION_SOURCE_ONLY;
- static const Id SELECTION_WITH_SOURCE = SELECTION_WITH_SOURCE;
-
- Selection(Pool *pool) {
- Selection *s;
- s = solv_calloc(1, sizeof(*s));
- s->pool = pool;
- return s;
- }
-
- ~Selection() {
- queue_free(&$self->q);
- solv_free($self);
- }
- int flags() {
- return $self->flags;
- }
-#ifdef SWIGRUBY
- %rename("isempty?") isempty;
-#endif
- bool isempty() {
- return $self->q.count == 0;
- }
- void filter(Selection *lsel) {
- if ($self->pool != lsel->pool)
- queue_empty(&$self->q);
- else
- selection_filter($self->pool, &$self->q, &lsel->q);
- }
- void add(Selection *lsel) {
- if ($self->pool == lsel->pool)
- {
- selection_add($self->pool, &$self->q, &lsel->q);
- $self->flags |= lsel->flags;
- }
- }
- void add_raw(Id how, Id what) {
- queue_push2(&$self->q, how, what);
- }
- %typemap(out) Queue jobs Queue2Array(Job *, 2, new_Job(arg1->pool, id, idp[1]));
- %newobject jobs;
- Queue jobs(int flags) {
- Queue q;
- int i;
- queue_init_clone(&q, &$self->q);
- for (i = 0; i < q.count; i += 2)
- q.elements[i] |= flags;
- return q;
- }
-
- %typemap(out) Queue solvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
- %newobject solvables;
- Queue solvables() {
- Queue q;
- queue_init(&q);
- selection_solvables($self->pool, &$self->q, &q);
- return q;
- }
-
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("str") __str__;
-#endif
- const char *__str__() {
- return pool_selection2str($self->pool, &$self->q, 0);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("repr") __repr__;
-#endif
- const char *__repr__() {
- const char *str = pool_selection2str($self->pool, &$self->q, ~0);
- return pool_tmpjoin($self->pool, "<Selection ", str, ">");
- }
-}
-
-%extend Chksum {
- Chksum(Id type) {
- return solv_chksum_create(type);
- }
- Chksum(Id type, const char *hex) {
- unsigned char buf[64];
- int l = solv_chksum_len(type);
- if (!l)
- return 0;
- if (solv_hex2bin(&hex, buf, sizeof(buf)) != l || hex[0])
- return 0;
- return solv_chksum_create_from_bin(type, buf);
- }
- %newobject from_bin;
- static Chksum *from_bin(Id type, const unsigned char *str, size_t len) {
- return len == solv_chksum_len(type) ? solv_chksum_create_from_bin(type, str) : 0;
- }
-#if defined(SWIGPERL)
- %perlcode {
- undef *solv::Chksum::from_bin;
- *solv::Chksum::from_bin = sub {
- my $pkg = shift;
- my $self = solvc::Chksum_from_bin(@_);
- bless $self, $pkg if defined $self;
- };
- }
-#endif
- ~Chksum() {
- solv_chksum_free($self, 0);
- }
- Id const type;
- %{
- SWIGINTERN Id Chksum_type_get(Chksum *chk) {
- return solv_chksum_get_type(chk);
- }
- %}
- void add(const unsigned char *str, size_t len) {
- solv_chksum_add($self, str, (int)len);
- }
- void add_fp(FILE *fp) {
- char buf[4096];
- int l;
- while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
- solv_chksum_add($self, buf, l);
- rewind(fp); /* convenience */
- }
- void add_fd(int fd) {
- char buf[4096];
- int l;
- while ((l = read(fd, buf, sizeof(buf))) > 0)
- solv_chksum_add($self, buf, l);
- lseek(fd, 0, 0); /* convenience */
- }
- void add_stat(const char *filename) {
- struct stat stb;
- if (stat(filename, &stb))
- memset(&stb, 0, sizeof(stb));
- solv_chksum_add($self, &stb.st_dev, sizeof(stb.st_dev));
- solv_chksum_add($self, &stb.st_ino, sizeof(stb.st_ino));
- solv_chksum_add($self, &stb.st_size, sizeof(stb.st_size));
- solv_chksum_add($self, &stb.st_mtime, sizeof(stb.st_mtime));
- }
- void add_fstat(int fd) {
- struct stat stb;
- if (fstat(fd, &stb))
- memset(&stb, 0, sizeof(stb));
- solv_chksum_add($self, &stb.st_dev, sizeof(stb.st_dev));
- solv_chksum_add($self, &stb.st_ino, sizeof(stb.st_ino));
- solv_chksum_add($self, &stb.st_size, sizeof(stb.st_size));
- solv_chksum_add($self, &stb.st_mtime, sizeof(stb.st_mtime));
- }
- BinaryBlob raw() {
- BinaryBlob bl;
- int l;
- const unsigned char *b;
- b = solv_chksum_get($self, &l);
- bl.data = b;
- bl.len = l;
- return bl;
- }
- %newobject hex;
- char *hex() {
- int l;
- const unsigned char *b;
- char *ret;
-
- b = solv_chksum_get($self, &l);
- ret = solv_malloc(2 * l + 1);
- solv_bin2hex(b, l, ret);
- return ret;
- }
- const char *typestr() {
- return solv_chksum_type2str(solv_chksum_get_type($self));
- }
-
-#if defined(SWIGTCL)
- %rename("==") __eq__;
-#endif
- bool __eq__(Chksum *chk) {
- return solv_chksum_cmp($self, chk);
- }
-#if defined(SWIGTCL)
- %rename("!=") __ne__;
-#endif
- bool __ne__(Chksum *chk) {
- return !solv_chksum_cmp($self, chk);
- }
-#if defined(SWIGRUBY)
- %rename("to_s") __str__;
-#endif
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("str") __str__;
-#endif
- %newobject __str__;
- const char *__str__() {
- const char *str;
- const char *h = 0;
- if (solv_chksum_isfinished($self))
- h = Chksum_hex($self);
- str = solv_dupjoin(solv_chksum_type2str(solv_chksum_get_type($self)), ":", h ? h : "unfinished");
- solv_free((void *)h);
- return str;
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("repr") __repr__;
-#endif
- %newobject __repr__;
- const char *__repr__() {
- const char *h = Chksum___str__($self);
- const char *str = solv_dupjoin("<Chksum ", h, ">");
- solv_free((void *)h);
- return str;
- }
-}
-
-%extend Pool {
- static const int POOL_FLAG_PROMOTEEPOCH = POOL_FLAG_PROMOTEEPOCH;
- static const int POOL_FLAG_FORBIDSELFCONFLICTS = POOL_FLAG_FORBIDSELFCONFLICTS;
- static const int POOL_FLAG_OBSOLETEUSESPROVIDES = POOL_FLAG_OBSOLETEUSESPROVIDES;
- static const int POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES = POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES;
- static const int POOL_FLAG_OBSOLETEUSESCOLORS = POOL_FLAG_OBSOLETEUSESCOLORS;
- static const int POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS = POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS;
- static const int POOL_FLAG_NOINSTALLEDOBSOLETES = POOL_FLAG_NOINSTALLEDOBSOLETES;
- static const int POOL_FLAG_HAVEDISTEPOCH = POOL_FLAG_HAVEDISTEPOCH;
- static const int POOL_FLAG_NOOBSOLETESMULTIVERSION = POOL_FLAG_NOOBSOLETESMULTIVERSION;
-
- Pool() {
- Pool *pool = pool_create();
- return pool;
- }
- void set_debuglevel(int level) {
- pool_setdebuglevel($self, level);
- }
- int set_flag(int flag, int value) {
- return pool_set_flag($self, flag, value);
- }
- int get_flag(int flag) {
- return pool_get_flag($self, flag);
- }
- void set_rootdir(const char *rootdir) {
- pool_set_rootdir($self, rootdir);
- }
- const char *get_rootdir(int flag) {
- return pool_get_rootdir($self);
- }
-#if defined(SWIGPYTHON)
- %{
- SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
- XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
- PyObject *args = Py_BuildValue("(O)", SWIG_NewPointerObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, SWIG_POINTER_OWN | 0));
- PyObject *result = PyEval_CallObject((PyObject *)d, args);
- int ecode = 0;
- int vresult = 0;
- Py_DECREF(args);
- if (!result)
- return 0; /* exception */
- ecode = SWIG_AsVal_int(result, &vresult);
- Py_DECREF(result);
- return SWIG_IsOK(ecode) ? vresult : 0;
- }
- %}
- void clr_loadcallback() {
- if ($self->loadcallback == loadcallback) {
- PyObject *obj = $self->loadcallbackdata;
- Py_DECREF(obj);
- pool_setloadcallback($self, 0, 0);
- }
- }
- void set_loadcallback(PyObject *callable) {
- Pool_clr_loadcallback($self);
- if (callable) {
- Py_INCREF(callable);
- pool_setloadcallback($self, loadcallback, callable);
- }
- }
-#elif defined(SWIGPERL)
-%{
- SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
- int count;
- int ret = 0;
- dSP;
- XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
-
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
- XPUSHs(SWIG_NewPointerObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, SWIG_OWNER | SWIG_SHADOW));
- PUTBACK;
- count = perl_call_sv((SV *)d, G_EVAL|G_SCALAR);
- SPAGAIN;
- if (count)
- ret = POPi;
- PUTBACK;
- FREETMPS;
- LEAVE;
- return ret;
- }
-%}
- void clr_loadcallback() {
- if ($self->loadcallback == loadcallback) {
- SvREFCNT_dec($self->loadcallbackdata);
- pool_setloadcallback($self, 0, 0);
- }
- }
- void set_loadcallback(SV *callable) {
- Pool_clr_loadcallback($self);
- if (callable) {
- SvREFCNT_inc(callable);
- pool_setloadcallback($self, loadcallback, callable);
- }
- }
-#elif defined(SWIGRUBY)
-%{
- SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
- XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
- VALUE callable = (VALUE)d;
- VALUE rd = SWIG_NewPointerObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, SWIG_POINTER_OWN | 0);
- VALUE res = rb_funcall(callable, rb_intern("call"), 1, rd);
- return res == Qtrue;
- }
- SWIGINTERN void mark_Pool(void *ptr) {
- Pool *pool = ptr;
- if (pool->loadcallback == loadcallback && pool->loadcallbackdata) {
- VALUE callable = (VALUE)pool->loadcallbackdata;
- rb_gc_mark(callable);
- }
- }
-%}
- void clr_loadcallback() {
- pool_setloadcallback($self, 0, 0);
- }
- %typemap(in, numinputs=0) VALUE callable {
- $1 = rb_block_given_p() ? rb_block_proc() : 0;
- }
- void set_loadcallback(VALUE callable) {
- pool_setloadcallback($self, callable ? loadcallback : 0, (void *)callable);
- }
-#elif defined(SWIGTCL)
- %{
- typedef struct {
- Tcl_Interp *interp;
- Tcl_Obj *obj;
- } tcl_callback_t;
- SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
- tcl_callback_t *callback_var = (tcl_callback_t *)d;
- Tcl_Interp *interp = callback_var->interp;
- XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
- int result, ecode = 0, vresult = 0;
- Tcl_Obj *objvx[2];
- objvx[0] = callback_var->obj;
- objvx[1] = SWIG_NewInstanceObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, 0);
- Tcl_IncrRefCount(objvx[1]);
- result = Tcl_EvalObjv(interp, sizeof(objvx)/sizeof(*objvx), objvx, TCL_EVAL_GLOBAL);
- Tcl_DecrRefCount(objvx[1]);
- if (result != TCL_OK)
- return 0; /* exception */
- ecode = SWIG_AsVal_int(interp, Tcl_GetObjResult(interp), &vresult);
- return SWIG_IsOK(ecode) ? vresult : 0;
- }
- %}
- void clr_loadcallback() {
- if ($self->loadcallback == loadcallback) {
- tcl_callback_t *callback_var = $self->loadcallbackdata;
- Tcl_DecrRefCount(callback_var->obj);
- solv_free(callback_var);
- pool_setloadcallback($self, 0, 0);
- }
- }
- void set_loadcallback(Tcl_Obj *callable, Tcl_Interp *interp) {
- Pool_clr_loadcallback($self);
- if (callable) {
- tcl_callback_t *callback_var = solv_malloc(sizeof(tcl_callback_t));
- Tcl_IncrRefCount(callable);
- callback_var->interp = interp;
- callback_var->obj = callable;
- pool_setloadcallback($self, loadcallback, callback_var);
- }
- }
-#else
-#warning loadcallback not implemented for this language
-#endif
-
- ~Pool() {
- Pool *pool = $self;
- Id repoid;
- Repo *repo;
- FOR_REPOS(repoid, repo)
- appdata_clr_helper(&repo->appdata);
- Pool_clr_loadcallback(pool);
- appdata_clr_helper(&pool->appdata);
- pool_free(pool);
- }
- disown_helper free() {
- Pool *pool = $self;
- Id repoid;
- Repo *repo;
- FOR_REPOS(repoid, repo)
- appdata_clr_helper(&repo->appdata);
- Pool_clr_loadcallback(pool);
- appdata_clr_helper(&pool->appdata);
- pool_free(pool);
- return 0;
- }
- disown_helper disown() {
- return 0;
- }
- AppObjectPtr appdata;
- %{
- SWIGINTERN void Pool_appdata_set(Pool *pool, AppObjectPtr appdata) {
- appdata_set_helper(&pool->appdata, appdata);
- }
- SWIGINTERN AppObjectPtr Pool_appdata_get(Pool *pool) {
- return appdata_get_helper(pool->appdata);
- }
- %}
- void appdata_disown() {
- appdata_disown_helper($self->appdata);
- }
-
- Id str2id(const char *str, bool create=1) {
- return pool_str2id($self, str, create);
- }
- %newobject Dep;
- Dep *Dep(const char *str, bool create=1) {
- Id id = pool_str2id($self, str, create);
- return new_Dep($self, id);
- }
- const char *id2str(Id id) {
- return pool_id2str($self, id);
- }
- const char *dep2str(Id id) {
- return pool_dep2str($self, id);
- }
- Id rel2id(Id name, Id evr, int flags, bool create=1) {
- return pool_rel2id($self, name, evr, flags, create);
- }
- Id id2langid(Id id, const char *lang, bool create=1) {
- return pool_id2langid($self, id, lang, create);
- }
- void setarch(const char *arch = 0) {
- struct utsname un;
- if (!arch) {
- if (uname(&un)) {
- perror("uname");
- return;
- }
- arch = un.machine;
- }
- pool_setarch($self, arch);
- }
- Repo *add_repo(const char *name) {
- return repo_create($self, name);
- }
- const char *lookup_str(Id entry, Id keyname) {
- return pool_lookup_str($self, entry, keyname);
- }
- Id lookup_id(Id entry, Id keyname) {
- return pool_lookup_id($self, entry, keyname);
- }
- unsigned long long lookup_num(Id entry, Id keyname, unsigned long long notfound = 0) {
- return pool_lookup_num($self, entry, keyname, notfound);
- }
- bool lookup_void(Id entry, Id keyname) {
- return pool_lookup_void($self, entry, keyname);
- }
- %newobject lookup_checksum;
- Chksum *lookup_checksum(Id entry, Id keyname) {
- Id type = 0;
- const unsigned char *b = pool_lookup_bin_checksum($self, entry, keyname, &type);
- return solv_chksum_create_from_bin(type, b);
- }
-
- %newobject Dataiterator;
- Dataiterator *Dataiterator(Id key, const char *match = 0, int flags = 0) {
- return new_Dataiterator($self, 0, 0, key, match, flags);
- }
- %newobject Dataiterator_solvid;
- Dataiterator *Dataiterator_solvid(Id p, Id key, const char *match = 0, int flags = 0) {
- return new_Dataiterator($self, 0, p, key, match, flags);
- }
- const char *solvid2str(Id solvid) {
- return pool_solvid2str($self, solvid);
- }
- void addfileprovides() {
- pool_addfileprovides($self);
- }
- Queue addfileprovides_queue() {
- Queue r;
- queue_init(&r);
- pool_addfileprovides_queue($self, &r, 0);
- return r;
- }
- void createwhatprovides() {
- pool_createwhatprovides($self);
- }
-
- %newobject id2solvable;
- XSolvable *id2solvable(Id id) {
- return new_XSolvable($self, id);
- }
- %newobject solvables;
- Pool_solvable_iterator * const solvables;
- %{
- SWIGINTERN Pool_solvable_iterator * Pool_solvables_get(Pool *pool) {
- return new_Pool_solvable_iterator(pool);
- }
- %}
- %newobject solvables_iter;
- Pool_solvable_iterator * solvables_iter() {
- return new_Pool_solvable_iterator($self);
- }
-
- Repo *id2repo(Id id) {
- if (id < 1 || id >= $self->nrepos)
- return 0;
- return pool_id2repo($self, id);
- }
-
- %newobject repos;
- Pool_repo_iterator * const repos;
- %{
- SWIGINTERN Pool_repo_iterator * Pool_repos_get(Pool *pool) {
- return new_Pool_repo_iterator(pool);
- }
- %}
- %newobject repos_iter;
- Pool_repo_iterator * repos_iter() {
- return new_Pool_repo_iterator($self);
- }
-
- Repo *installed;
- const char * const errstr;
- %{
- SWIGINTERN void Pool_installed_set(Pool *pool, Repo *installed) {
- pool_set_installed(pool, installed);
- }
- SWIGINTERN Repo *Pool_installed_get(Pool *pool) {
- return pool->installed;
- }
- SWIGINTERN const char *Pool_errstr_get(Pool *pool) {
- return pool_errstr(pool);
- }
- %}
-
- Queue matchprovidingids(const char *match, int flags) {
- Pool *pool = $self;
- Queue q;
- Id id;
- queue_init(&q);
- if (!flags) {
- for (id = 1; id < pool->ss.nstrings; id++)
- if (pool->whatprovides[id])
- queue_push(&q, id);
- } else {
- Datamatcher ma;
- if (!datamatcher_init(&ma, match, flags)) {
- for (id = 1; id < pool->ss.nstrings; id++)
- if (pool->whatprovides[id] && datamatcher_match(&ma, pool_id2str(pool, id)))
- queue_push(&q, id);
- datamatcher_free(&ma);
- }
- }
- return q;
- }
-
- %newobject Job;
- Job *Job(int how, Id what) {
- return new_Job($self, how, what);
- }
-
- %typemap(out) Queue whatprovides Queue2Array(XSolvable *, 1, new_XSolvable(arg1, id));
- %newobject whatprovides;
- Queue whatprovides(DepId dep) {
- Pool *pool = $self;
- Queue q;
- Id p, pp;
- queue_init(&q);
- FOR_PROVIDES(p, pp, dep)
- queue_push(&q, p);
- return q;
- }
-
- Id towhatprovides(Queue q) {
- return pool_queuetowhatprovides($self, &q);
- }
-
- %typemap(out) Queue whatmatchesdep Queue2Array(XSolvable *, 1, new_XSolvable(arg1, id));
- %newobject whatmatchesdep;
- Queue whatmatchesdep(Id keyname, DepId dep, Id marker = -1) {
- Queue q;
- queue_init(&q);
- pool_whatmatchesdep($self, keyname, dep, &q, marker);
- return q;
- }
-
-#ifdef SWIGRUBY
- %rename("isknownarch?") isknownarch;
-#endif
- bool isknownarch(DepId id) {
- Pool *pool = $self;
- if (!id || id == ID_EMPTY)
- return 0;
- if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
- return 1;
- if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
- return 0;
- return 1;
- }
-
- %newobject Solver;
- Solver *Solver() {
- return solver_create($self);
- }
-
- %newobject Selection;
- Selection *Selection() {
- return new_Selection($self);
- }
- %newobject Selection_all;
- Selection *Selection_all(int setflags=0) {
- Selection *sel = new_Selection($self);
- queue_push2(&sel->q, SOLVER_SOLVABLE_ALL | setflags, 0);
- return sel;
- }
- %newobject select;
- Selection *select(const char *name, int flags) {
- Selection *sel = new_Selection($self);
- sel->flags = selection_make($self, &sel->q, name, flags);
- return sel;
- }
-
- void setpooljobs_helper(Queue jobs) {
- queue_free(&$self->pooljobs);
- queue_init_clone(&$self->pooljobs, &jobs);
- }
- %typemap(out) Queue getpooljobs Queue2Array(Job *, 2, new_Job(arg1, id, idp[1]));
- %newobject getpooljobs;
- Queue getpooljobs() {
- Queue q;
- queue_init_clone(&q, &$self->pooljobs);
- return q;
- }
-
-#if defined(SWIGPYTHON)
- %pythoncode {
- def setpooljobs(self, jobs):
- j = []
- for job in jobs: j += [job.how, job.what]
- self.setpooljobs_helper(j)
- }
-#endif
-#if defined(SWIGPERL)
- %perlcode {
- sub solv::Solver::setpooljobs {
- my ($self, $jobs) = @_;
- my @j = map {($_->{'how'}, $_->{'what'})} @$jobs;
- return $self->setpooljobs_helper(\@j);
- }
- }
-#endif
-#if defined(SWIGRUBY)
-%init %{
-rb_eval_string(
- "class Solv::Pool\n"
- " def setpooljobs(jobs)\n"
- " jl = []\n"
- " jobs.each do |j| ; jl << j.how << j.what ; end\n"
- " setpooljobs_helper(jl)\n"
- " end\n"
- "end\n"
- );
-%}
-#endif
-}
-
-%extend Repo {
- static const int REPO_REUSE_REPODATA = REPO_REUSE_REPODATA;
- static const int REPO_NO_INTERNALIZE = REPO_NO_INTERNALIZE;
- static const int REPO_LOCALPOOL = REPO_LOCALPOOL;
- static const int REPO_USE_LOADING = REPO_USE_LOADING;
- static const int REPO_EXTEND_SOLVABLES = REPO_EXTEND_SOLVABLES;
- static const int REPO_USE_ROOTDIR = REPO_USE_ROOTDIR;
- static const int REPO_NO_LOCATION = REPO_NO_LOCATION;
- static const int SOLV_ADD_NO_STUBS = SOLV_ADD_NO_STUBS; /* repo_solv */
-#ifdef ENABLE_SUSEREPO
- static const int SUSETAGS_RECORD_SHARES = SUSETAGS_RECORD_SHARES; /* repo_susetags */
-#endif
-
- void free(bool reuseids = 0) {
- appdata_clr_helper(&$self->appdata);
- repo_free($self, reuseids);
- }
- void empty(bool reuseids = 0) {
- repo_empty($self, reuseids);
- }
-#ifdef SWIGRUBY
- %rename("isempty?") isempty;
-#endif
- bool isempty() {
- return !$self->nsolvables;
- }
-
- AppObjectPtr appdata;
- %{
- SWIGINTERN void Repo_appdata_set(Repo *repo, AppObjectPtr appdata) {
- appdata_set_helper(&repo->appdata, appdata);
- }
- SWIGINTERN AppObjectPtr Repo_appdata_get(Repo *repo) {
- return appdata_get_helper(repo->appdata);
- }
- %}
-
- bool add_solv(const char *name, int flags = 0) {
- FILE *fp = fopen(name, "r");
- int r;
- if (!fp)
- return 0;
- r = repo_add_solv($self, fp, flags);
- fclose(fp);
- return r == 0;
- }
- bool add_solv(FILE *fp, int flags = 0) {
- return repo_add_solv($self, fp, flags) == 0;
- }
-
- %newobject add_solvable;
- XSolvable *add_solvable() {
- Id solvid = repo_add_solvable($self);
- return new_XSolvable($self->pool, solvid);
- }
-
-#ifdef ENABLE_RPMDB
- bool add_rpmdb(int flags = 0) {
- return repo_add_rpmdb($self, 0, flags) == 0;
- }
- bool add_rpmdb_reffp(FILE *reffp, int flags = 0) {
- return repo_add_rpmdb_reffp($self, reffp, flags) == 0;
- }
- %newobject add_rpm;
- XSolvable *add_rpm(const char *name, int flags = 0) {
- return new_XSolvable($self->pool, repo_add_rpm($self, name, flags));
- }
-#endif
-#ifdef ENABLE_PUBKEY
-#ifdef ENABLE_RPMDB
- bool add_rpmdb_pubkeys(int flags = 0) {
- return repo_add_rpmdb_pubkeys($self, flags) == 0;
- }
-#endif
- %newobject add_pubkey;
- XSolvable *add_pubkey(const char *keyfile, int flags = 0) {
- return new_XSolvable($self->pool, repo_add_pubkey($self, keyfile, flags));
- }
- bool add_keyring(FILE *fp, int flags = 0) {
- return repo_add_keyring($self, fp, flags);
- }
- bool add_keydir(const char *keydir, const char *suffix, int flags = 0) {
- return repo_add_keydir($self, keydir, suffix, flags);
- }
-#endif
-#ifdef ENABLE_RPMMD
- bool add_rpmmd(FILE *fp, const char *language, int flags = 0) {
- return repo_add_rpmmd($self, fp, language, flags) == 0;
- }
- bool add_repomdxml(FILE *fp, int flags = 0) {
- return repo_add_repomdxml($self, fp, flags) == 0;
- }
- bool add_updateinfoxml(FILE *fp, int flags = 0) {
- return repo_add_updateinfoxml($self, fp, flags) == 0;
- }
- bool add_deltainfoxml(FILE *fp, int flags = 0) {
- return repo_add_deltainfoxml($self, fp, flags) == 0;
- }
-#endif
-#ifdef ENABLE_DEBIAN
- bool add_debdb(int flags = 0) {
- return repo_add_debdb($self, flags) == 0;
- }
- bool add_debpackages(FILE *fp, int flags = 0) {
- return repo_add_debpackages($self, fp, flags) == 0;
- }
- %newobject add_deb;
- XSolvable *add_deb(const char *name, int flags = 0) {
- return new_XSolvable($self->pool, repo_add_deb($self, name, flags));
- }
-#endif
-#ifdef ENABLE_SUSEREPO
- bool add_susetags(FILE *fp, Id defvendor, const char *language, int flags = 0) {
- return repo_add_susetags($self, fp, defvendor, language, flags) == 0;
- }
- bool add_content(FILE *fp, int flags = 0) {
- return repo_add_content($self, fp, flags) == 0;
- }
- bool add_products(const char *proddir, int flags = 0) {
- return repo_add_products($self, proddir, flags) == 0;
- }
-#endif
-#ifdef ENABLE_MDKREPO
- bool add_mdk(FILE *fp, int flags = 0) {
- return repo_add_mdk($self, fp, flags) == 0;
- }
- bool add_mdk_info(FILE *fp, int flags = 0) {
- return repo_add_mdk_info($self, fp, flags) == 0;
- }
-#endif
-#ifdef ENABLE_ARCHREPO
- bool add_arch_repo(FILE *fp, int flags = 0) {
- return repo_add_arch_repo($self, fp, flags) == 0;
- }
- bool add_arch_local(const char *dir, int flags = 0) {
- return repo_add_arch_local($self, dir, flags) == 0;
- }
- %newobject add_arch_pkg;
- XSolvable *add_arch_pkg(const char *name, int flags = 0) {
- return new_XSolvable($self->pool, repo_add_arch_pkg($self, name, flags));
- }
-#endif
-#ifdef SUSE
- bool add_autopattern(int flags = 0) {
- return repo_add_autopattern($self, flags) == 0;
- }
-#endif
- void internalize() {
- repo_internalize($self);
- }
- bool write(FILE *fp) {
- return repo_write($self, fp) == 0;
- }
- /* HACK, remove if no longer needed! */
- bool write_first_repodata(FILE *fp) {
- int oldnrepodata = $self->nrepodata;
- int res;
- $self->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata;
- res = repo_write($self, fp);
- $self->nrepodata = oldnrepodata;
- return res == 0;
- }
-
- %newobject Dataiterator;
- Dataiterator *Dataiterator(Id key, const char *match = 0, int flags = 0) {
- return new_Dataiterator($self->pool, $self, 0, key, match, flags);
- }
- %newobject Dataiterator_meta;
- Dataiterator *Dataiterator_meta(Id key, const char *match = 0, int flags = 0) {
- return new_Dataiterator($self->pool, $self, SOLVID_META, key, match, flags);
- }
-
- Id const id;
- %{
- SWIGINTERN Id Repo_id_get(Repo *repo) {
- return repo->repoid;
- }
- %}
- %newobject solvables;
- Repo_solvable_iterator * const solvables;
- %{
- SWIGINTERN Repo_solvable_iterator * Repo_solvables_get(Repo *repo) {
- return new_Repo_solvable_iterator(repo);
- }
- %}
- %newobject meta;
- Datapos * const meta;
- %{
- SWIGINTERN Datapos * Repo_meta_get(Repo *repo) {
- Datapos *pos = solv_calloc(1, sizeof(*pos));
- pos->solvid = SOLVID_META;
- pos->repo = repo;
- return pos;
- }
- %}
-
- %newobject solvables_iter;
- Repo_solvable_iterator *solvables_iter() {
- return new_Repo_solvable_iterator($self);
- }
-
- %newobject add_repodata;
- XRepodata *add_repodata(int flags = 0) {
- Repodata *rd = repo_add_repodata($self, flags);
- return new_XRepodata($self, rd->repodataid);
- }
-
- void create_stubs() {
- Repodata *data;
- if (!$self->nrepodata)
- return;
- data = repo_id2repodata($self, $self->nrepodata - 1);
- if (data->state != REPODATA_STUB)
- (void)repodata_create_stubs(data);
- }
-#ifdef SWIGRUBY
- %rename("iscontiguous?") iscontiguous;
-#endif
- bool iscontiguous() {
- int i;
- for (i = $self->start; i < $self->end; i++)
- if ($self->pool->solvables[i].repo != $self)
- return 0;
- return 1;
- }
- %newobject first_repodata;
- XRepodata *first_repodata() {
- Repodata *data;
- int i;
- if ($self->nrepodata < 2)
- return 0;
- /* make sure all repodatas but the first are extensions */
- data = repo_id2repodata($self, 1);
- if (data->loadcallback)
- return 0;
- for (i = 2; i < $self->nrepodata; i++)
- {
- data = repo_id2repodata($self, i);
- if (!data->loadcallback)
- return 0; /* oops, not an extension */
- }
- return new_XRepodata($self, 1);
- }
-
- %newobject Selection;
- Selection *Selection(int setflags=0) {
- Selection *sel = new_Selection($self->pool);
- setflags |= SOLVER_SETREPO;
- queue_push2(&sel->q, SOLVER_SOLVABLE_REPO | setflags, $self->repoid);
- return sel;
- }
-
-#ifdef ENABLE_PUBKEY
- %newobject find_pubkey;
- XSolvable *find_pubkey(const char *keyid) {
- return new_XSolvable($self->pool, repo_find_pubkey($self, keyid));
- }
-#endif
-
-#if defined(SWIGTCL)
- %rename("==") __eq__;
-#endif
- bool __eq__(Repo *repo) {
- return $self == repo;
- }
-#if defined(SWIGTCL)
- %rename("!=") __ne__;
-#endif
- bool __ne__(Repo *repo) {
- return $self != repo;
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("str") __str__;
-#endif
- %newobject __str__;
- const char *__str__() {
- char buf[20];
- if ($self->name)
- return solv_strdup($self->name);
- sprintf(buf, "Repo#%d", $self->repoid);
- return solv_strdup(buf);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("repr") __repr__;
-#endif
- %newobject __repr__;
- const char *__repr__() {
- char buf[20];
- if ($self->name)
- {
- sprintf(buf, "<Repo #%d ", $self->repoid);
- return solv_dupjoin(buf, $self->name, ">");
- }
- sprintf(buf, "<Repo #%d>", $self->repoid);
- return solv_strdup(buf);
- }
-}
-
-%extend Dataiterator {
- static const int SEARCH_STRING = SEARCH_STRING;
- static const int SEARCH_STRINGSTART = SEARCH_STRINGSTART;
- static const int SEARCH_STRINGEND = SEARCH_STRINGEND;
- static const int SEARCH_SUBSTRING = SEARCH_SUBSTRING;
- static const int SEARCH_GLOB = SEARCH_GLOB;
- static const int SEARCH_REGEX = SEARCH_REGEX;
- static const int SEARCH_NOCASE = SEARCH_NOCASE;
- static const int SEARCH_FILES = SEARCH_FILES;
- static const int SEARCH_COMPLETE_FILELIST = SEARCH_COMPLETE_FILELIST;
- static const int SEARCH_CHECKSUMS = SEARCH_CHECKSUMS;
-
- Dataiterator(Pool *pool, Repo *repo, Id p, Id key, const char *match, int flags) {
- Dataiterator *di = solv_calloc(1, sizeof(*di));
- dataiterator_init(di, pool, repo, p, key, match, flags);
- return di;
- }
- ~Dataiterator() {
- dataiterator_free($self);
- solv_free($self);
- }
-#if defined(SWIGPYTHON)
- %pythoncode {
- def __iter__(self): return self
- }
-#ifndef PYTHON3
- %rename("next") __next__();
-#endif
- %exception __next__ {
- $action
- if (!result) {
- PyErr_SetString(PyExc_StopIteration,"no more matches");
- return NULL;
- }
- }
-#endif
-#ifdef SWIGPERL
- perliter(solv::Dataiterator)
-#endif
- %newobject __next__;
- Datamatch *__next__() {
- Dataiterator *ndi;
- if (!dataiterator_step($self)) {
- return 0;
- }
- ndi = solv_calloc(1, sizeof(*ndi));
- dataiterator_init_clone(ndi, $self);
- dataiterator_strdup(ndi);
- return ndi;
- }
-#ifdef SWIGRUBY
- void each() {
- Datamatch *d;
- while ((d = Dataiterator___next__($self)) != 0) {
- rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(d), SWIGTYPE_p_Datamatch, SWIG_POINTER_OWN | 0));
- }
- }
-#endif
- void prepend_keyname(Id key) {
- dataiterator_prepend_keyname($self, key);
- }
- void skip_solvable() {
- dataiterator_skip_solvable($self);
- }
-}
-
-%extend Datapos {
- Id lookup_id(Id keyname) {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- Id r;
- pool->pos = *$self;
- r = pool_lookup_id(pool, SOLVID_POS, keyname);
- pool->pos = oldpos;
- return r;
- }
- const char *lookup_str(Id keyname) {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- const char *r;
- pool->pos = *$self;
- r = pool_lookup_str(pool, SOLVID_POS, keyname);
- pool->pos = oldpos;
- return r;
- }
- unsigned long long lookup_num(Id keyname, unsigned long long notfound = 0) {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- unsigned long long r;
- pool->pos = *$self;
- r = pool_lookup_num(pool, SOLVID_POS, keyname, notfound);
- pool->pos = oldpos;
- return r;
- }
- bool lookup_void(Id keyname) {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- int r;
- pool->pos = *$self;
- r = pool_lookup_void(pool, SOLVID_POS, keyname);
- pool->pos = oldpos;
- return r;
- }
- %newobject lookup_checksum;
- Chksum *lookup_checksum(Id keyname) {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- Id type = 0;
- const unsigned char *b;
- pool->pos = *$self;
- b = pool_lookup_bin_checksum(pool, SOLVID_POS, keyname, &type);
- pool->pos = oldpos;
- return solv_chksum_create_from_bin(type, b);
- }
- const char *lookup_deltaseq() {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- const char *seq;
- pool->pos = *$self;
- seq = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
- if (seq) {
- seq = pool_tmpjoin(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
- seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
- }
- pool->pos = oldpos;
- return seq;
- }
- const char *lookup_deltalocation(unsigned int *OUTPUT) {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- const char *loc;
- pool->pos = *$self;
- loc = pool_lookup_deltalocation(pool, SOLVID_POS, OUTPUT);
- pool->pos = oldpos;
- return loc;
- }
- Queue lookup_idarray(Id keyname) {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- Queue r;
- queue_init(&r);
- pool->pos = *$self;
- pool_lookup_idarray(pool, SOLVID_POS, keyname, &r);
- pool->pos = oldpos;
- return r;
- }
- %newobject Dataiterator;
- Dataiterator *Dataiterator(Id key, const char *match = 0, int flags = 0) {
- Pool *pool = $self->repo->pool;
- Datapos oldpos = pool->pos;
- Dataiterator *di;
- pool->pos = *$self;
- di = new_Dataiterator(pool, 0, SOLVID_POS, key, match, flags);
- pool->pos = oldpos;
- return di;
- }
-}
-
-%extend Datamatch {
- ~Datamatch() {
- dataiterator_free($self);
- solv_free($self);
- }
- %newobject solvable;
- XSolvable * const solvable;
- Id const key_id;
- const char * const key_idstr;
- Id const type_id;
- const char * const type_idstr;
- Id const id;
- const char * const idstr;
- const char * const str;
- BinaryBlob const binary;
- unsigned long long const num;
- unsigned int const num2;
- %{
- SWIGINTERN XSolvable *Datamatch_solvable_get(Dataiterator *di) {
- return new_XSolvable(di->pool, di->solvid);
- }
- SWIGINTERN Id Datamatch_key_id_get(Dataiterator *di) {
- return di->key->name;
- }
- SWIGINTERN const char *Datamatch_key_idstr_get(Dataiterator *di) {
- return pool_id2str(di->pool, di->key->name);
- }
- SWIGINTERN Id Datamatch_type_id_get(Dataiterator *di) {
- return di->key->type;
- }
- SWIGINTERN const char *Datamatch_type_idstr_get(Dataiterator *di) {
- return pool_id2str(di->pool, di->key->type);
- }
- SWIGINTERN Id Datamatch_id_get(Dataiterator *di) {
- return di->kv.id;
- }
- SWIGINTERN const char *Datamatch_idstr_get(Dataiterator *di) {
- if (di->data && (di->key->type == REPOKEY_TYPE_DIR || di->key->type == REPOKEY_TYPE_DIRSTRARRAY || di->key->type == REPOKEY_TYPE_DIRNUMNUMARRAY))
- return repodata_dir2str(di->data, di->kv.id, 0);
- if (di->data && di->data->localpool)
- return stringpool_id2str(&di->data->spool, di->kv.id);
- return pool_id2str(di->pool, di->kv.id);
- }
- SWIGINTERN const char * const Datamatch_str_get(Dataiterator *di) {
- return di->kv.str;
- }
- SWIGINTERN BinaryBlob Datamatch_binary_get(Dataiterator *di) {
- BinaryBlob bl;
- bl.data = 0;
- bl.len = 0;
- if (di->key->type == REPOKEY_TYPE_BINARY)
- {
- bl.data = di->kv.str;
- bl.len = di->kv.num;
- }
- else if ((bl.len = solv_chksum_len(di->key->type)) != 0)
- bl.data = di->kv.str;
- return bl;
- }
- SWIGINTERN unsigned long long Datamatch_num_get(Dataiterator *di) {
- if (di->key->type == REPOKEY_TYPE_NUM)
- return SOLV_KV_NUM64(&di->kv);
- return di->kv.num;
- }
- SWIGINTERN unsigned int Datamatch_num2_get(Dataiterator *di) {
- return di->kv.num2;
- }
- %}
- %newobject pos;
- Datapos *pos() {
- Pool *pool = $self->pool;
- Datapos *pos, oldpos = pool->pos;
- dataiterator_setpos($self);
- pos = solv_calloc(1, sizeof(*pos));
- *pos = pool->pos;
- pool->pos = oldpos;
- return pos;
- }
- %newobject parentpos;
- Datapos *parentpos() {
- Pool *pool = $self->pool;
- Datapos *pos, oldpos = pool->pos;
- dataiterator_setpos_parent($self);
- pos = solv_calloc(1, sizeof(*pos));
- *pos = pool->pos;
- pool->pos = oldpos;
- return pos;
- }
-#if defined(SWIGPERL)
- /* cannot use str here because swig reports a bogus conflict... */
- %rename("stringify") __str__;
- %perlcode {
- *solv::Datamatch::str = *solvc::Datamatch_stringify;
- }
-#endif
-#if defined(SWIGTCL)
- %rename("stringify") __str__;
-#endif
- const char *__str__() {
- KeyValue kv = $self->kv;
- const char *str = repodata_stringify($self->pool, $self->data, $self->key, &kv, SEARCH_FILES | SEARCH_CHECKSUMS);
- return str ? str : "";
- }
-}
-
-%extend Pool_solvable_iterator {
- Pool_solvable_iterator(Pool *pool) {
- Pool_solvable_iterator *s;
- s = solv_calloc(1, sizeof(*s));
- s->pool = pool;
- return s;
- }
-#if defined(SWIGPYTHON)
- %pythoncode {
- def __iter__(self): return self
- }
-#ifndef PYTHON3
- %rename("next") __next__();
-#endif
- %exception __next__ {
- $action
- if (!result) {
- PyErr_SetString(PyExc_StopIteration,"no more matches");
- return NULL;
- }
- }
-#endif
-#ifdef SWIGPERL
- perliter(solv::Pool_solvable_iterator)
-#endif
- %newobject __next__;
- XSolvable *__next__() {
- Pool *pool = $self->pool;
- if ($self->id >= pool->nsolvables)
- return 0;
- while (++$self->id < pool->nsolvables)
- if (pool->solvables[$self->id].repo)
- return new_XSolvable(pool, $self->id);
- return 0;
- }
-#ifdef SWIGRUBY
- void each() {
- XSolvable *n;
- while ((n = Pool_solvable_iterator___next__($self)) != 0) {
- rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(n), SWIGTYPE_p_XSolvable, SWIG_POINTER_OWN | 0));
- }
- }
-#endif
- %newobject __getitem__;
- XSolvable *__getitem__(Id key) {
- Pool *pool = $self->pool;
- if (key > 0 && key < pool->nsolvables && pool->solvables[key].repo)
- return new_XSolvable(pool, key);
- return 0;
- }
- int __len__() {
- return $self->pool->nsolvables;
- }
-}
-
-%extend Pool_repo_iterator {
- Pool_repo_iterator(Pool *pool) {
- Pool_repo_iterator *s;
- s = solv_calloc(1, sizeof(*s));
- s->pool = pool;
- return s;
- }
-#if defined(SWIGPYTHON)
- %pythoncode {
- def __iter__(self): return self
- }
-#ifndef PYTHON3
- %rename("next") __next__();
-#endif
- %exception __next__ {
- $action
- if (!result) {
- PyErr_SetString(PyExc_StopIteration,"no more matches");
- return NULL;
- }
- }
-#endif
-#ifdef SWIGPERL
- perliter(solv::Pool_repo_iterator)
-#endif
- %newobject __next__;
- Repo *__next__() {
- Pool *pool = $self->pool;
- if ($self->id >= pool->nrepos)
- return 0;
- while (++$self->id < pool->nrepos) {
- Repo *r = pool_id2repo(pool, $self->id);
- if (r)
- return r;
- }
- return 0;
- }
-#ifdef SWIGRUBY
- void each() {
- Repo *n;
- while ((n = Pool_repo_iterator___next__($self)) != 0) {
- rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(n), SWIGTYPE_p_Repo, SWIG_POINTER_OWN | 0));
- }
- }
-#endif
- Repo *__getitem__(Id key) {
- Pool *pool = $self->pool;
- if (key > 0 && key < pool->nrepos)
- return pool_id2repo(pool, key);
- return 0;
- }
- int __len__() {
- return $self->pool->nrepos;
- }
-}
-
-%extend Repo_solvable_iterator {
- Repo_solvable_iterator(Repo *repo) {
- Repo_solvable_iterator *s;
- s = solv_calloc(1, sizeof(*s));
- s->repo = repo;
- return s;
- }
-#if defined(SWIGPYTHON)
- %pythoncode {
- def __iter__(self): return self
- }
-#ifndef PYTHON3
- %rename("next") __next__();
-#endif
- %exception __next__ {
- $action
- if (!result) {
- PyErr_SetString(PyExc_StopIteration,"no more matches");
- return NULL;
- }
- }
-#endif
-#ifdef SWIGPERL
- perliter(solv::Repo_solvable_iterator)
-#endif
- %newobject __next__;
- XSolvable *__next__() {
- Repo *repo = $self->repo;
- Pool *pool = repo->pool;
- if (repo->start > 0 && $self->id < repo->start)
- $self->id = repo->start - 1;
- if ($self->id >= repo->end)
- return 0;
- while (++$self->id < repo->end)
- if (pool->solvables[$self->id].repo == repo)
- return new_XSolvable(pool, $self->id);
- return 0;
- }
-#ifdef SWIGRUBY
- void each() {
- XSolvable *n;
- while ((n = Repo_solvable_iterator___next__($self)) != 0) {
- rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(n), SWIGTYPE_p_XSolvable, SWIG_POINTER_OWN | 0));
- }
- }
-#endif
- %newobject __getitem__;
- XSolvable *__getitem__(Id key) {
- Repo *repo = $self->repo;
- Pool *pool = repo->pool;
- if (key > 0 && key < pool->nsolvables && pool->solvables[key].repo == repo)
- return new_XSolvable(pool, key);
- return 0;
- }
- int __len__() {
- return $self->repo->pool->nsolvables;
- }
-}
-
-%extend Dep {
- Dep(Pool *pool, Id id) {
- Dep *s;
- if (!id)
- return 0;
- s = solv_calloc(1, sizeof(*s));
- s->pool = pool;
- s->id = id;
- return s;
- }
- %newobject Rel;
- Dep *Rel(int flags, DepId evrid, bool create=1) {
- Id id = pool_rel2id($self->pool, $self->id, evrid, flags, create);
- if (!id)
- return 0;
- return new_Dep($self->pool, id);
- }
- %newobject Selection_name;
- Selection *Selection_name(int setflags=0) {
- Selection *sel = new_Selection($self->pool);
- if (ISRELDEP($self->id)) {
- Reldep *rd = GETRELDEP($self->pool, $self->id);
- if (rd->flags == REL_EQ) {
- setflags |= $self->pool->disttype == DISTTYPE_DEB || strchr(pool_id2str($self->pool, rd->evr), '-') != 0 ? SOLVER_SETEVR : SOLVER_SETEV;
- if (ISRELDEP(rd->name))
- rd = GETRELDEP($self->pool, rd->name);
- }
- if (rd->flags == REL_ARCH)
- setflags |= SOLVER_SETARCH;
- }
- queue_push2(&sel->q, SOLVER_SOLVABLE_NAME | setflags, $self->id);
- return sel;
- }
- %newobject Selection_provides;
- Selection *Selection_provides(int setflags=0) {
- Selection *sel = new_Selection($self->pool);
- if (ISRELDEP($self->id)) {
- Reldep *rd = GETRELDEP($self->pool, $self->id);
- if (rd->flags == REL_ARCH)
- setflags |= SOLVER_SETARCH;
- }
- queue_push2(&sel->q, SOLVER_SOLVABLE_PROVIDES | setflags, $self->id);
- return sel;
- }
- const char *str() {
- return pool_dep2str($self->pool, $self->id);
- }
-#if defined(SWIGTCL)
- %rename("==") __eq__;
-#endif
- bool __eq__(Dep *s) {
- return $self->pool == s->pool && $self->id == s->id;
- }
-#if defined(SWIGTCL)
- %rename("!=") __ne__;
-#endif
- bool __ne__(Dep *s) {
- return !Dep___eq__($self, s);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("str") __str__;
-#endif
- const char *__str__() {
- return pool_dep2str($self->pool, $self->id);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("repr") __repr__;
-#endif
- %newobject __repr__;
- const char *__repr__() {
- char buf[20];
- sprintf(buf, "<Id #%d ", $self->id);
- return solv_dupjoin(buf, pool_dep2str($self->pool, $self->id), ">");
- }
-}
-
-%extend XSolvable {
- XSolvable(Pool *pool, Id id) {
- XSolvable *s;
- if (!id || id >= pool->nsolvables)
- return 0;
- s = solv_calloc(1, sizeof(*s));
- s->pool = pool;
- s->id = id;
- return s;
- }
- const char *str() {
- return pool_solvid2str($self->pool, $self->id);
- }
- const char *lookup_str(Id keyname) {
- return pool_lookup_str($self->pool, $self->id, keyname);
- }
- Id lookup_id(Id keyname) {
- return pool_lookup_id($self->pool, $self->id, keyname);
- }
- unsigned long long lookup_num(Id keyname, unsigned long long notfound = 0) {
- return pool_lookup_num($self->pool, $self->id, keyname, notfound);
- }
- bool lookup_void(Id keyname) {
- return pool_lookup_void($self->pool, $self->id, keyname);
- }
- %newobject lookup_checksum;
- Chksum *lookup_checksum(Id keyname) {
- Id type = 0;
- const unsigned char *b = pool_lookup_bin_checksum($self->pool, $self->id, keyname, &type);
- return solv_chksum_create_from_bin(type, b);
- }
- Queue lookup_idarray(Id keyname, Id marker = -1) {
- Solvable *s = $self->pool->solvables + $self->id;
- Queue r;
- queue_init(&r);
- solvable_lookup_deparray(s, keyname, &r, marker);
- return r;
- }
- %typemap(out) Queue lookup_deparray Queue2Array(Dep *, 1, new_Dep(arg1->pool, id));
- %newobject lookup_deparray;
- Queue lookup_deparray(Id keyname, Id marker = -1) {
- Solvable *s = $self->pool->solvables + $self->id;
- Queue r;
- queue_init(&r);
- solvable_lookup_deparray(s, keyname, &r, marker);
- return r;
- }
- const char *lookup_location(unsigned int *OUTPUT) {
- return solvable_lookup_location($self->pool->solvables + $self->id, OUTPUT);
- }
- %newobject Dataiterator;
- Dataiterator *Dataiterator(Id key, const char *match = 0, int flags = 0) {
- return new_Dataiterator($self->pool, 0, $self->id, key, match, flags);
- }
-#ifdef SWIGRUBY
- %rename("installable?") installable;
-#endif
- bool installable() {
- return pool_installable($self->pool, pool_id2solvable($self->pool, $self->id));
- }
-#ifdef SWIGRUBY
- %rename("isinstalled?") isinstalled;
-#endif
- bool isinstalled() {
- Pool *pool = $self->pool;
- return pool->installed && pool_id2solvable(pool, $self->id)->repo == pool->installed;
- }
-
- const char *name;
- %{
- SWIGINTERN void XSolvable_name_set(XSolvable *xs, const char *name) {
- Pool *pool = xs->pool;
- pool->solvables[xs->id].name = pool_str2id(pool, name, 1);
- }
- SWIGINTERN const char *XSolvable_name_get(XSolvable *xs) {
- Pool *pool = xs->pool;
- return pool_id2str(pool, pool->solvables[xs->id].name);
- }
- %}
- Id nameid;
- %{
- SWIGINTERN void XSolvable_nameid_set(XSolvable *xs, Id nameid) {
- xs->pool->solvables[xs->id].name = nameid;
- }
- SWIGINTERN Id XSolvable_nameid_get(XSolvable *xs) {
- return xs->pool->solvables[xs->id].name;
- }
- %}
- const char *evr;
- %{
- SWIGINTERN void XSolvable_evr_set(XSolvable *xs, const char *evr) {
- Pool *pool = xs->pool;
- pool->solvables[xs->id].evr = pool_str2id(pool, evr, 1);
- }
- SWIGINTERN const char *XSolvable_evr_get(XSolvable *xs) {
- Pool *pool = xs->pool;
- return pool_id2str(pool, pool->solvables[xs->id].evr);
- }
- %}
- Id evrid;
- %{
- SWIGINTERN void XSolvable_evrid_set(XSolvable *xs, Id evrid) {
- xs->pool->solvables[xs->id].evr = evrid;
- }
- SWIGINTERN Id XSolvable_evrid_get(XSolvable *xs) {
- return xs->pool->solvables[xs->id].evr;
- }
- %}
- const char *arch;
- %{
- SWIGINTERN void XSolvable_arch_set(XSolvable *xs, const char *arch) {
- Pool *pool = xs->pool;
- pool->solvables[xs->id].arch = pool_str2id(pool, arch, 1);
- }
- SWIGINTERN const char *XSolvable_arch_get(XSolvable *xs) {
- Pool *pool = xs->pool;
- return pool_id2str(pool, pool->solvables[xs->id].arch);
- }
- %}
- Id archid;
- %{
- SWIGINTERN void XSolvable_archid_set(XSolvable *xs, Id archid) {
- xs->pool->solvables[xs->id].arch = archid;
- }
- SWIGINTERN Id XSolvable_archid_get(XSolvable *xs) {
- return xs->pool->solvables[xs->id].arch;
- }
- %}
- const char *vendor;
- %{
- SWIGINTERN void XSolvable_vendor_set(XSolvable *xs, const char *vendor) {
- Pool *pool = xs->pool;
- pool->solvables[xs->id].vendor = pool_str2id(pool, vendor, 1);
- }
- SWIGINTERN const char *XSolvable_vendor_get(XSolvable *xs) {
- Pool *pool = xs->pool;
- return pool_id2str(pool, pool->solvables[xs->id].vendor);
- }
- %}
- Id vendorid;
- %{
- SWIGINTERN void XSolvable_vendorid_set(XSolvable *xs, Id vendorid) {
- xs->pool->solvables[xs->id].vendor = vendorid;
- }
- SWIGINTERN Id XSolvable_vendorid_get(XSolvable *xs) {
- return xs->pool->solvables[xs->id].vendor;
- }
- %}
- Repo * const repo;
- %{
- SWIGINTERN Repo *XSolvable_repo_get(XSolvable *xs) {
- return xs->pool->solvables[xs->id].repo;
- }
- %}
-
- /* old interface, please use the generic add_deparray instead */
- void add_provides(DepId id, Id marker = -1) {
- Solvable *s = $self->pool->solvables + $self->id;
- marker = solv_depmarker(SOLVABLE_PROVIDES, marker);
- s->provides = repo_addid_dep(s->repo, s->provides, id, marker);
- }
- void add_obsoletes(DepId id) {
- Solvable *s = $self->pool->solvables + $self->id;
- s->obsoletes = repo_addid_dep(s->repo, s->obsoletes, id, 0);
- }
- void add_conflicts(DepId id) {
- Solvable *s = $self->pool->solvables + $self->id;
- s->conflicts = repo_addid_dep(s->repo, s->conflicts, id, 0);
- }
- void add_requires(DepId id, Id marker = -1) {
- Solvable *s = $self->pool->solvables + $self->id;
- marker = solv_depmarker(SOLVABLE_REQUIRES, marker);
- s->requires = repo_addid_dep(s->repo, s->requires, id, marker);
- }
- void add_recommends(DepId id) {
- Solvable *s = $self->pool->solvables + $self->id;
- s->recommends = repo_addid_dep(s->repo, s->recommends, id, 0);
- }
- void add_suggests(DepId id) {
- Solvable *s = $self->pool->solvables + $self->id;
- s->suggests = repo_addid_dep(s->repo, s->suggests, id, 0);
- }
- void add_supplements(DepId id) {
- Solvable *s = $self->pool->solvables + $self->id;
- s->supplements = repo_addid_dep(s->repo, s->supplements, id, 0);
- }
- void add_enhances(DepId id) {
- Solvable *s = $self->pool->solvables + $self->id;
- s->enhances = repo_addid_dep(s->repo, s->enhances, id, 0);
- }
-
- void unset(Id keyname) {
- Solvable *s = $self->pool->solvables + $self->id;
- repo_unset(s->repo, $self->id, keyname);
- }
-
- void add_deparray(Id keyname, DepId id, Id marker = -1) {
- Solvable *s = $self->pool->solvables + $self->id;
- solvable_add_deparray(s, keyname, id, marker);
- }
-
- %newobject Selection;
- Selection *Selection(int setflags=0) {
- Selection *sel = new_Selection($self->pool);
- queue_push2(&sel->q, SOLVER_SOLVABLE | setflags, $self->id);
- return sel;
- }
-
-#ifdef SWIGRUBY
- %rename("identical?") identical;
-#endif
- bool identical(XSolvable *s2) {
- return solvable_identical($self->pool->solvables + $self->id, s2->pool->solvables + s2->id);
- }
- int evrcmp(XSolvable *s2) {
- return pool_evrcmp($self->pool, $self->pool->solvables[$self->id].evr, s2->pool->solvables[s2->id].evr, EVRCMP_COMPARE);
- }
-
-#if defined(SWIGTCL)
- %rename("==") __eq__;
-#endif
- bool __eq__(XSolvable *s) {
- return $self->pool == s->pool && $self->id == s->id;
- }
-#if defined(SWIGTCL)
- %rename("!=") __ne__;
-#endif
- bool __ne__(XSolvable *s) {
- return !XSolvable___eq__($self, s);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("str") __str__;
-#endif
- const char *__str__() {
- return pool_solvid2str($self->pool, $self->id);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("repr") __repr__;
-#endif
- %newobject __repr__;
- const char *__repr__() {
- char buf[20];
- sprintf(buf, "<Solvable #%d ", $self->id);
- return solv_dupjoin(buf, pool_solvid2str($self->pool, $self->id), ">");
- }
-}
-
-%extend Problem {
- Problem(Solver *solv, Id id) {
- Problem *p;
- p = solv_calloc(1, sizeof(*p));
- p->solv = solv;
- p->id = id;
- return p;
- }
- %newobject findproblemrule;
- XRule *findproblemrule() {
- Id r = solver_findproblemrule($self->solv, $self->id);
- return new_XRule($self->solv, r);
- }
- %newobject findallproblemrules;
- %typemap(out) Queue findallproblemrules Queue2Array(XRule *, 1, new_XRule(arg1->solv, id));
- Queue findallproblemrules(int unfiltered=0) {
- Solver *solv = $self->solv;
- Id probr;
- int i, j;
- Queue q;
- queue_init(&q);
- solver_findallproblemrules(solv, $self->id, &q);
- if (!unfiltered)
- {
- for (i = j = 0; i < q.count; i++)
- {
- SolverRuleinfo rclass;
- probr = q.elements[i];
- rclass = solver_ruleclass(solv, probr);
- if (rclass == SOLVER_RULE_UPDATE || rclass == SOLVER_RULE_JOB)
- continue;
- q.elements[j++] = probr;
- }
- if (j)
- queue_truncate(&q, j);
- }
- return q;
- }
- int solution_count() {
- return solver_solution_count($self->solv, $self->id);
- }
- %typemap(out) Queue solutions Queue2Array(Solution *, 1, new_Solution(arg1, id));
- %newobject solutions;
- Queue solutions() {
- Queue q;
- int i, cnt;
- queue_init(&q);
- cnt = solver_solution_count($self->solv, $self->id);
- for (i = 1; i <= cnt; i++)
- queue_push(&q, i);
- return q;
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("str") __str__;
-#endif
- const char *__str__() {
- return solver_problem2str($self->solv, $self->id);
- }
-}
-
-%extend Solution {
- Solution(Problem *p, Id id) {
- Solution *s;
- s = solv_calloc(1, sizeof(*s));
- s->solv = p->solv;
- s->problemid = p->id;
- s->id = id;
- return s;
- }
- int element_count() {
- return solver_solutionelement_count($self->solv, $self->problemid, $self->id);
- }
-
- %typemap(out) Queue elements Queue2Array(Solutionelement *, 4, new_Solutionelement(arg1->solv, arg1->problemid, arg1->id, id, idp[1], idp[2], idp[3]));
- %newobject elements;
- Queue elements(bool expandreplaces=0) {
- Queue q;
- int i, cnt;
- queue_init(&q);
- cnt = solver_solutionelement_count($self->solv, $self->problemid, $self->id);
- for (i = 1; i <= cnt; i++)
- {
- Id p, rp, type;
- solver_next_solutionelement($self->solv, $self->problemid, $self->id, i - 1, &p, &rp);
- if (p > 0) {
- type = rp ? SOLVER_SOLUTION_REPLACE : SOLVER_SOLUTION_ERASE;
- } else {
- type = p;
- p = rp;
- rp = 0;
- }
- if (type == SOLVER_SOLUTION_REPLACE && expandreplaces) {
- int illegal = policy_is_illegal(self->solv, self->solv->pool->solvables + p, self->solv->pool->solvables + rp, 0);
- if (illegal) {
- if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0) {
- queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_DOWNGRADE);
- queue_push2(&q, p, rp);
- }
- if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0) {
- queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_ARCHCHANGE);
- queue_push2(&q, p, rp);
- }
- if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0) {
- queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_VENDORCHANGE);
- queue_push2(&q, p, rp);
- }
- if ((illegal & POLICY_ILLEGAL_NAMECHANGE) != 0) {
- queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_NAMECHANGE);
- queue_push2(&q, p, rp);
- }
- continue;
- }
- }
- queue_push2(&q, i, type);
- queue_push2(&q, p, rp);
- }
- return q;
- }
-}
-
-%extend Solutionelement {
- Solutionelement(Solver *solv, Id problemid, Id solutionid, Id id, Id type, Id p, Id rp) {
- Solutionelement *e;
- e = solv_calloc(1, sizeof(*e));
- e->solv = solv;
- e->problemid = problemid;
- e->solutionid = id;
- e->id = id;
- e->type = type;
- e->p = p;
- e->rp = rp;
- return e;
- }
- const char *str() {
- Id p = $self->type;
- Id rp = $self->p;
- int illegal = 0;
- if (p == SOLVER_SOLUTION_ERASE)
- {
- p = rp;
- rp = 0;
- }
- else if (p == SOLVER_SOLUTION_REPLACE)
- {
- p = rp;
- rp = $self->rp;
- }
- else if (p == SOLVER_SOLUTION_REPLACE_DOWNGRADE)
- illegal = POLICY_ILLEGAL_DOWNGRADE;
- else if (p == SOLVER_SOLUTION_REPLACE_ARCHCHANGE)
- illegal = POLICY_ILLEGAL_ARCHCHANGE;
- else if (p == SOLVER_SOLUTION_REPLACE_VENDORCHANGE)
- illegal = POLICY_ILLEGAL_VENDORCHANGE;
- else if (p == SOLVER_SOLUTION_REPLACE_NAMECHANGE)
- illegal = POLICY_ILLEGAL_NAMECHANGE;
- if (illegal)
- return pool_tmpjoin($self->solv->pool, "allow ", policy_illegal2str($self->solv, illegal, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp), 0);
- return solver_solutionelement2str($self->solv, p, rp);
- }
- %typemap(out) Queue replaceelements Queue2Array(Solutionelement *, 1, new_Solutionelement(arg1->solv, arg1->problemid, arg1->solutionid, arg1->id, id, arg1->p, arg1->rp));
- %newobject replaceelements;
- Queue replaceelements() {
- Queue q;
- int illegal;
-
- queue_init(&q);
- if ($self->type != SOLVER_SOLUTION_REPLACE || $self->p <= 0 || $self->rp <= 0)
- illegal = 0;
- else
- illegal = policy_is_illegal($self->solv, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp, 0);
- if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0)
- queue_push(&q, SOLVER_SOLUTION_REPLACE_DOWNGRADE);
- if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0)
- queue_push(&q, SOLVER_SOLUTION_REPLACE_ARCHCHANGE);
- if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0)
- queue_push(&q, SOLVER_SOLUTION_REPLACE_VENDORCHANGE);
- if ((illegal & POLICY_ILLEGAL_NAMECHANGE) != 0)
- queue_push(&q, SOLVER_SOLUTION_REPLACE_NAMECHANGE);
- if (!q.count)
- queue_push(&q, $self->type);
- return q;
- }
- int illegalreplace() {
- if ($self->type != SOLVER_SOLUTION_REPLACE || $self->p <= 0 || $self->rp <= 0)
- return 0;
- return policy_is_illegal($self->solv, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp, 0);
- }
- %newobject solvable;
- XSolvable * const solvable;
- %newobject replacement;
- XSolvable * const replacement;
- int const jobidx;
- %{
- SWIGINTERN XSolvable *Solutionelement_solvable_get(Solutionelement *e) {
- return new_XSolvable(e->solv->pool, e->p);
- }
- SWIGINTERN XSolvable *Solutionelement_replacement_get(Solutionelement *e) {
- return new_XSolvable(e->solv->pool, e->rp);
- }
- SWIGINTERN int Solutionelement_jobidx_get(Solutionelement *e) {
- if (e->type != SOLVER_SOLUTION_JOB && e->type != SOLVER_SOLUTION_POOLJOB)
- return -1;
- return (e->p - 1) / 2;
- }
- %}
- %newobject Job;
- Job *Job() {
- Id extraflags = solver_solutionelement_extrajobflags($self->solv, $self->problemid, $self->solutionid);
- if ($self->type == SOLVER_SOLUTION_JOB || $self->type == SOLVER_SOLUTION_POOLJOB)
- return new_Job($self->solv->pool, SOLVER_NOOP, 0);
- if ($self->type == SOLVER_SOLUTION_INFARCH || $self->type == SOLVER_SOLUTION_DISTUPGRADE || $self->type == SOLVER_SOLUTION_BEST)
- return new_Job($self->solv->pool, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_NOTBYUSER|extraflags, $self->p);
- if ($self->type == SOLVER_SOLUTION_REPLACE || $self->type == SOLVER_SOLUTION_REPLACE_DOWNGRADE || $self->type == SOLVER_SOLUTION_REPLACE_ARCHCHANGE || $self->type == SOLVER_SOLUTION_REPLACE_VENDORCHANGE || $self->type == SOLVER_SOLUTION_REPLACE_NAMECHANGE)
- return new_Job($self->solv->pool, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_NOTBYUSER|extraflags, $self->rp);
- if ($self->type == SOLVER_SOLUTION_ERASE)
- return new_Job($self->solv->pool, SOLVER_ERASE|SOLVER_SOLVABLE|extraflags, $self->p);
- return 0;
- }
-}
-
-%extend Solver {
- static const int SOLVER_RULE_UNKNOWN = SOLVER_RULE_UNKNOWN;
- static const int SOLVER_RULE_PKG = SOLVER_RULE_PKG;
- static const int SOLVER_RULE_PKG_NOT_INSTALLABLE = SOLVER_RULE_PKG_NOT_INSTALLABLE;
- static const int SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP = SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP;
- static const int SOLVER_RULE_PKG_REQUIRES = SOLVER_RULE_PKG_REQUIRES;
- static const int SOLVER_RULE_PKG_SELF_CONFLICT = SOLVER_RULE_PKG_SELF_CONFLICT;
- static const int SOLVER_RULE_PKG_CONFLICTS = SOLVER_RULE_PKG_CONFLICTS;
- static const int SOLVER_RULE_PKG_SAME_NAME = SOLVER_RULE_PKG_SAME_NAME;
- static const int SOLVER_RULE_PKG_OBSOLETES = SOLVER_RULE_PKG_OBSOLETES;
- static const int SOLVER_RULE_PKG_IMPLICIT_OBSOLETES = SOLVER_RULE_PKG_IMPLICIT_OBSOLETES;
- static const int SOLVER_RULE_PKG_INSTALLED_OBSOLETES = SOLVER_RULE_PKG_INSTALLED_OBSOLETES;
- static const int SOLVER_RULE_UPDATE = SOLVER_RULE_UPDATE;
- static const int SOLVER_RULE_FEATURE = SOLVER_RULE_FEATURE;
- static const int SOLVER_RULE_JOB = SOLVER_RULE_JOB;
- static const int SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP = SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP;
- static const int SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM = SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM;
- static const int SOLVER_RULE_JOB_UNKNOWN_PACKAGE = SOLVER_RULE_JOB_UNKNOWN_PACKAGE;
- static const int SOLVER_RULE_JOB_UNSUPPORTED = SOLVER_RULE_JOB_UNSUPPORTED;
- static const int SOLVER_RULE_DISTUPGRADE = SOLVER_RULE_DISTUPGRADE;
- static const int SOLVER_RULE_INFARCH = SOLVER_RULE_INFARCH;
- static const int SOLVER_RULE_CHOICE = SOLVER_RULE_CHOICE;
- static const int SOLVER_RULE_LEARNT = SOLVER_RULE_LEARNT;
-
- static const int SOLVER_SOLUTION_JOB = SOLVER_SOLUTION_JOB;
- static const int SOLVER_SOLUTION_POOLJOB = SOLVER_SOLUTION_POOLJOB;
- static const int SOLVER_SOLUTION_INFARCH = SOLVER_SOLUTION_INFARCH;
- static const int SOLVER_SOLUTION_DISTUPGRADE = SOLVER_SOLUTION_DISTUPGRADE;
- static const int SOLVER_SOLUTION_BEST = SOLVER_SOLUTION_BEST;
- static const int SOLVER_SOLUTION_ERASE = SOLVER_SOLUTION_ERASE;
- static const int SOLVER_SOLUTION_REPLACE = SOLVER_SOLUTION_REPLACE;
- static const int SOLVER_SOLUTION_REPLACE_DOWNGRADE = SOLVER_SOLUTION_REPLACE_DOWNGRADE;
- static const int SOLVER_SOLUTION_REPLACE_ARCHCHANGE = SOLVER_SOLUTION_REPLACE_ARCHCHANGE;
- static const int SOLVER_SOLUTION_REPLACE_VENDORCHANGE = SOLVER_SOLUTION_REPLACE_VENDORCHANGE;
- static const int SOLVER_SOLUTION_REPLACE_NAMECHANGE = SOLVER_SOLUTION_REPLACE_NAMECHANGE;
-
- static const int POLICY_ILLEGAL_DOWNGRADE = POLICY_ILLEGAL_DOWNGRADE;
- static const int POLICY_ILLEGAL_ARCHCHANGE = POLICY_ILLEGAL_ARCHCHANGE;
- static const int POLICY_ILLEGAL_VENDORCHANGE = POLICY_ILLEGAL_VENDORCHANGE;
- static const int POLICY_ILLEGAL_NAMECHANGE = POLICY_ILLEGAL_NAMECHANGE;
-
- static const int SOLVER_FLAG_ALLOW_DOWNGRADE = SOLVER_FLAG_ALLOW_DOWNGRADE;
- static const int SOLVER_FLAG_ALLOW_ARCHCHANGE = SOLVER_FLAG_ALLOW_ARCHCHANGE;
- static const int SOLVER_FLAG_ALLOW_VENDORCHANGE = SOLVER_FLAG_ALLOW_VENDORCHANGE;
- static const int SOLVER_FLAG_ALLOW_NAMECHANGE = SOLVER_FLAG_ALLOW_NAMECHANGE;
- static const int SOLVER_FLAG_ALLOW_UNINSTALL = SOLVER_FLAG_ALLOW_UNINSTALL;
- static const int SOLVER_FLAG_NO_UPDATEPROVIDE = SOLVER_FLAG_NO_UPDATEPROVIDE;
- static const int SOLVER_FLAG_SPLITPROVIDES = SOLVER_FLAG_SPLITPROVIDES;
- static const int SOLVER_FLAG_IGNORE_RECOMMENDED = SOLVER_FLAG_IGNORE_RECOMMENDED;
- static const int SOLVER_FLAG_ADD_ALREADY_RECOMMENDED = SOLVER_FLAG_ADD_ALREADY_RECOMMENDED;
- static const int SOLVER_FLAG_NO_INFARCHCHECK = SOLVER_FLAG_NO_INFARCHCHECK;
- static const int SOLVER_FLAG_BEST_OBEY_POLICY = SOLVER_FLAG_BEST_OBEY_POLICY;
- static const int SOLVER_FLAG_NO_AUTOTARGET = SOLVER_FLAG_NO_AUTOTARGET;
- static const int SOLVER_FLAG_DUP_ALLOW_DOWNGRADE = SOLVER_FLAG_DUP_ALLOW_DOWNGRADE;
- static const int SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE = SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE;
- static const int SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE = SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE;
- static const int SOLVER_FLAG_DUP_ALLOW_NAMECHANGE = SOLVER_FLAG_DUP_ALLOW_NAMECHANGE;
- static const int SOLVER_FLAG_KEEP_ORPHANS = SOLVER_FLAG_KEEP_ORPHANS;
- static const int SOLVER_FLAG_BREAK_ORPHANS = SOLVER_FLAG_BREAK_ORPHANS;
- static const int SOLVER_FLAG_FOCUS_INSTALLED = SOLVER_FLAG_FOCUS_INSTALLED;
- static const int SOLVER_FLAG_YUM_OBSOLETES = SOLVER_FLAG_YUM_OBSOLETES;
- static const int SOLVER_FLAG_NEED_UPDATEPROVIDE = SOLVER_FLAG_NEED_UPDATEPROVIDE;
-
- static const int SOLVER_REASON_UNRELATED = SOLVER_REASON_UNRELATED;
- static const int SOLVER_REASON_UNIT_RULE = SOLVER_REASON_UNIT_RULE;
- static const int SOLVER_REASON_KEEP_INSTALLED = SOLVER_REASON_KEEP_INSTALLED;
- static const int SOLVER_REASON_RESOLVE_JOB = SOLVER_REASON_RESOLVE_JOB;
- static const int SOLVER_REASON_UPDATE_INSTALLED = SOLVER_REASON_UPDATE_INSTALLED;
- static const int SOLVER_REASON_CLEANDEPS_ERASE = SOLVER_REASON_CLEANDEPS_ERASE;
- static const int SOLVER_REASON_RESOLVE = SOLVER_REASON_RESOLVE;
- static const int SOLVER_REASON_WEAKDEP = SOLVER_REASON_WEAKDEP;
- static const int SOLVER_REASON_RESOLVE_ORPHAN = SOLVER_REASON_RESOLVE_ORPHAN;
- static const int SOLVER_REASON_RECOMMENDED = SOLVER_REASON_RECOMMENDED;
- static const int SOLVER_REASON_SUPPLEMENTED = SOLVER_REASON_SUPPLEMENTED;
-
- /* legacy */
- static const int SOLVER_RULE_RPM = SOLVER_RULE_RPM;
-
- ~Solver() {
- solver_free($self);
- }
-
- int set_flag(int flag, int value) {
- return solver_set_flag($self, flag, value);
- }
- int get_flag(int flag) {
- return solver_get_flag($self, flag);
- }
-#if defined(SWIGPYTHON)
- %pythoncode {
- def solve(self, jobs):
- j = []
- for job in jobs: j += [job.how, job.what]
- return self.solve_helper(j)
- }
-#endif
-#if defined(SWIGPERL)
- %perlcode {
- sub solv::Solver::solve {
- my ($self, $jobs) = @_;
- my @j = map {($_->{'how'}, $_->{'what'})} @$jobs;
- return $self->solve_helper(\@j);
- }
- }
-#endif
-#if defined(SWIGRUBY)
-%init %{
-rb_eval_string(
- "class Solv::Solver\n"
- " def solve(jobs)\n"
- " jl = []\n"
- " jobs.each do |j| ; jl << j.how << j.what ; end\n"
- " solve_helper(jl)\n"
- " end\n"
- "end\n"
- );
-%}
-#endif
- %typemap(out) Queue solve_helper Queue2Array(Problem *, 1, new_Problem(arg1, id));
- %newobject solve_helper;
- Queue solve_helper(Queue jobs) {
- Queue q;
- int i, cnt;
- queue_init(&q);
- solver_solve($self, &jobs);
- cnt = solver_problem_count($self);
- for (i = 1; i <= cnt; i++)
- queue_push(&q, i);
- return q;
- }
-#if defined(SWIGTCL)
- %typemap(out) Queue solve Queue2Array(Problem *, 1, new_Problem(arg1, id));
- %newobject solve;
- Queue solve(Queue solvejobs) {
- Queue q;
- int i, cnt;
- queue_init(&q);
- solver_solve($self, &solvejobs);
- cnt = solver_problem_count($self);
- for (i = 1; i <= cnt; i++)
- queue_push(&q, i);
- return q;
- }
-#endif
-
- %newobject transaction;
- Transaction *transaction() {
- return solver_create_transaction($self);
- }
-
- int describe_decision(XSolvable *s, XRule **OUTPUT) {
- int ruleid;
- int reason = solver_describe_decision($self, s->id, &ruleid);
- *OUTPUT = new_XRule($self, ruleid);
- return reason;
- }
-
- %newobject describe_weakdep_decision_raw;
- Queue describe_weakdep_decision_raw(XSolvable *s) {
- Queue q;
- queue_init(&q);
- solver_describe_weakdep_decision($self, s->id, &q);
- return q;
- }
-#if defined(SWIGPYTHON)
- %pythoncode {
- def describe_weakdep_decision(self, s):
- d = iter(self.describe_weakdep_decision_raw(s))
- return [ (t, XSolvable(self.pool, sid), Dep(self.pool, id)) for t, sid, id in zip(d, d, d) ]
- }
-#endif
-#if defined(SWIGPERL)
- %perlcode {
- sub solv::Solver::describe_weakdep_decision {
- my ($self, $s) = @_;
- my $pool = $self->{'pool'};
- my @res;
- my @d = $self->describe_weakdep_decision_raw($s);
- push @res, [ splice(@d, 0, 3) ] while @d;
- return map { [ $_->[0], solv::XSolvable->new($pool, $_->[1]), solv::Dep->new($pool, $_->[2]) ] } @res;
- }
- }
-#endif
-#if defined(SWIGRUBY)
-%init %{
-rb_eval_string(
- "class Solv::Solver\n"
- " def describe_weakdep_decision(s)\n"
- " self.describe_weakdep_decision_raw(s).each_slice(3).map { |t, sid, id| [ t, Solv::XSolvable.new(self.pool, sid), Solv::Dep.new(self.pool, id)] }\n"
- " end\n"
- "end\n"
- );
-%}
-#endif
-
- int alternatives_count() {
- return solver_alternatives_count($self);
- }
-
- %newobject alternative;
- Alternative *alternative(Id aid) {
- Alternative *a = solv_calloc(1, sizeof(*a));
- a->solv = $self;
- queue_init(&a->choices);
- a->type = solver_get_alternative($self, aid, &a->dep_id, &a->from_id, &a->chosen_id, &a->choices, &a->level);
- if (!a->type) {
- queue_free(&a->choices);
- solv_free(a);
- return 0;
- }
- if (a->type == SOLVER_ALTERNATIVE_TYPE_RULE) {
- a->rid = a->dep_id;
- a->dep_id = 0;
- }
- return a;
- }
-
- %typemap(out) Queue all_alternatives Queue2Array(Alternative *, 1, Solver_alternative(arg1, id));
- %newobject all_alternatives;
- Queue all_alternatives() {
- Queue q;
- int i, cnt;
- queue_init(&q);
- cnt = solver_alternatives_count($self);
- for (i = 1; i <= cnt; i++)
- queue_push(&q, i);
- return q;
- }
-
- bool write_testcase(const char *dir) {
- return testcase_write($self, dir, TESTCASE_RESULT_TRANSACTION | TESTCASE_RESULT_PROBLEMS, 0, 0);
- }
-}
-
-%extend Transaction {
- static const int SOLVER_TRANSACTION_IGNORE = SOLVER_TRANSACTION_IGNORE;
- static const int SOLVER_TRANSACTION_ERASE = SOLVER_TRANSACTION_ERASE;
- static const int SOLVER_TRANSACTION_REINSTALLED = SOLVER_TRANSACTION_REINSTALLED;
- static const int SOLVER_TRANSACTION_DOWNGRADED = SOLVER_TRANSACTION_DOWNGRADED;
- static const int SOLVER_TRANSACTION_CHANGED = SOLVER_TRANSACTION_CHANGED;
- static const int SOLVER_TRANSACTION_UPGRADED = SOLVER_TRANSACTION_UPGRADED;
- static const int SOLVER_TRANSACTION_OBSOLETED = SOLVER_TRANSACTION_OBSOLETED;
- static const int SOLVER_TRANSACTION_INSTALL = SOLVER_TRANSACTION_INSTALL;
- static const int SOLVER_TRANSACTION_REINSTALL = SOLVER_TRANSACTION_REINSTALL;
- static const int SOLVER_TRANSACTION_DOWNGRADE = SOLVER_TRANSACTION_DOWNGRADE;
- static const int SOLVER_TRANSACTION_CHANGE = SOLVER_TRANSACTION_CHANGE;
- static const int SOLVER_TRANSACTION_UPGRADE = SOLVER_TRANSACTION_UPGRADE;
- static const int SOLVER_TRANSACTION_OBSOLETES = SOLVER_TRANSACTION_OBSOLETES;
- static const int SOLVER_TRANSACTION_MULTIINSTALL = SOLVER_TRANSACTION_MULTIINSTALL;
- static const int SOLVER_TRANSACTION_MULTIREINSTALL = SOLVER_TRANSACTION_MULTIREINSTALL;
- static const int SOLVER_TRANSACTION_MAXTYPE = SOLVER_TRANSACTION_MAXTYPE;
- static const int SOLVER_TRANSACTION_SHOW_ACTIVE = SOLVER_TRANSACTION_SHOW_ACTIVE;
- static const int SOLVER_TRANSACTION_SHOW_ALL = SOLVER_TRANSACTION_SHOW_ALL;
- static const int SOLVER_TRANSACTION_SHOW_OBSOLETES = SOLVER_TRANSACTION_SHOW_OBSOLETES;
- static const int SOLVER_TRANSACTION_SHOW_MULTIINSTALL = SOLVER_TRANSACTION_SHOW_MULTIINSTALL;
- static const int SOLVER_TRANSACTION_CHANGE_IS_REINSTALL = SOLVER_TRANSACTION_CHANGE_IS_REINSTALL;
- static const int SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE = SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE;
- static const int SOLVER_TRANSACTION_MERGE_VENDORCHANGES = SOLVER_TRANSACTION_MERGE_VENDORCHANGES;
- static const int SOLVER_TRANSACTION_MERGE_ARCHCHANGES = SOLVER_TRANSACTION_MERGE_ARCHCHANGES;
- static const int SOLVER_TRANSACTION_RPM_ONLY = SOLVER_TRANSACTION_RPM_ONLY;
- static const int SOLVER_TRANSACTION_ARCHCHANGE = SOLVER_TRANSACTION_ARCHCHANGE;
- static const int SOLVER_TRANSACTION_VENDORCHANGE = SOLVER_TRANSACTION_VENDORCHANGE;
- static const int SOLVER_TRANSACTION_KEEP_ORDERDATA = SOLVER_TRANSACTION_KEEP_ORDERDATA;
- ~Transaction() {
- transaction_free($self);
- }
-#ifdef SWIGRUBY
- %rename("isempty?") isempty;
-#endif
- bool isempty() {
- return $self->steps.count == 0;
- }
-
- %newobject othersolvable;
- XSolvable *othersolvable(XSolvable *s) {
- Id op = transaction_obs_pkg($self, s->id);
- return new_XSolvable($self->pool, op);
- }
-
- %typemap(out) Queue allothersolvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
- %newobject allothersolvables;
- Queue allothersolvables(XSolvable *s) {
- Queue q;
- queue_init(&q);
- transaction_all_obs_pkgs($self, s->id, &q);
- return q;
- }
-
- %typemap(out) Queue classify Queue2Array(TransactionClass *, 4, new_TransactionClass(arg1, arg2, id, idp[1], idp[2], idp[3]));
- %newobject classify;
- Queue classify(int mode = 0) {
- Queue q;
- queue_init(&q);
- transaction_classify($self, mode, &q);
- return q;
- }
-
- /* deprecated, use newsolvables instead */
- %typemap(out) Queue newpackages Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
- %newobject newpackages;
- Queue newpackages() {
- Queue q;
- int cut;
- queue_init(&q);
- cut = transaction_installedresult(self, &q);
- queue_truncate(&q, cut);
- return q;
- }
-
- /* deprecated, use keptsolvables instead */
- %typemap(out) Queue keptpackages Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
- %newobject keptpackages;
- Queue keptpackages() {
- Queue q;
- int cut;
- queue_init(&q);
- cut = transaction_installedresult(self, &q);
- if (cut)
- queue_deleten(&q, 0, cut);
- return q;
- }
-
- %typemap(out) Queue newsolvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
- %newobject newsolvables;
- Queue newsolvables() {
- Queue q;
- int cut;
- queue_init(&q);
- cut = transaction_installedresult(self, &q);
- queue_truncate(&q, cut);
- return q;
- }
-
- %typemap(out) Queue keptsolvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
- %newobject keptsolvables;
- Queue keptsolvables() {
- Queue q;
- int cut;
- queue_init(&q);
- cut = transaction_installedresult(self, &q);
- if (cut)
- queue_deleten(&q, 0, cut);
- return q;
- }
-
- %typemap(out) Queue steps Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
- %newobject steps;
- Queue steps() {
- Queue q;
- queue_init_clone(&q, &$self->steps);
- return q;
- }
-
- int steptype(XSolvable *s, int mode) {
- return transaction_type($self, s->id, mode);
- }
- int calc_installsizechange() {
- return transaction_calc_installsizechange($self);
- }
- void order(int flags=0) {
- transaction_order($self, flags);
- }
-}
-
-%extend TransactionClass {
- TransactionClass(Transaction *trans, int mode, Id type, int count, Id fromid, Id toid) {
- TransactionClass *cl = solv_calloc(1, sizeof(*cl));
- cl->transaction = trans;
- cl->mode = mode;
- cl->type = type;
- cl->count = count;
- cl->fromid = fromid;
- cl->toid = toid;
- return cl;
- }
- %typemap(out) Queue solvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->transaction->pool, id));
- %newobject solvables;
- Queue solvables() {
- Queue q;
- queue_init(&q);
- transaction_classify_pkgs($self->transaction, $self->mode, $self->type, $self->fromid, $self->toid, &q);
- return q;
- }
- const char * const fromstr;
- const char * const tostr;
- %{
- SWIGINTERN const char *TransactionClass_fromstr_get(TransactionClass *cl) {
- return pool_id2str(cl->transaction->pool, cl->fromid);
- }
- SWIGINTERN const char *TransactionClass_tostr_get(TransactionClass *cl) {
- return pool_id2str(cl->transaction->pool, cl->toid);
- }
- %}
-}
-
-%extend XRule {
- XRule(Solver *solv, Id id) {
- if (!id)
- return 0;
- XRule *xr = solv_calloc(1, sizeof(*xr));
- xr->solv = solv;
- xr->id = id;
- return xr;
- }
- int const type;
- %{
- SWIGINTERN int XRule_type_get(XRule *xr) {
- return solver_ruleclass(xr->solv, xr->id);
- }
- %}
- %newobject info;
- Ruleinfo *info() {
- Id type, source, target, dep;
- type = solver_ruleinfo($self->solv, $self->id, &source, &target, &dep);
- return new_Ruleinfo($self, type, source, target, dep);
- }
- %typemap(out) Queue allinfos Queue2Array(Ruleinfo *, 4, new_Ruleinfo(arg1, id, idp[1], idp[2], idp[3]));
- %newobject allinfos;
- Queue allinfos() {
- Queue q;
- queue_init(&q);
- solver_allruleinfos($self->solv, $self->id, &q);
- return q;
- }
-
-#if defined(SWIGTCL)
- %rename("==") __eq__;
-#endif
- bool __eq__(XRule *xr) {
- return $self->solv == xr->solv && $self->id == xr->id;
- }
-#if defined(SWIGTCL)
- %rename("!=") __ne__;
-#endif
- bool __ne__(XRule *xr) {
- return !XRule___eq__($self, xr);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("repr") __repr__;
-#endif
- %newobject __repr__;
- const char *__repr__() {
- char buf[20];
- sprintf(buf, "<Rule #%d>", $self->id);
- return solv_strdup(buf);
- }
-}
-
-%extend Ruleinfo {
- Ruleinfo(XRule *r, Id type, Id source, Id target, Id dep_id) {
- Ruleinfo *ri = solv_calloc(1, sizeof(*ri));
- ri->solv = r->solv;
- ri->rid = r->id;
- ri->type = type;
- ri->source = source;
- ri->target = target;
- ri->dep_id = dep_id;
- return ri;
- }
- %newobject solvable;
- XSolvable * const solvable;
- %newobject othersolvable;
- XSolvable * const othersolvable;
- %newobject dep;
- Dep * const dep;
- %{
- SWIGINTERN XSolvable *Ruleinfo_solvable_get(Ruleinfo *ri) {
- return new_XSolvable(ri->solv->pool, ri->source);
- }
- SWIGINTERN XSolvable *Ruleinfo_othersolvable_get(Ruleinfo *ri) {
- return new_XSolvable(ri->solv->pool, ri->target);
- }
- SWIGINTERN Dep *Ruleinfo_dep_get(Ruleinfo *ri) {
- return new_Dep(ri->solv->pool, ri->dep_id);
- }
- %}
- const char *problemstr() {
- return solver_problemruleinfo2str($self->solv, $self->type, $self->source, $self->target, $self->dep_id);
- }
-}
-
-%extend XRepodata {
- XRepodata(Repo *repo, Id id) {
- XRepodata *xr = solv_calloc(1, sizeof(*xr));
- xr->repo = repo;
- xr->id = id;
- return xr;
- }
- Id new_handle() {
- return repodata_new_handle(repo_id2repodata($self->repo, $self->id));
- }
- void set_id(Id solvid, Id keyname, DepId id) {
- repodata_set_id(repo_id2repodata($self->repo, $self->id), solvid, keyname, id);
- }
- void set_str(Id solvid, Id keyname, const char *str) {
- repodata_set_str(repo_id2repodata($self->repo, $self->id), solvid, keyname, str);
- }
- void set_poolstr(Id solvid, Id keyname, const char *str) {
- repodata_set_poolstr(repo_id2repodata($self->repo, $self->id), solvid, keyname, str);
- }
- void add_idarray(Id solvid, Id keyname, DepId id) {
- repodata_add_idarray(repo_id2repodata($self->repo, $self->id), solvid, keyname, id);
- }
- void add_flexarray(Id solvid, Id keyname, Id handle) {
- repodata_add_flexarray(repo_id2repodata($self->repo, $self->id), solvid, keyname, handle);
- }
- void set_checksum(Id solvid, Id keyname, Chksum *chksum) {
- const unsigned char *buf = solv_chksum_get(chksum, 0);
- if (buf)
- repodata_set_bin_checksum(repo_id2repodata($self->repo, $self->id), solvid, keyname, solv_chksum_get_type(chksum), buf);
- }
- const char *lookup_str(Id solvid, Id keyname) {
- return repodata_lookup_str(repo_id2repodata($self->repo, $self->id), solvid, keyname);
- }
- Queue lookup_idarray(Id solvid, Id keyname) {
- Queue r;
- queue_init(&r);
- repodata_lookup_idarray(repo_id2repodata($self->repo, $self->id), solvid, keyname, &r);
- return r;
- }
- %newobject lookup_checksum;
- Chksum *lookup_checksum(Id solvid, Id keyname) {
- Id type = 0;
- const unsigned char *b = repodata_lookup_bin_checksum(repo_id2repodata($self->repo, $self->id), solvid, keyname, &type);
- return solv_chksum_create_from_bin(type, b);
- }
- void internalize() {
- repodata_internalize(repo_id2repodata($self->repo, $self->id));
- }
- void create_stubs() {
- Repodata *data = repo_id2repodata($self->repo, $self->id);
- data = repodata_create_stubs(data);
- $self->id = data->repodataid;
- }
- bool write(FILE *fp) {
- return repodata_write(repo_id2repodata($self->repo, $self->id), fp) == 0;
- }
- bool add_solv(FILE *fp, int flags = 0) {
- Repodata *data = repo_id2repodata($self->repo, $self->id);
- int r, oldstate = data->state;
- data->state = REPODATA_LOADING;
- r = repo_add_solv(data->repo, fp, flags | REPO_USE_LOADING);
- if (r || data->state == REPODATA_LOADING)
- data->state = oldstate;
- return r;
- }
- void extend_to_repo() {
- Repodata *data = repo_id2repodata($self->repo, $self->id);
- repodata_extend_block(data, data->repo->start, data->repo->end - data->repo->start);
- }
-#if defined(SWIGTCL)
- %rename("==") __eq__;
-#endif
- bool __eq__(XRepodata *xr) {
- return $self->repo == xr->repo && $self->id == xr->id;
- }
-#if defined(SWIGTCL)
- %rename("!=") __ne__;
-#endif
- bool __ne__(XRepodata *xr) {
- return !XRepodata___eq__($self, xr);
- }
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("repr") __repr__;
-#endif
- %newobject __repr__;
- const char *__repr__() {
- char buf[20];
- sprintf(buf, "<Repodata #%d>", $self->id);
- return solv_strdup(buf);
- }
-}
-
-#ifdef ENABLE_PUBKEY
-%extend Solvsig {
- Solvsig(FILE *fp) {
- return solvsig_create(fp);
- }
- ~Solvsig() {
- solvsig_free($self);
- }
- %newobject Chksum;
- Chksum *Chksum() {
- return $self->htype ? (Chksum *)solv_chksum_create($self->htype) : 0;
- }
-#ifdef ENABLE_PGPVRFY
- %newobject verify;
- XSolvable *verify(Repo *repo, Chksum *chksum) {
- Id p = solvsig_verify($self, repo, chksum);
- return new_XSolvable(repo->pool, p);
- }
-#endif
-}
-#endif
-
-%extend Alternative {
- static const int SOLVER_ALTERNATIVE_TYPE_RULE = SOLVER_ALTERNATIVE_TYPE_RULE;
- static const int SOLVER_ALTERNATIVE_TYPE_RECOMMENDS = SOLVER_ALTERNATIVE_TYPE_RECOMMENDS;
- static const int SOLVER_ALTERNATIVE_TYPE_SUGGESTS = SOLVER_ALTERNATIVE_TYPE_SUGGESTS;
-
- ~Alternative() {
- queue_free(&$self->choices);
- solv_free($self);
- }
- %newobject chosen;
- XSolvable * const chosen;
- %newobject rule;
- XRule * const rule;
- %newobject depsolvable;
- XSolvable * const depsolvable;
- %newobject dep;
- Dep * const dep;
- %{
- SWIGINTERN XSolvable *Alternative_chosen_get(Alternative *a) {
- return new_XSolvable(a->solv->pool, a->chosen_id);
- }
- SWIGINTERN XRule *Alternative_rule_get(Alternative *a) {
- return new_XRule(a->solv, a->rid);
- }
- SWIGINTERN XSolvable *Alternative_depsolvable_get(Alternative *a) {
- return new_XSolvable(a->solv->pool, a->from_id);
- }
- SWIGINTERN Dep *Alternative_dep_get(Alternative *a) {
- return new_Dep(a->solv->pool, a->dep_id);
- }
- %}
-
- Queue choices_raw() {
- Queue r;
- queue_init_clone(&r, &$self->choices);
- return r;
- }
-
- %typemap(out) Queue choices Queue2Array(XSolvable *, 1, new_XSolvable(arg1->solv->pool, id));
- Queue choices() {
- int i;
- Queue r;
- queue_init_clone(&r, &$self->choices);
- for (i = 0; i < r.count; i++)
- if (r.elements[i] < 0)
- r.elements[i] = -r.elements[i];
- return r;
- }
-
-#if defined(SWIGPERL) || defined(SWIGTCL)
- %rename("str") __str__;
-#endif
- const char *__str__() {
- return solver_alternative2str($self->solv, $self->type, $self->type == SOLVER_ALTERNATIVE_TYPE_RULE ? $self->rid : $self->dep_id, $self->from_id);
- }
-}
-
-#if defined(SWIGTCL)
-%init %{
- Tcl_Eval(interp,
-"proc solv::iter {varname iter body} {\n"\
-" while 1 {\n"\
-" set value [$iter __next__]\n"\
-" if {$value eq \"NULL\"} { break }\n"\
-" uplevel [list set $varname $value]\n"\
-" set code [catch {uplevel $body} result]\n"\
-" switch -exact -- $code {\n"\
-" 0 {}\n"\
-" 3 { return }\n"\
-" 4 {}\n"\
-" default { return -code $code $result }\n"\
-" }\n"\
-" }\n"\
-"}\n"
- );
-%}
-#endif
-
+++ /dev/null
-FIND_PACKAGE (TCL)
-
-SET (SWIG_TCL_FLAGS -namespace -pkgversion ${VERSION})
-
-EXECUTE_PROCESS (
- COMMAND echo "puts -nonewline [lindex [::tcl::tm::list] end]"
- COMMAND ${TCL_TCLSH}
- OUTPUT_VARIABLE TCL_INSTALL_DIR
-)
-
-MESSAGE (STATUS "Tclsh executable: ${TCL_TCLSH}")
-MESSAGE (STATUS "Tcl installation dir: ${TCL_INSTALL_DIR}")
-
-ADD_CUSTOM_COMMAND (
- OUTPUT solv_tcl.c
- COMMAND ${SWIG_EXECUTABLE} ${SWIG_FLAGS} -tcl ${SWIG_TCL_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_tcl.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
-)
-
-ADD_DEFINITIONS(-Wno-unused)
-INCLUDE_DIRECTORIES (${TCL_INCLUDE_PATH})
-
-ADD_LIBRARY (bindings_tcl SHARED solv_tcl.c)
-SET_TARGET_PROPERTIES (bindings_tcl PROPERTIES PREFIX "" OUTPUT_NAME "solv-${VERSION}" INSTALL_NAME_DIR "${TCL_INSTALL_DIR}")
-TARGET_LINK_LIBRARIES (bindings_tcl libsolvext libsolv ${TCL_LIBRARY} ${SYSTEM_LIBRARIES})
-INSTALL (TARGETS bindings_tcl LIBRARY DESTINATION ${TCL_INSTALL_DIR})
-
-ADD_CUSTOM_COMMAND (
- OUTPUT solv.tm
- COMMAND sed -e "s/__VERSION__/${VERSION}/" ${CMAKE_SOURCE_DIR}/bindings/tcl/solv.tm.in >${CMAKE_CURRENT_BINARY_DIR}/solv.tm
- DEPENDS ${CMAKE_SOURCE_DIR}/bindings/tcl/solv.tm.in
- COMMENT "Creating Tcl module to load libsolv"
-)
-ADD_CUSTOM_TARGET (solv_tm ALL DEPENDS solv.tm)
-SET_SOURCE_FILES_PROPERTIES (solv.tm PROPERTIES GENERATED TRUE)
-
-INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/solv.tm DESTINATION ${TCL_INSTALL_DIR} RENAME solv-${VERSION}.tm)
+++ /dev/null
-package require Tcl
-
-#package provide solv __VERSION__
-load [::file join [::file dirname [::info script]] "solv-__VERSION__[::info sharedlibextension]"]
+++ /dev/null
-
-IF (CHECK_INCLUDE_DIR)
- # Already in cache, be silent
- SET(CHECK_FIND_QUIETLY TRUE)
-ENDIF (CHECK_INCLUDE_DIR)
-
-FIND_PATH(CHECK_INCLUDE_DIR NAMES check.h)
-
-# Look for the library.
-FIND_LIBRARY(CHECK_LIBRARY NAMES check)
-
-IF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.4)
- # handle the QUIETLY and REQUIRED arguments and set CHECK_FOUND to TRUE if
- # all listed variables are TRUE
- INCLUDE(FindPackageHandleStandardArgs)
-
- FIND_PACKAGE_HANDLE_STANDARD_ARGS(Check "Please install 'check' and 'check-devel' packages" CHECK_LIBRARY CHECK_INCLUDE_DIR)
-ENDIF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.4)
-
-IF(CHECK_FOUND)
- SET( CHECK_LIBRARIES ${CHECK_LIBRARY} )
-ELSE(CHECK_FOUND)
- SET( CHECK_LIBRARIES )
-ENDIF(CHECK_FOUND)
-
-MARK_AS_ADVANCED(CHECK_INCLUDE_DIR)
-MARK_AS_ADVANCED(CHECK_LIBRARY)
+++ /dev/null
-# - Find expat
-# Find the native EXPAT headers and libraries.
-#
-# EXPAT_INCLUDE_DIRS - where to find expat.h, etc.
-# EXPAT_LIBRARIES - List of libraries when using expat.
-# EXPAT_FOUND - True if expat found.
-
-# Look for the header file.
-FIND_PATH(EXPAT_INCLUDE_DIR NAMES expat.h)
-MARK_AS_ADVANCED(EXPAT_INCLUDE_DIR)
-
-# Look for the library.
-FIND_LIBRARY(EXPAT_LIBRARY NAMES expat)
-MARK_AS_ADVANCED(EXPAT_LIBRARY)
-
-# Copy the results to the output variables.
-IF(EXPAT_INCLUDE_DIR AND EXPAT_LIBRARY)
- SET(EXPAT_FOUND 1)
- SET(EXPAT_LIBRARIES ${EXPAT_LIBRARY})
- SET(EXPAT_INCLUDE_DIRS ${EXPAT_INCLUDE_DIR})
-ELSE(EXPAT_INCLUDE_DIR AND EXPAT_LIBRARY)
- SET(EXPAT_FOUND 0)
- SET(EXPAT_LIBRARIES)
- SET(EXPAT_INCLUDE_DIRS)
-ENDIF(EXPAT_INCLUDE_DIR AND EXPAT_LIBRARY)
-
-# Report the results.
-IF(NOT EXPAT_FOUND)
- SET(EXPAT_DIR_MESSAGE
- "EXPAT was not found. Make sure EXPAT_LIBRARY and EXPAT_INCLUDE_DIR are set.")
- IF(NOT EXPAT_FIND_QUIETLY)
- MESSAGE(STATUS "${EXPAT_DIR_MESSAGE}")
- ELSE(NOT EXPAT_FIND_QUIETLY)
- IF(EXPAT_FIND_REQUIRED)
- MESSAGE(FATAL_ERROR "${EXPAT_DIR_MESSAGE}")
- ENDIF(EXPAT_FIND_REQUIRED)
- ENDIF(NOT EXPAT_FIND_QUIETLY)
-ENDIF(NOT EXPAT_FOUND)
+++ /dev/null
-# - Find lzma
-# Find the native LZMA headers and library
-#
-# LZMA_INCLUDE_DIR - where to find lzma.h, etc.
-# LZMA_LIBRARIES - List of libraries when using liblzma.
-# LZMA_FOUND - True if liblzma found.
-
-IF (LZMA_INCLUDE_DIR)
- # Already in cache, be silent
- SET(LZMA_FIND_QUIETLY TRUE)
-ENDIF (LZMA_INCLUDE_DIR)
-
-FIND_PATH(LZMA_INCLUDE_DIR lzma.h)
-FIND_LIBRARY(LZMA_LIBRARY NAMES lzma liblzma)
-
-# handle the QUIETLY and REQUIRED arguments and set LZMA_FOUND to TRUE if
-# all listed variables are TRUE
-INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZMA DEFAULT_MSG LZMA_LIBRARY LZMA_INCLUDE_DIR)
-
-IF(LZMA_FOUND)
- SET( LZMA_LIBRARIES ${LZMA_LIBRARY} )
-ELSE(LZMA_FOUND)
- SET( LZMA_LIBRARIES )
-ENDIF(LZMA_FOUND)
+++ /dev/null
-# FindLibSolv - Find libsolv headers and libraries.
-#
-# Sample:
-#
-# SET( LibSolv_USE_STATIC_LIBS OFF )
-# FIND_PACKAGE( LibSolv REQUIRED ext )
-# IF( LibSolv_FOUND )
-# INCLUDE_DIRECTORIES( ${LibSolv_INCLUDE_DIRS} )
-# TARGET_LINK_LIBRARIES( ... ${LibSolv_LIBRARIES} )
-# ENDIF()
-#
-# Variables used by this module need to be set before calling find_package
-# (not that they are cmale cased like the modiulemane itself):
-#
-# LibSolv_USE_STATIC_LIBS Can be set to ON to force the use of the static
-# libsolv libraries. Defaults to OFF.
-#
-# Supported components:
-#
-# ext Also include libsolvext
-#
-# Variables provided by this module:
-#
-# LibSolv_FOUND Include dir, libsolv and all extra libraries
-# specified in the COMPONENTS list were found.
-#
-# LibSolv_LIBRARIES Link to these to use all the libraries you specified.
-#
-# LibSolv_INCLUDE_DIRS Include directories.
-#
-# For each component you specify in find_package(), the following (UPPER-CASE)
-# variables are set to pick and choose components instead of just using LibSolv_LIBRARIES:
-#
-# LIBSOLV_FOUND TRUE if libsolv was found
-# LIBSOLV_LIBRARY libsolv libraries
-#
-# LIBSOLV_${COMPONENT}_FOUND TRUE if the library component was found
-# LIBSOLV_${COMPONENT}_LIBRARY The libraries for the specified component
-#
-
-# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
-IF(LibSolv_USE_STATIC_LIBS)
- SET( _ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
- SET(CMAKE_FIND_LIBRARY_SUFFIXES .a )
-ENDIF()
-
-# Look for the header files
-UNSET(LibSolv_INCLUDE_DIRS CACHE)
-FIND_PATH(LibSolv_INCLUDE_DIRS NAMES solv/solvable.h)
-
-# Look for the core library
-UNSET(LIBSOLV_LIBRARY CACHE)
-FIND_LIBRARY(LIBSOLV_LIBRARY NAMES solv)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibSolv DEFAULT_MSG LIBSOLV_LIBRARY LibSolv_INCLUDE_DIRS)
-MARK_AS_ADVANCED(
- LIBSOLV_FOUND
- LIBSOLV_LIBRARY
-)
-
-# Prepare return values and collectiong more components
-SET(LibSolv_FOUND ${LIBSOLV_FOUND})
-SET(LibSolv_LIBRARIES ${LIBSOLV_LIBRARY})
-MARK_AS_ADVANCED(
- LibSolv_FOUND
- LibSolv_LIBRARIES
- LibSolv_INCLUDE_DIRS
-)
-
-# Look for components
-FOREACH(COMPONENT ${LibSolv_FIND_COMPONENTS})
- STRING(TOUPPER ${COMPONENT} _UPPERCOMPONENT)
- UNSET(LIBSOLV_${_UPPERCOMPONENT}_LIBRARY CACHE)
- FIND_LIBRARY(LIBSOLV_${_UPPERCOMPONENT}_LIBRARY NAMES solv${COMPONENT})
- SET(LibSolv_${COMPONENT}_FIND_REQUIRED ${LibSolv_FIND_REQUIRED})
- SET(LibSolv_${COMPONENT}_FIND_QUIETLY ${LibSolv_FIND_QUIETLY})
- FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibSolv_${COMPONENT} DEFAULT_MSG LIBSOLV_${_UPPERCOMPONENT}_LIBRARY)
- MARK_AS_ADVANCED(
- LIBSOLV_${_UPPERCOMPONENT}_FOUND
- LIBSOLV_${_UPPERCOMPONENT}_LIBRARY
- )
- IF(LIBSOLV_${_UPPERCOMPONENT}_FOUND)
- SET(LibSolv_LIBRARIES ${LibSolv_LIBRARIES} ${LIBSOLV_${_UPPERCOMPONENT}_LIBRARY})
- ELSE()
- SET(LibSolv_FOUND FALSE)
- ENDIF()
-ENDFOREACH()
-
-# restore CMAKE_FIND_LIBRARY_SUFFIXES
-IF(Solv_USE_STATIC_LIBS)
- SET(CMAKE_FIND_LIBRARY_SUFFIXES ${_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES} )
-ENDIF()
-
-IF(LibSolv_FOUND AND NOT LibSolv_FIND_QUIETLY)
- MESSAGE(STATUS "Found LibSolv: ${LibSolv_INCLUDE_DIRS} ${LibSolv_LIBRARIES}")
-ENDIF()
+++ /dev/null
-# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> ... )
-#
-# This function is intended to be used in FindXXX.cmake modules files.
-# It handles the REQUIRED, QUIET and version-related arguments to FIND_PACKAGE().
-# It also sets the <UPPERCASED_NAME>_FOUND variable.
-# The package is considered found if all variables <var1>... listed contain
-# valid results, e.g. valid filepaths.
-#
-# There are two modes of this function. The first argument in both modes is
-# the name of the Find-module where it is called (in original casing).
-#
-# The first simple mode looks like this:
-# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> (DEFAULT_MSG|"Custom failure message") <var1>...<varN> )
-# If the variables <var1> to <varN> are all valid, then <UPPERCASED_NAME>_FOUND
-# will be set to TRUE.
-# If DEFAULT_MSG is given as second argument, then the function will generate
-# itself useful success and error messages. You can also supply a custom error message
-# for the failure case. This is not recommended.
-#
-# The second mode is more powerful and also supports version checking:
-# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME [REQUIRED_VARS <var1>...<varN>]
-# [VERSION_VAR <versionvar>]
-# [HANDLE_COMPONENTS]
-# [CONFIG_MODE]
-# [FAIL_MESSAGE "Custom failure message"] )
-#
-# As above, if <var1> through <varN> are all valid, <UPPERCASED_NAME>_FOUND
-# will be set to TRUE.
-# After REQUIRED_VARS the variables which are required for this package are listed.
-# Following VERSION_VAR the name of the variable can be specified which holds
-# the version of the package which has been found. If this is done, this version
-# will be checked against the (potentially) specified required version used
-# in the find_package() call. The EXACT keyword is also handled. The default
-# messages include information about the required version and the version
-# which has been actually found, both if the version is ok or not.
-# If the package supports components, use the HANDLE_COMPONENTS option to enable
-# handling them. In this case, find_package_handle_standard_args() will report
-# which components have been found and which are missing, and the <NAME>_FOUND
-# variable will be set to FALSE if any of the required components (i.e. not the
-# ones listed after OPTIONAL_COMPONENTS) are missing.
-# Use the option CONFIG_MODE if your FindXXX.cmake module is a wrapper for
-# a find_package(... NO_MODULE) call. In this case VERSION_VAR will be set
-# to <NAME>_VERSION and the macro will automatically check whether the
-# Config module was found.
-# Via FAIL_MESSAGE a custom failure message can be specified, if this is not
-# used, the default message will be displayed.
-#
-# Example for mode 1:
-#
-# FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibXml2 DEFAULT_MSG LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR)
-#
-# LibXml2 is considered to be found, if both LIBXML2_LIBRARY and
-# LIBXML2_INCLUDE_DIR are valid. Then also LIBXML2_FOUND is set to TRUE.
-# If it is not found and REQUIRED was used, it fails with FATAL_ERROR,
-# independent whether QUIET was used or not.
-# If it is found, success will be reported, including the content of <var1>.
-# On repeated Cmake runs, the same message won't be printed again.
-#
-# Example for mode 2:
-#
-# FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON REQUIRED_VARS BISON_EXECUTABLE
-# VERSION_VAR BISON_VERSION)
-# In this case, BISON is considered to be found if the variable(s) listed
-# after REQUIRED_VAR are all valid, i.e. BISON_EXECUTABLE in this case.
-# Also the version of BISON will be checked by using the version contained
-# in BISON_VERSION.
-# Since no FAIL_MESSAGE is given, the default messages will be printed.
-#
-# Another example for mode 2:
-#
-# FIND_PACKAGE(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4)
-# FIND_PACKAGE_HANDLE_STANDARD_ARGS(Automoc4 CONFIG_MODE)
-# In this case, FindAutmoc4.cmake wraps a call to FIND_PACKAGE(Automoc4 NO_MODULE)
-# and adds an additional search directory for automoc4.
-# The following FIND_PACKAGE_HANDLE_STANDARD_ARGS() call produces a proper
-# success/error message.
-
-#=============================================================================
-# Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# * Neither the names of Kitware, Inc., the Insight Software Consortium,
-# nor the names of their contributors may be used to endorse or promote
-# products derived from this software without specific prior written
-# permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#=============================================================================
-
-INCLUDE(FindPackageMessage)
-INCLUDE(_CMakeParseArguments)
-
-# internal helper macro
-MACRO(_FPHSA_FAILURE_MESSAGE _msg)
- IF (${_NAME}_FIND_REQUIRED)
- MESSAGE(FATAL_ERROR "${_msg}")
- ELSE (${_NAME}_FIND_REQUIRED)
- IF (NOT ${_NAME}_FIND_QUIETLY)
- MESSAGE(STATUS "${_msg}")
- ENDIF (NOT ${_NAME}_FIND_QUIETLY)
- ENDIF (${_NAME}_FIND_REQUIRED)
-ENDMACRO(_FPHSA_FAILURE_MESSAGE _msg)
-
-
-# internal helper macro to generate the failure message when used in CONFIG_MODE:
-MACRO(_FPHSA_HANDLE_FAILURE_CONFIG_MODE)
- # <name>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found:
- IF(${_NAME}_CONFIG)
- _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing: ${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})")
- ELSE(${_NAME}_CONFIG)
- # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version.
- # List them all in the error message:
- IF(${_NAME}_CONSIDERED_CONFIGS)
- SET(configsText "")
- LIST(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount)
- MATH(EXPR configsCount "${configsCount} - 1")
- FOREACH(currentConfigIndex RANGE ${configsCount})
- LIST(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename)
- LIST(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version)
- SET(configsText "${configsText} ${filename} (version ${version})\n")
- ENDFOREACH(currentConfigIndex)
- _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}")
-
- ELSE(${_NAME}_CONSIDERED_CONFIGS)
- # Simple case: No Config-file was found at all:
- _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}")
- ENDIF(${_NAME}_CONSIDERED_CONFIGS)
- ENDIF(${_NAME}_CONFIG)
-ENDMACRO(_FPHSA_HANDLE_FAILURE_CONFIG_MODE)
-
-
-FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
-
-# set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in
-# new extended or in the "old" mode:
- SET(options CONFIG_MODE HANDLE_COMPONENTS)
- SET(oneValueArgs FAIL_MESSAGE VERSION_VAR)
- SET(multiValueArgs REQUIRED_VARS)
- SET(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} )
- LIST(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX)
-
- IF(${INDEX} EQUAL -1)
- SET(FPHSA_FAIL_MESSAGE ${_FIRST_ARG})
- SET(FPHSA_REQUIRED_VARS ${ARGN})
- SET(FPHSA_VERSION_VAR)
- ELSE(${INDEX} EQUAL -1)
-
- CMAKE_PARSE_ARGUMENTS(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN})
-
- IF(FPHSA_UNPARSED_ARGUMENTS)
- MESSAGE(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"")
- ENDIF(FPHSA_UNPARSED_ARGUMENTS)
-
- IF(NOT FPHSA_FAIL_MESSAGE)
- SET(FPHSA_FAIL_MESSAGE "DEFAULT_MSG")
- ENDIF(NOT FPHSA_FAIL_MESSAGE)
- ENDIF(${INDEX} EQUAL -1)
-
-# now that we collected all arguments, process them
-
- IF("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG")
- SET(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}")
- ENDIF("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG")
-
- # In config-mode, we rely on the variable <package>_CONFIG, which is set by find_package()
- # when it successfully found the config-file, including version checking:
- IF(FPHSA_CONFIG_MODE)
- LIST(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG)
- LIST(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS)
- SET(FPHSA_VERSION_VAR ${_NAME}_VERSION)
- ENDIF(FPHSA_CONFIG_MODE)
-
- IF(NOT FPHSA_REQUIRED_VARS)
- MESSAGE(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()")
- ENDIF(NOT FPHSA_REQUIRED_VARS)
-
- LIST(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR)
-
- STRING(TOUPPER ${_NAME} _NAME_UPPER)
- STRING(TOLOWER ${_NAME} _NAME_LOWER)
-
- # collect all variables which were not found, so they can be printed, so the
- # user knows better what went wrong (#6375)
- SET(MISSING_VARS "")
- SET(DETAILS "")
- SET(${_NAME_UPPER}_FOUND TRUE)
- # check if all passed variables are valid
- FOREACH(_CURRENT_VAR ${FPHSA_REQUIRED_VARS})
- IF(NOT ${_CURRENT_VAR})
- SET(${_NAME_UPPER}_FOUND FALSE)
- SET(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}")
- ELSE(NOT ${_CURRENT_VAR})
- SET(DETAILS "${DETAILS}[${${_CURRENT_VAR}}]")
- ENDIF(NOT ${_CURRENT_VAR})
- ENDFOREACH(_CURRENT_VAR)
-
- # component handling
- SET(FOUND_COMPONENTS_MSG "")
- SET(MISSING_COMPONENTS_MSG "")
-
- IF(FPHSA_HANDLE_COMPONENTS)
- FOREACH(comp ${${_NAME}_FIND_COMPONENTS})
- IF(${_NAME}_${comp}_FOUND)
-
- IF(NOT FOUND_COMPONENTS_MSG)
- SET(FOUND_COMPONENTS_MSG "found components: ")
- ENDIF()
- SET(FOUND_COMPONENTS_MSG "${FOUND_COMPONENTS_MSG} ${comp}")
-
- ELSE()
-
- IF(NOT MISSING_COMPONENTS_MSG)
- SET(MISSING_COMPONENTS_MSG "missing components: ")
- ENDIF()
- SET(MISSING_COMPONENTS_MSG "${MISSING_COMPONENTS_MSG} ${comp}")
-
- IF(${_NAME}_FIND_REQUIRED_${comp})
- SET(${_NAME_UPPER}_FOUND FALSE)
- SET(MISSING_VARS "${MISSING_VARS} ${comp}")
- ENDIF()
-
- ENDIF()
- ENDFOREACH(comp)
- SET(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}")
- SET(DETAILS "${DETAILS}[c${COMPONENT_MSG}]")
- ENDIF(FPHSA_HANDLE_COMPONENTS)
-
- # version handling:
- SET(VERSION_MSG "")
- SET(VERSION_OK TRUE)
- SET(VERSION ${${FPHSA_VERSION_VAR}} )
- IF (${_NAME}_FIND_VERSION)
-
- IF(VERSION)
-
- IF(${_NAME}_FIND_VERSION_EXACT) # exact version required
- IF (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}")
- SET(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"")
- SET(VERSION_OK FALSE)
- ELSE (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}")
- SET(VERSION_MSG "(found suitable exact version \"${VERSION}\")")
- ENDIF (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}")
-
- ELSE(${_NAME}_FIND_VERSION_EXACT) # minimum version specified:
- IF ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}")
- SET(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"")
- SET(VERSION_OK FALSE)
- ELSE ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}")
- SET(VERSION_MSG "(found suitable version \"${VERSION}\", required is \"${${_NAME}_FIND_VERSION}\")")
- ENDIF ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}")
- ENDIF(${_NAME}_FIND_VERSION_EXACT)
-
- ELSE(VERSION)
-
- # if the package was not found, but a version was given, add that to the output:
- IF(${_NAME}_FIND_VERSION_EXACT)
- SET(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")")
- ELSE(${_NAME}_FIND_VERSION_EXACT)
- SET(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")")
- ENDIF(${_NAME}_FIND_VERSION_EXACT)
-
- ENDIF(VERSION)
- ELSE (${_NAME}_FIND_VERSION)
- IF(VERSION)
- SET(VERSION_MSG "(found version \"${VERSION}\")")
- ENDIF(VERSION)
- ENDIF (${_NAME}_FIND_VERSION)
-
- IF(VERSION_OK)
- SET(DETAILS "${DETAILS}[v${VERSION}(${${_NAME}_FIND_VERSION})]")
- ELSE(VERSION_OK)
- SET(${_NAME_UPPER}_FOUND FALSE)
- ENDIF(VERSION_OK)
-
-
- # print the result:
- IF (${_NAME_UPPER}_FOUND)
- FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}")
- ELSE (${_NAME_UPPER}_FOUND)
-
- IF(FPHSA_CONFIG_MODE)
- _FPHSA_HANDLE_FAILURE_CONFIG_MODE()
- ELSE(FPHSA_CONFIG_MODE)
- IF(NOT VERSION_OK)
- _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})")
- ELSE(NOT VERSION_OK)
- _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}")
- ENDIF(NOT VERSION_OK)
- ENDIF(FPHSA_CONFIG_MODE)
-
- ENDIF (${_NAME_UPPER}_FOUND)
-
- SET(${_NAME_UPPER}_FOUND ${${_NAME_UPPER}_FOUND} PARENT_SCOPE)
-
-ENDFUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _FIRST_ARG)
+++ /dev/null
-# - Find Ruby
-# This module finds if Ruby is installed and determines where the include files
-# and libraries are. Ruby 1.8 and 1.9 are supported.
-#
-# The minimum required version of Ruby can be specified using the
-# standard syntax, e.g. FIND_PACKAGE(Ruby 1.8)
-#
-# It also determines what the name of the library is. This
-# code sets the following variables:
-#
-# RUBY_EXECUTABLE = full path to the ruby binary
-# RUBY_INCLUDE_DIRS = include dirs to be used when using the ruby library
-# RUBY_LIBRARY = full path to the ruby library
-# RUBY_VERSION = the version of ruby which was found, e.g. "1.8.7"
-# RUBY_FOUND = set to true if ruby ws found successfully
-#
-# RUBY_INCLUDE_PATH = same as RUBY_INCLUDE_DIRS, only provided for compatibility reasons, don't use it
-
-#=============================================================================
-# Copyright 2004-2009 Kitware, Inc.
-# Copyright 2008-2009 Alexander Neundorf <neundorf@kde.org>
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# * Neither the names of Kitware, Inc., the Insight Software Consortium,
-# nor the names of their contributors may be used to endorse or promote
-# products derived from this software without specific prior written
-# permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#=============================================================================
-
-# RUBY_ARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"archdir"@:>@)'`
-# RUBY_SITEARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitearchdir"@:>@)'`
-# RUBY_SITEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitelibdir"@:>@)'`
-# RUBY_LIBDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"libdir"@:>@)'`
-# RUBY_LIBRUBYARG=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"LIBRUBYARG_SHARED"@:>@)'`
-
-# uncomment the following line to get debug output for this file
-# SET(_RUBY_DEBUG_OUTPUT TRUE)
-
-# Determine the list of possible names of the ruby executable depending
-# on which version of ruby is required
-SET(_RUBY_POSSIBLE_EXECUTABLE_NAMES ruby)
-
-# if 1.9 is required, don't look for ruby18 and ruby1.8, default to version 1.8
-IF(Ruby_FIND_VERSION_MAJOR AND Ruby_FIND_VERSION_MINOR)
- SET(Ruby_FIND_VERSION_SHORT_NODOT "${Ruby_FIND_VERSION_MAJOR}${RUBY_FIND_VERSION_MINOR}")
-ELSE(Ruby_FIND_VERSION_MAJOR AND Ruby_FIND_VERSION_MINOR)
- SET(Ruby_FIND_VERSION_SHORT_NODOT "18")
-ENDIF(Ruby_FIND_VERSION_MAJOR AND Ruby_FIND_VERSION_MINOR)
-
-SET(_RUBY_POSSIBLE_EXECUTABLE_NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES} ruby1.9 ruby19)
-
-# if we want a version below 1.9, also look for ruby 1.8
-IF("${Ruby_FIND_VERSION_SHORT_NODOT}" VERSION_LESS "19")
- SET(_RUBY_POSSIBLE_EXECUTABLE_NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES} ruby1.8 ruby18)
-ENDIF("${Ruby_FIND_VERSION_SHORT_NODOT}" VERSION_LESS "19")
-
-FIND_PROGRAM(RUBY_EXECUTABLE NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES})
-
-
-IF(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
- FUNCTION(_RUBY_CONFIG_VAR RBVAR OUTVAR)
- EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['${RBVAR}']"
- RESULT_VARIABLE _RUBY_SUCCESS
- OUTPUT_VARIABLE _RUBY_OUTPUT
- ERROR_QUIET)
- IF(_RUBY_SUCCESS OR NOT _RUBY_OUTPUT)
- EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['${RBVAR}']"
- RESULT_VARIABLE _RUBY_SUCCESS
- OUTPUT_VARIABLE _RUBY_OUTPUT
- ERROR_QUIET)
- ENDIF(_RUBY_SUCCESS OR NOT _RUBY_OUTPUT)
- SET(${OUTVAR} "${_RUBY_OUTPUT}" PARENT_SCOPE)
- ENDFUNCTION(_RUBY_CONFIG_VAR)
-
-
- # query the ruby version
- _RUBY_CONFIG_VAR("MAJOR" RUBY_VERSION_MAJOR)
- _RUBY_CONFIG_VAR("MINOR" RUBY_VERSION_MINOR)
- _RUBY_CONFIG_VAR("TEENY" RUBY_VERSION_PATCH)
-
- # query the different directories
- _RUBY_CONFIG_VAR("archdir" RUBY_ARCH_DIR)
- _RUBY_CONFIG_VAR("arch" RUBY_ARCH)
- _RUBY_CONFIG_VAR("rubyhdrdir" RUBY_HDR_DIR)
- _RUBY_CONFIG_VAR("libdir" RUBY_POSSIBLE_LIB_DIR)
- _RUBY_CONFIG_VAR("rubylibdir" RUBY_RUBY_LIB_DIR)
-
- # site_ruby
- _RUBY_CONFIG_VAR("sitearchdir" RUBY_SITEARCH_DIR)
- _RUBY_CONFIG_VAR("sitelibdir" RUBY_SITELIB_DIR)
-
- # vendor_ruby available ?
- EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print 'true' unless RbConfig::CONFIG['vendorarchdir'].nil?"
- OUTPUT_VARIABLE RUBY_HAS_VENDOR_RUBY ERROR_QUIET)
-
- IF(RUBY_HAS_VENDOR_RUBY)
- _RUBY_CONFIG_VAR("vendorlibdir" RUBY_VENDORLIB_DIR)
- _RUBY_CONFIG_VAR("vendorarchdir" RUBY_VENDORARCH_DIR)
- ENDIF(RUBY_HAS_VENDOR_RUBY)
-
- # save the results in the cache so we don't have to run ruby the next time again
- SET(RUBY_VERSION_MAJOR ${RUBY_VERSION_MAJOR} CACHE PATH "The Ruby major version" FORCE)
- SET(RUBY_VERSION_MINOR ${RUBY_VERSION_MINOR} CACHE PATH "The Ruby minor version" FORCE)
- SET(RUBY_VERSION_PATCH ${RUBY_VERSION_PATCH} CACHE PATH "The Ruby patch version" FORCE)
- SET(RUBY_ARCH_DIR ${RUBY_ARCH_DIR} CACHE PATH "The Ruby arch dir" FORCE)
- SET(RUBY_HDR_DIR ${RUBY_HDR_DIR} CACHE PATH "The Ruby header dir (1.9)" FORCE)
- SET(RUBY_POSSIBLE_LIB_DIR ${RUBY_POSSIBLE_LIB_DIR} CACHE PATH "The Ruby lib dir" FORCE)
- SET(RUBY_RUBY_LIB_DIR ${RUBY_RUBY_LIB_DIR} CACHE PATH "The Ruby ruby-lib dir" FORCE)
- SET(RUBY_SITEARCH_DIR ${RUBY_SITEARCH_DIR} CACHE PATH "The Ruby site arch dir" FORCE)
- SET(RUBY_SITELIB_DIR ${RUBY_SITELIB_DIR} CACHE PATH "The Ruby site lib dir" FORCE)
- SET(RUBY_HAS_VENDOR_RUBY ${RUBY_HAS_VENDOR_RUBY} CACHE BOOL "Vendor Ruby is available" FORCE)
- SET(RUBY_VENDORARCH_DIR ${RUBY_VENDORARCH_DIR} CACHE PATH "The Ruby vendor arch dir" FORCE)
- SET(RUBY_VENDORLIB_DIR ${RUBY_VENDORLIB_DIR} CACHE PATH "The Ruby vendor lib dir" FORCE)
-
- MARK_AS_ADVANCED(
- RUBY_ARCH_DIR
- RUBY_ARCH
- RUBY_HDR_DIR
- RUBY_POSSIBLE_LIB_DIR
- RUBY_RUBY_LIB_DIR
- RUBY_SITEARCH_DIR
- RUBY_SITELIB_DIR
- RUBY_HAS_VENDOR_RUBY
- RUBY_VENDORARCH_DIR
- RUBY_VENDORLIB_DIR
- RUBY_VERSION_MAJOR
- RUBY_VERSION_MINOR
- RUBY_VERSION_PATCH
- )
-ENDIF(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
-
-# In case RUBY_EXECUTABLE could not be executed (e.g. cross compiling)
-# try to detect which version we found. This is not too good.
-IF(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
- # by default assume 1.8.0
- SET(RUBY_VERSION_MAJOR 1)
- SET(RUBY_VERSION_MINOR 8)
- SET(RUBY_VERSION_PATCH 0)
- # check whether we found 1.9.x
- IF(${RUBY_EXECUTABLE} MATCHES "ruby1.?9" OR RUBY_HDR_DIR)
- SET(RUBY_VERSION_MAJOR 1)
- SET(RUBY_VERSION_MINOR 9)
- ENDIF(${RUBY_EXECUTABLE} MATCHES "ruby1.?9" OR RUBY_HDR_DIR)
-ENDIF(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
-
-IF(RUBY_VERSION_MAJOR)
- SET(RUBY_VERSION "${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH}")
- SET(_RUBY_VERSION_SHORT "${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}")
- SET(_RUBY_VERSION_SHORT_NODOT "${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}")
- SET(_RUBY_NODOT_VERSION "${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}${RUBY_VERSION_PATCH}")
-ENDIF(RUBY_VERSION_MAJOR)
-
-FIND_PATH(RUBY_INCLUDE_DIR
- NAMES ruby.h
- HINTS
- ${RUBY_HDR_DIR}
- ${RUBY_ARCH_DIR}
- /usr/lib/ruby/${_RUBY_VERSION_SHORT}/i586-linux-gnu/ )
-
-SET(RUBY_INCLUDE_DIRS ${RUBY_INCLUDE_DIR} )
-
-# if ruby > 1.8 is required or if ruby > 1.8 was found, search for the config.h dir
-IF( "${Ruby_FIND_VERSION_SHORT_NODOT}" GREATER 18 OR "${_RUBY_VERSION_SHORT_NODOT}" GREATER 18 OR RUBY_HDR_DIR)
- FIND_PATH(RUBY_CONFIG_INCLUDE_DIR
- NAMES ruby/config.h config.h
- HINTS
- ${RUBY_HDR_DIR}/${RUBY_ARCH}
- ${RUBY_ARCH_DIR}
- )
-
- SET(RUBY_INCLUDE_DIRS ${RUBY_INCLUDE_DIRS} ${RUBY_CONFIG_INCLUDE_DIR} )
-ENDIF( "${Ruby_FIND_VERSION_SHORT_NODOT}" GREATER 18 OR "${_RUBY_VERSION_SHORT_NODOT}" GREATER 18 OR RUBY_HDR_DIR)
-
-
-# Determine the list of possible names for the ruby library
-SET(_RUBY_POSSIBLE_LIB_NAMES ruby ruby-static ruby${_RUBY_VERSION_SHORT} ruby${_RUBY_VERSION_SHORT_NODOT} ruby-${_RUBY_VERSION_SHORT} ruby-${RUBY_VERSION})
-
-IF(WIN32)
- SET( _RUBY_MSVC_RUNTIME "" )
- IF( MSVC60 )
- SET( _RUBY_MSVC_RUNTIME "60" )
- ENDIF( MSVC60 )
- IF( MSVC70 )
- SET( _RUBY_MSVC_RUNTIME "70" )
- ENDIF( MSVC70 )
- IF( MSVC71 )
- SET( _RUBY_MSVC_RUNTIME "71" )
- ENDIF( MSVC71 )
- IF( MSVC80 )
- SET( _RUBY_MSVC_RUNTIME "80" )
- ENDIF( MSVC80 )
- IF( MSVC90 )
- SET( _RUBY_MSVC_RUNTIME "90" )
- ENDIF( MSVC90 )
-
- LIST(APPEND _RUBY_POSSIBLE_LIB_NAMES
- "msvcr${_RUBY_MSVC_RUNTIME}-ruby${_RUBY_NODOT_VERSION}"
- "msvcr${_RUBY_MSVC_RUNTIME}-ruby${_RUBY_NODOT_VERSION}-static"
- "msvcrt-ruby${_RUBY_NODOT_VERSION}"
- "msvcrt-ruby${_RUBY_NODOT_VERSION}-static" )
-ENDIF(WIN32)
-
-FIND_LIBRARY(RUBY_LIBRARY NAMES ${_RUBY_POSSIBLE_LIB_NAMES} HINTS ${RUBY_POSSIBLE_LIB_DIR} )
-
-INCLUDE(FindPackageHandleStandardArgs)
-SET(_RUBY_REQUIRED_VARS RUBY_EXECUTABLE RUBY_INCLUDE_DIR RUBY_LIBRARY)
-IF(_RUBY_VERSION_SHORT_NODOT GREATER 18)
- LIST(APPEND _RUBY_REQUIRED_VARS RUBY_CONFIG_INCLUDE_DIR)
-ENDIF(_RUBY_VERSION_SHORT_NODOT GREATER 18)
-
-IF(_RUBY_DEBUG_OUTPUT)
- MESSAGE(STATUS "--------FindRuby.cmake debug------------")
- MESSAGE(STATUS "_RUBY_POSSIBLE_EXECUTABLE_NAMES: ${_RUBY_POSSIBLE_EXECUTABLE_NAMES}")
- MESSAGE(STATUS "_RUBY_POSSIBLE_LIB_NAMES: ${_RUBY_POSSIBLE_LIB_NAMES}")
- MESSAGE(STATUS "RUBY_ARCH_DIR: ${RUBY_ARCH_DIR}")
- MESSAGE(STATUS "RUBY_HDR_DIR: ${RUBY_HDR_DIR}")
- MESSAGE(STATUS "RUBY_POSSIBLE_LIB_DIR: ${RUBY_POSSIBLE_LIB_DIR}")
- MESSAGE(STATUS "Found RUBY_VERSION: \"${RUBY_VERSION}\" , short: \"${_RUBY_VERSION_SHORT}\", nodot: \"${_RUBY_VERSION_SHORT_NODOT}\"")
- MESSAGE(STATUS "_RUBY_REQUIRED_VARS: ${_RUBY_REQUIRED_VARS}")
- MESSAGE(STATUS "RUBY_EXECUTABLE: ${RUBY_EXECUTABLE}")
- MESSAGE(STATUS "RUBY_LIBRARY: ${RUBY_LIBRARY}")
- MESSAGE(STATUS "RUBY_INCLUDE_DIR: ${RUBY_INCLUDE_DIR}")
- MESSAGE(STATUS "RUBY_CONFIG_INCLUDE_DIR: ${RUBY_CONFIG_INCLUDE_DIR}")
- MESSAGE(STATUS "--------------------")
-ENDIF(_RUBY_DEBUG_OUTPUT)
-
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(Ruby REQUIRED_VARS ${_RUBY_REQUIRED_VARS}
- VERSION_VAR RUBY_VERSION )
-
-MARK_AS_ADVANCED(
- RUBY_EXECUTABLE
- RUBY_LIBRARY
- RUBY_INCLUDE_DIR
- RUBY_CONFIG_INCLUDE_DIR
- )
-
-# Set some variables for compatibility with previous version of this file
-SET(RUBY_POSSIBLE_LIB_PATH ${RUBY_POSSIBLE_LIB_DIR})
-SET(RUBY_RUBY_LIB_PATH ${RUBY_RUBY_LIB_DIR})
-SET(RUBY_INCLUDE_PATH ${RUBY_INCLUDE_DIRS})
+++ /dev/null
-# CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords> <multi_value_keywords> args...)
-#
-# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions for
-# parsing the arguments given to that macro or function.
-# It processes the arguments and defines a set of variables which hold the
-# values of the respective options.
-#
-# The <options> argument contains all options for the respective macro,
-# i.e. keywords which can be used when calling the macro without any value
-# following, like e.g. the OPTIONAL keyword of the install() command.
-#
-# The <one_value_keywords> argument contains all keywords for this macro
-# which are followed by one value, like e.g. DESTINATION keyword of the
-# install() command.
-#
-# The <multi_value_keywords> argument contains all keywords for this macro
-# which can be followed by more than one value, like e.g. the TARGETS or
-# FILES keywords of the install() command.
-#
-# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the
-# keywords listed in <options>, <one_value_keywords> and
-# <multi_value_keywords> a variable composed of the given <prefix>
-# followed by "_" and the name of the respective keyword.
-# These variables will then hold the respective value from the argument list.
-# For the <options> keywords this will be TRUE or FALSE.
-#
-# All remaining arguments are collected in a variable
-# <prefix>_UNPARSED_ARGUMENTS, this can be checked afterwards to see whether
-# your macro was called with unrecognized parameters.
-#
-# As an example here a my_install() macro, which takes similar arguments as the
-# real install() command:
-#
-# function(MY_INSTALL)
-# set(options OPTIONAL FAST)
-# set(oneValueArgs DESTINATION RENAME)
-# set(multiValueArgs TARGETS CONFIGURATIONS)
-# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
-# ...
-#
-# Assume my_install() has been called like this:
-# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub)
-#
-# After the cmake_parse_arguments() call the macro will have set the following
-# variables:
-# MY_INSTALL_OPTIONAL = TRUE
-# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install()
-# MY_INSTALL_DESTINATION = "bin"
-# MY_INSTALL_RENAME = "" (was not used)
-# MY_INSTALL_TARGETS = "foo;bar"
-# MY_INSTALL_CONFIGURATIONS = "" (was not used)
-# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL"
-#
-# You can the continue and process these variables.
-#
-# Keywords terminate lists of values, e.g. if directly after a one_value_keyword
-# another recognized keyword follows, this is interpreted as the beginning of
-# the new option.
-# E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in
-# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would
-# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor.
-
-#=============================================================================
-# Copyright 2010 Alexander Neundorf <neundorf@kde.org>
-#
-# Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# * Neither the names of Kitware, Inc., the Insight Software Consortium,
-# nor the names of their contributors may be used to endorse or promote
-# products derived from this software without specific prior written
-# permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#=============================================================================
-
-
-if(__CMAKE_PARSE_ARGUMENTS_INCLUDED)
- return()
-endif()
-set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE)
-
-
-function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames)
- # first set all result variables to empty/FALSE
- foreach(arg_name ${_singleArgNames} ${_multiArgNames})
- set(${prefix}_${arg_name})
- endforeach(arg_name)
-
- foreach(option ${_optionNames})
- set(${prefix}_${option} FALSE)
- endforeach(option)
-
- set(${prefix}_UNPARSED_ARGUMENTS)
-
- set(insideValues FALSE)
- set(currentArgName)
-
- # now iterate over all arguments and fill the result variables
- foreach(currentArg ${ARGN})
- list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword
- list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword
- list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword
-
- if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1)
- if(insideValues)
- if("${insideValues}" STREQUAL "SINGLE")
- set(${prefix}_${currentArgName} ${currentArg})
- set(insideValues FALSE)
- elseif("${insideValues}" STREQUAL "MULTI")
- list(APPEND ${prefix}_${currentArgName} ${currentArg})
- endif()
- else(insideValues)
- list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg})
- endif(insideValues)
- else()
- if(NOT ${optionIndex} EQUAL -1)
- set(${prefix}_${currentArg} TRUE)
- set(insideValues FALSE)
- elseif(NOT ${singleArgIndex} EQUAL -1)
- set(currentArgName ${currentArg})
- set(${prefix}_${currentArgName})
- set(insideValues "SINGLE")
- elseif(NOT ${multiArgIndex} EQUAL -1)
- set(currentArgName ${currentArg})
- set(${prefix}_${currentArgName})
- set(insideValues "MULTI")
- endif()
- endif()
-
- endforeach(currentArg)
-
- # propagate the result variables to the caller:
- foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames})
- set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE)
- endforeach(arg_name)
- set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE)
-
-endfunction(CMAKE_PARSE_ARGUMENTS _options _singleArgs _multiArgs)
+++ /dev/null
-
-SET (libsolv_MANPAGES3
- libsolv.3 libsolv-bindings.3 libsolv-constantids.3 libsolv-history.3
- libsolv-pool.3)
-
-SET (libsolv_MANPAGES1
- mergesolv.1 dumpsolv.1 installcheck.1 testsolv.1)
-
-IF (ENABLE_RPMDB)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} rpmdb2solv.1 rpms2solv.1)
-ENDIF (ENABLE_RPMDB)
-
-IF (ENABLE_RPMMD)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} repomdxml2solv.1 rpmmd2solv.1 updateinfoxml2solv.1 deltainfoxml2solv.1)
-ENDIF (ENABLE_RPMMD)
-
-IF (ENABLE_HELIXREPO)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} helix2solv.1)
-ENDIF (ENABLE_HELIXREPO)
-
-IF (ENABLE_SUSEREPO)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} susetags2solv.1)
-ENDIF (ENABLE_SUSEREPO)
-
-IF (ENABLE_COMPS)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} comps2solv.1)
-ENDIF (ENABLE_COMPS)
-
-IF (ENABLE_DEBIAN)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} deb2solv.1)
-ENDIF (ENABLE_DEBIAN)
-
-IF (ENABLE_MDKREPO)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} mdk2solv.1)
-ENDIF (ENABLE_MDKREPO)
-
-IF (ENABLE_ARCHREPO)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} archpkgs2solv.1 archrepo2solv.1)
-ENDIF (ENABLE_ARCHREPO)
-
-IF (ENABLE_APPDATA)
-SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} appdata2solv.1)
-ENDIF (ENABLE_APPDATA)
-
-INSTALL(FILES
- ${libsolv_MANPAGES3}
- DESTINATION "${MAN_INSTALL_DIR}/man3")
-
-INSTALL(FILES
- ${libsolv_MANPAGES1}
- DESTINATION "${MAN_INSTALL_DIR}/man1")
+++ /dev/null
-
-man: man3 man1
-
-man3: libsolv.3 libsolv-bindings.3 libsolv-constantids.3 libsolv-history.3 libsolv-pool.3
-
-man1: mergesolv.1 dumpsolv.1 installcheck.1 testsolv.1 rpmdb2solv.1 rpms2solv.1 \
- rpmmd2solv.1 repomdxml2solv.1 updateinfoxml2solv.1 deltainfoxml2solv.1 \
- helix2solv.1 susetags2solv.1 comps2solv.1 deb2solv.1 mdk2solv.1 \
- archpkgs2solv.1 archrepo2solv.1 appdata2solv.1
-
-html: libsolv.html libsolv-bindings.html libsolv-constantids.html libsolv-history.html libsolv-pool.html
-
-.SUFFIXES: .html .3 .1 .txt
-
-.txt.1:
- a2x -f manpage $<
-
-.txt.3:
- a2x -f manpage $<
-
-.txt.html:
- a2x -f xhtml $<
+++ /dev/null
-'\" t
-.\" Title: appdata2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "APPDATA2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-appdata2solv \- convert application meta data into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBappdata2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The appdata format contains metadata about application\&. It can be available both in repositories (for available applications) and in the installed system (for installed applications)\&. The appdata2solv tool reads the metadata from stdin and writes the parsed data as solv file to standard output\&. The parser will create \fBapplication:\fR pseudo packages for each entry\&.
-.PP
-\fB\-d\fR \fIAPPDATADIR\fR
-.RS 4
-Do not read from standard input, instead scan the specified directory for appdata entries\&.
-\fIAPPDATADIR\fR
-is normally set to
-\fB/usr/share/appdata\fR\&.
-.RE
-.PP
-\fB\-r\fR \fIROOTDIR\fR
-.RS 4
-Use
-\fIROOTDIR\fR
-as root directory\&.
-.RE
-.SH "SEE ALSO"
-.sp
-mergesolv(1)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-appdata2solv(1)
-===============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-appdata2solv - convert application meta data into a solv file
-
-Synopsis
---------
-*appdata2solv* ['OPTIONS']
-
-Description
------------
-The appdata format contains metadata about application. It can
-be available both in repositories (for available applications)
-and in the installed system (for installed applications).
-The appdata2solv tool reads the metadata from stdin and
-writes the parsed data as solv file to standard output. The
-parser will create *application:* pseudo packages for each entry.
-
-*-d* 'APPDATADIR'::
-Do not read from standard input, instead scan the specified
-directory for appdata entries. 'APPDATADIR' is normally
-set to */usr/share/appdata*.
-
-*-r* 'ROOTDIR'::
-Use 'ROOTDIR' as root directory.
-
-
-See Also
---------
-mergesolv(1)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: archpkgs2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "ARCHPKGS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-archpkgs2solv \- convert one or more Arch package files into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBarchpkgs2solv\fR [\fIOPTIONS\fR] \fIPKG1\&.pkg\&.xz\fR \&...
-.SH "DESCRIPTION"
-.sp
-The archpkgs2solv tool converts the meta data from one or more Arch Linux packages into the solv file written to standard output\&.
-.PP
-\fB\-m\fR \fIMANIFESTFILE\fR
-.RS 4
-Read the rpm file names from the specified
-\fIMANIFESTFILE\fR\&. You can use
-\fB\-\fR
-to read the manifest from standard input\&.
-.RE
-.PP
-\fB\-0\fR
-.RS 4
-Use a null byte as line terminator for manifest files instead of a newline\&. This is useful if the file names can contain newlines\&. See also the
-\fB\-print0\fR
-option in
-\fBfind\fR\&.
-.RE
-.SH "SEE ALSO"
-.sp
-pacman(8)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-archpkgs2solv(1)
-================
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-archpkgs2solv - convert one or more Arch package files into a solv file
-
-Synopsis
---------
-*archpkgs2solv* ['OPTIONS'] 'PKG1.pkg.xz' ...
-
-Description
------------
-The archpkgs2solv tool converts the meta data from one or more
-Arch Linux packages into the solv file written to standard output.
-
-*-m* 'MANIFESTFILE'::
-Read the rpm file names from the specified 'MANIFESTFILE'. You can
-use *-* to read the manifest from standard input.
-
-*-0*::
-Use a null byte as line terminator for manifest files instead of
-a newline. This is useful if the file names can contain newlines.
-See also the *-print0* option in *find*.
-
-See Also
---------
-pacman(8)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: archrepo2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "ARCHREPO2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-archrepo2solv \- convert files in Arch repository format into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBarchrepo2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The archrepo2solv tool reads Arch Linux repository data (\fBcore\&.db\fR) from stdin, and writes it as solv file to standard output\&.
-.PP
-\fB\-l\fR \fIDATABASEDIR\fR
-.RS 4
-Instead of reading from standard input, scan the specified directory for package meta files\&. Set
-\fIDATABASEDIR\fR
-to
-\fB/var/lib/pacman/local\fR
-to scan the installed packages\&.
-.RE
-.SH "SEE ALSO"
-.sp
-pacman(8)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-archrepo2solv(1)
-================
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-archrepo2solv - convert files in Arch repository format into a solv file
-
-Synopsis
---------
-*archrepo2solv* ['OPTIONS']
-
-Description
------------
-The archrepo2solv tool reads Arch Linux repository data (*core.db*) from stdin,
-and writes it as solv file to standard output.
-
-*-l* 'DATABASEDIR'::
-Instead of reading from standard input, scan the specified directory for
-package meta files. Set 'DATABASEDIR' to */var/lib/pacman/local* to
-scan the installed packages.
-
-See Also
---------
-pacman(8)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: comps2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "COMPS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-comps2solv \- convert rpm\-md comps\&.xml file into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBcomps2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The comps\&.xml file is Fedora\(cqs way to implement package groups\&. The comps2solv tool reads the comps xml file from stdin and writes the parsed data as solv file to standard output\&. The parser will create \fBgroup:\fR and \fBcategory:\fR pseudo packages for each comps entry\&.
-.SH "SEE ALSO"
-.sp
-mergesolv(1), createrepo(8)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-comps2solv(1)
-=============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-comps2solv - convert rpm-md comps.xml file into a solv file
-
-Synopsis
---------
-*comps2solv* ['OPTIONS']
-
-Description
------------
-The comps.xml file is Fedora's way to implement package groups.
-The comps2solv tool reads the comps xml file from stdin and
-writes the parsed data as solv file to standard output. The
-parser will create *group:* and *category:* pseudo packages
-for each comps entry.
-
-See Also
---------
-mergesolv(1), createrepo(8)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: deb2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "DEB2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-deb2solv \- convert one or more Debian package files into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBdeb2solv\fR [\fIOPTIONS\fR] \fIPKG1\&.deb\fR \&...
-.SH "DESCRIPTION"
-.sp
-The deb2solv tool converts the meta data from one or more Debian packages into the solv file written to standard output\&.
-.PP
-\fB\-m\fR \fIMANIFESTFILE\fR
-.RS 4
-Read the rpm file names from the specified
-\fIMANIFESTFILE\fR\&. You can use
-\fB\-\fR
-to read the manifest from standard input\&.
-.RE
-.PP
-\fB\-0\fR
-.RS 4
-Use a null byte as line terminator for manifest files instead of a newline\&. This is useful if the file names can contain newlines\&. See also the
-\fB\-print0\fR
-option in
-\fBfind\fR\&.
-.RE
-.SH "SEE ALSO"
-.sp
-deb(5), dpkg\-deb(1)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-deb2solv(1)
-============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-deb2solv - convert one or more Debian package files into a solv file
-
-Synopsis
---------
-*deb2solv* ['OPTIONS'] 'PKG1.deb' ...
-
-Description
------------
-The deb2solv tool converts the meta data from one or more
-Debian packages into the solv file written to standard output.
-
-*-m* 'MANIFESTFILE'::
-Read the rpm file names from the specified 'MANIFESTFILE'. You can
-use *-* to read the manifest from standard input.
-
-*-0*::
-Use a null byte as line terminator for manifest files instead of
-a newline. This is useful if the file names can contain newlines.
-See also the *-print0* option in *find*.
-
-See Also
---------
-deb(5), dpkg-deb(1)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: deltainfoxml2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "DELTAINFOXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-deltainfoxml2solv \- convert rpm\-md\*(Aqs deltainfo format into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBdeltainfoxml2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The deltainfoxml2solv tool reads rpm\-md\(cqs \fBdeltainfo\&.xml\fR data from stdin, and writes it as solv file to standard output\&. Some distributions name the input \fBprestodelta\&.xml\fR instead\&. Each delta rpm element is converted and added as \fBrepository:deltainfo\fR element to the meta section of the solv file\&.
-.SH "SEE ALSO"
-.sp
-mergesolv(1), createrepo(8)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-deltainfoxml2solv(1)
-====================
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-deltainfoxml2solv - convert rpm-md's deltainfo format into a solv file
-
-Synopsis
---------
-*deltainfoxml2solv* ['OPTIONS']
-
-Description
------------
-The deltainfoxml2solv tool reads rpm-md's *deltainfo.xml* data from stdin,
-and writes it as solv file to standard output. Some distributions name
-the input *prestodelta.xml* instead. Each delta rpm element is converted
-and added as *repository:deltainfo* element to the meta section of the
-solv file.
-
-See Also
---------
-mergesolv(1), createrepo(8)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: dumpsolv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "DUMPSOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-dumpsolv \- print a solv file into a human readable format
-.SH "SYNOPSIS"
-.sp
-\fBdumpsolv\fR [\fIOPTIONS\fR] [\fIFILE\&.solv\fR]
-.SH "DESCRIPTION"
-.sp
-The dumpsolv tool reads a solv files and writes its contents to standard output\&. If no input file is given, it reads the solv file from standard input\&.
-.PP
-\fB\-j\fR
-.RS 4
-Write the contents in JSON format\&.
-.RE
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-dumpsolv(1)
-===========
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-dumpsolv - print a solv file into a human readable format
-
-Synopsis
---------
-*dumpsolv* ['OPTIONS'] ['FILE.solv']
-
-Description
------------
-The dumpsolv tool reads a solv files and writes its contents
-to standard output. If no input file is given, it reads the
-solv file from standard input.
-
-*-j*::
-Write the contents in JSON format.
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-[blockdef-listing]
-xcode-style=template="verseblock",presubs=(),postsubs=("callouts",),filter="filters/xcode.pl {basebackend}"
-
-[paradef-xcode]
-delimiter=(?s)^(?P<text>\s+.*)
-template=verseblock
-subs=verbatim
-filter=filters/xcode.pl {basebackend}
-
-[paradef-literal]
-delimiter=(?s)^(?P<text>\s{1,7}\S.*)
-
+++ /dev/null
-#!/usr/bin/perl
-
-die("I only understand docbook\n") unless @ARGV && $ARGV[0] eq 'docbook';
-
-#my $ii = '//';
-#my $io = '//';
-
-#my $si = '**';
-#my $so = '**';
-
-my $ii = '<emphasis>';
-my $io = '</emphasis>';
-
-my $si = '<emphasis role="strong">';
-my $so = '</emphasis>';
-
-while(<STDIN>) {
- chomp;
- my $in = '';
- my $out = '';
- s/^\s+//;
- s/\s+$//;
- if (/^(.*)(\s*\/\*.*?\*\/\s*?)$/) {
- $out = $2;
- $_ = $1;
- }
- if (/^(my\s+)(.*?)$/) {
- $in = $1;
- $_ = $2;
- }
- if (/(?<!\>);$/) {
- $out = ";$out";
- chop $_;
- }
- if (!/^[a-zA-Z0-9_]+$/) {
- $_ = " $_";
- $_ = "$_ ";
- if (s/^ TCL +/ /) {
- s/(\$[a-zA-Z_][a-zA-Z0-9_:]*)/<-S><I>$1<-I><S>/g;
- } else {
- s/(?<=[^a-zA-Z_\&:\.\'\";])(?!solv\W|Solv\W|Pool\W)([\$\@a-zA-Z_][a-zA-Z0-9_]*)(?=[^a-zA-Z0-9_\(;\[])(?!::)(?! [^=])/<-S><I>$1<-I><S>/g;
- }
- # fixup for perl bare words
- s/{<-S><I>([a-zA-Z_][a-zA-Z0-9]*)<-I><S>}/{$1}/g;
- # fixup for callbackfunctions
- s/\\(&[a-zA-Z_]+)/\\<-S><I>$1<-I><S>/;
- # fixup for stringification
- s/\$<-S><I>/<-S><I>\$/g;
- # fixup for %d
- s/%<-S><I>d<-I><S>\"/%d\"/;
- s/%<-S><I>d<-I><S>\\<-S><I>n<-I><S>/%d\\n/;
- # iterators
- s/^ //;
- s/ $//;
- s/^(for (?:my )?)(\S+) /$1<-S><I>$2<-I><S> /;
- }
- $_ = "<S>$_<-S>";
- s/<S>(\s*)<-S>/$1/g;
- s/<-S>(\s*)<S>/$1/g;
- s/<I>(\s*)<-I>/$1/g;
- s/<-I>(\s*)<I>/$1/g;
- s/<S>(\s+)/$1<S>/g;
- s/(\s+)<-S>/<-S>$1/g;
- s/<I>(\s+)/$1<I>/g;
- s/(\s+)<-I>/<-I>$1/g;
- s/<S>/$si/g;
- s/<-S>/$so/g;
- s/<I>/$ii/g;
- s/<-I>/$io/g;
- print "$in$_$out\n";
-}
+++ /dev/null
-'\" t
-.\" Title: helix2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 12/14/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "HELIX2SOLV" "1" "12/14/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-helix2solv \- convert legacy helixcode format into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBhelix2solv\fR
-.SH "DESCRIPTION"
-.sp
-The helix format was a metadata format used in the RedCarpet package manager\&. It\(cqs still used in libzypp testcases\&. The helix2solv tool reads data in helix format from standard input and writes it in solv file format to standard output\&.
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-helix2solv(1)
-=============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-helix2solv - convert legacy helixcode format into a solv file
-
-Synopsis
---------
-*helix2solv*
-
-Description
------------
-The helix format was a metadata format used in the RedCarpet
-package manager. It's still used in libzypp testcases.
-The helix2solv tool reads data in helix format from standard
-input and writes it in solv file format to standard output.
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: installcheck
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "INSTALLCHECK" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-installcheck \- find out which packages cannot be installed
-.SH "SYNOPSIS"
-.sp
-\fBinstallcheck\fR \fIARCH\fR \fIREPO1\fR \fIREPO2\fR\&... \fB\-\-nocheck\fR \fINREPO1\fR \fINREPO2\fR\&...
-.SH "DESCRIPTION"
-.sp
-The installcheck tool checks if all packages in \fIREPO1\fR\&...\fIREPON\fR are installable\&. A package is installable if there is a set of packages from the repositories that satisfies its dependencies\&. The repositories after the \fB\-\-nocheck\fR option are only used for dependency resolving, but the tool does not check if the packages in them are installable\&.
-.sp
-A Repository can be a solv file, a rpmmd \fBprimary\&.xml\&.gz\fR file, a SUSE \fBpackages\fR or \fBpackages\&.gz\fR file, or a Debian \fBPackages\fR or \fBPackages\&.gz\fR file\&.
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-installcheck(1)
-===============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-installcheck - find out which packages cannot be installed
-
-Synopsis
---------
-*installcheck* 'ARCH' 'REPO1' 'REPO2'... *--nocheck* 'NREPO1' 'NREPO2'...
-
-Description
------------
-The installcheck tool checks if all packages in 'REPO1'...'REPON' are
-installable. A package is installable if there is a set of packages
-from the repositories that satisfies its dependencies. The repositories
-after the *--nocheck* option are only used for dependency resolving,
-but the tool does not check if the packages in them are installable.
-
-A Repository can be a solv file, a rpmmd *primary.xml.gz* file, a SUSE
-*packages* or *packages.gz* file, or a Debian *Packages* or *Packages.gz*
-file.
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: Libsolv-Bindings
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 12/14/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "LIBSOLV\-BINDINGS" "3" "12/14/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-libsolv-bindings \- access libsolv from perl/python/ruby
-.SH "DESCRIPTION"
-.sp
-Libsolv\(cqs language bindings offer an abstract, object orientated interface to the library\&. The supported languages are currently perl, python, and ruby\&. All example code (except in the specifics sections, of course) lists first the \(lqC\-ish\(rq interface, then the syntax for perl, python, and ruby (in that order)\&.
-.SH "PERL SPECIFICS"
-.sp
-Libsolv\(cqs perl bindings can be loaded with the following statement:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBuse solv\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Objects are either created by calling the new() method on a class or they are returned by calling methods on other objects\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-my \fI$pool\fR \fB= solv::Pool\->new()\fR;
-my \fI$repo\fR \fB=\fR \fI$pool\fR\fB\->add_repo("my_first_repo")\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Swig encapsulates all objects as tied hashes, thus the attributes can be accessed by treating the object as standard hash reference:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fI$pool\fR\fB\->{appdata} = 42\fR;
-\fBprintf "appdata is %d\en",\fR \fI$pool\fR\fB\->{appdata}\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-A special exception to this are iterator objects, they are encapsulated as tied arrays so that it is possible to iterate with a for() statement:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-my \fI$iter\fR \fB=\fR \fI$pool\fR\fB\->solvables_iter()\fR;
-\fBfor my\fR \fI$solvable\fR \fB(\fR\fI@$iter\fR\fB) { \&.\&.\&. }\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-As a downside of this approach, iterator objects cannot have attributes\&.
-.sp
-If an array needs to be passed to a method it is usually done by reference, if a method returns an array it returns it on the stack:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-my \fI@problems\fR \fB=\fR \fI$solver\fR\fB\->solve(\e\fR\fI@jobs\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Due to a bug in swig, stringification does not work for libsolv\(cqs objects\&. Instead, you have to call the object\(cqs str() method\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBprint\fR \fI$dep\fR\fB\->str() \&. "\e\fR\fIn\fR\fB"\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Swig implements all constants as numeric variables (instead of the more natural constant subs), so don\(cqt forget the leading \(lq$\(rq when accessing a constant\&. Also do not forget to prepend the namespace of the constant:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fI$pool\fR\fB\->set_flag($solv::Pool::POOL_FLAG_OBSOLETEUSESCOLORS, 1)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.SH "PYTHON SPECIFICS"
-.sp
-The python bindings can be loaded with:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBimport solv\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Objects are either created by calling the constructor method for a class or they are returned by calling methods on other objects\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIpool\fR \fB= solv\&.Pool()\fR
-\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.add_repo("my_first_repo")\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Attributes can be accessed as usual:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIpool\fR\fB\&.appdata = 42\fR
-\fBprint "appdata is %d" % (\fR\fIpool\fR\fB\&.appdata)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterators also work as expected:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBfor\fR \fIsolvable\fR \fBin\fR \fIpool\fR\fB\&.solvables_iter():\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Arrays are passed and returned as list objects:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIjobs\fR \fB= []\fR
-\fIproblems\fR \fB=\fR \fIsolver\fR\fB\&.solve(\fR\fIjobs\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The bindings define stringification for many classes, some also have a \fIrepr\fR method to ease debugging\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBprint\fR \fIdep\fR
-\fBprint repr(\fR\fIrepo\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Constants are attributes of the classes:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIpool\fR\fB\&.set_flag(solv\&.Pool\&.POOL_FLAG_OBSOLETEUSESCOLORS, 1)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.SH "RUBY SPECIFICS"
-.sp
-The ruby bindings can be loaded with:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBrequire \*(Aqsolv\*(Aq\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Objects are either created by calling the new method on a class or they are returned by calling methods on other objects\&. Note that all classes start with an uppercase letter in ruby, so the class is called \(lqSolv\(rq\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIpool\fR \fB= Solv::Pool\&.new\fR
-\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.add_repo("my_first_repo")\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Attributes can be accessed as usual:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIpool\fR\fB\&.appdata = 42\fR
-\fBputs "appdata is #{\fR\fIpool\fR\fB\&.appdata}"\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterators also work as expected:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBfor\fR \fIsolvable\fR \fBin\fR \fIpool\fR\fB\&.solvables_iter() do \&.\&.\&.\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Arrays are passed and returned as array objects:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIjobs\fR \fB= []\fR
-\fIproblems\fR \fB=\fR \fIsolver\fR\fB\&.solve(\fR\fIjobs\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Most classes define a to_s method, so objects can be easily stringified\&. Many also define an inspect() method\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBputs\fR \fIdep\fR
-\fBputs\fR \fIrepo\fR\fB\&.inspect\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Constants live in the namespace of the class they belong to:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fIpool\fR\fB\&.set_flag(Solv::Pool::POOL_FLAG_OBSOLETEUSESCOLORS, 1)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Note that boolean methods have an added trailing \(lq?\(rq, to be consistent with other ruby modules:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBputs "empty" if\fR \fIrepo\fR\fB\&.isempty?\fR
-.fi
-.if n \{\
-.RE
-.\}
-.SH "TCL SPECIFICS"
-.sp
-Libsolv\(cqs tcl bindings can be loaded with the following statement:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBpackage require solv\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Objects are either created by calling class name prefixed with \(lqnew_\(rq, or they are returned by calling methods on other objects\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBset pool [solv::new_Pool]\fR
-\fBset repo [\fR\fI$pool\fR \fBadd_repo "my_first_repo"]\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Swig provides a \(lqcget\(rq method to read object attributes, and a \(lqconfigure\(rq method to write them:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fI$pool\fR \fBconfigure \-appdata 42\fR
-\fBputs "appdata is [\fR\fI$pool\fR \fBcget \-appdata]"\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The tcl bindings provide a little helper to work with iterators in a foreach style:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBset iter [\fR\fI$pool\fR \fBsolvables_iter]\fR
-\fBsolv::iter s\fR \fI$iter\fR \fB{ \&.\&.\&. }\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-libsolv\(cqs arrays are mapped to tcl\(cqs lists:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBset jobs [list\fR \fI$job1 $job2\fR\fB]\fR
-\fBset problems [\fR\fI$solver\fR \fBsolve\fR \fI$jobs\fR\fB]\fR
-\fBputs "We have [llength\fR \fI$problems\fR\fB] problems\&.\&.\&."\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Stringification is done by calling the object\(cqs \(lqstr\(rq method\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBputs [\fR\fI$dep\fR \fBstr]\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-There is one exception: you have to use \(lqstringify\(rq for Datamatch objects, as swig reports a clash with the \(lqstr\(rq attribute\&. Some objects also support a \(lq==\(rq method for equality tests, and a \(lq!=\(rq method\&.
-.sp
-Swig implements all constants as numeric variables, constants belonging to a libsolv class are prefixed with the class name:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fI$pool\fR \fBset_flag\fR \fI$solv::Pool_POOL_FLAG_OBSOLETEUSESCOLORS\fR \fB1\fR
-\fBputs [\fR\fI$solvable\fR \fBlookup_str\fR \fI$solv::SOLVABLE_SUMMARY\fR\fB]\fR
-.fi
-.if n \{\
-.RE
-.\}
-.SH "THE SOLV CLASS"
-.sp
-This is the main namespace of the library, you cannot create objects of this type but it contains some useful constants\&.
-.SS "CONSTANTS"
-.sp
-Relational flag constants, the first three can be or\-ed together
-.PP
-\fBREL_LT\fR
-.RS 4
-the \(lqless than\(rq bit
-.RE
-.PP
-\fBREL_EQ\fR
-.RS 4
-the \(lqequals to\(rq bit
-.RE
-.PP
-\fBREL_GT\fR
-.RS 4
-the \(lqgreater than\(rq bit
-.RE
-.PP
-\fBREL_ARCH\fR
-.RS 4
-used for relations that describe an extra architecture filter, the version part of the relation is interpreted as architecture\&.
-.RE
-.sp
-Special Solvable Ids
-.PP
-\fBSOLVID_META\fR
-.RS 4
-Access the meta section of a repository or repodata area\&. This is like an extra Solvable that has the Id SOLVID_META\&.
-.RE
-.PP
-\fBSOLVID_POS\fR
-.RS 4
-Use the data position stored inside of the pool instead of accessing some solvable by Id\&. The bindings have the Datapos objects as an abstraction mechanism, so you do not need this constant\&.
-.RE
-.sp
-Constant string Ids
-.PP
-\fBID_NULL\fR
-.RS 4
-Always zero
-.RE
-.PP
-\fBID_EMPTY\fR
-.RS 4
-Always one, describes the empty string
-.RE
-.PP
-\fBSOLVABLE_NAME\fR
-.RS 4
-The keyname Id of the name of the solvable\&.
-.RE
-.PP
-\fB\&...\fR
-.RS 4
-see the libsolv\-constantids manpage for a list of fixed Ids\&.
-.RE
-.SH "THE POOL CLASS"
-.sp
-The pool is libsolv\(cqs central resource manager\&. A pool consists of Solvables, Repositories, Dependencies, each indexed by Ids\&.
-.SS "CLASS METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *Pool()\fR
-my \fI$pool\fR \fB= solv::Pool\->new()\fR;
-\fIpool\fR \fB= solv\&.Pool()\fR
-\fIpool\fR \fB= Solv::Pool\&.new()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a new pool instance\&. In most cases you just need one pool\&. Note that the returned object "owns" the pool, i\&.e\&. if the object is freed, the pool is also freed\&. You can use the disown method to break this ownership relation\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid *appdata;\fR /* read/write */
-\fI$pool\fR\fB\->{appdata}\fR
-\fIpool\fR\fB\&.appdata\fR
-\fIpool\fR\fB\&.appdata\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Application specific data that may be used in any way by the code using the pool\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable solvables[];\fR /* read only */
-my \fI$solvable\fR \fB=\fR \fI$pool\fR\fB\->{solvables}\->[\fR\fI$solvid\fR\fB]\fR;
-\fIsolvable\fR \fB=\fR \fIpool\fR\fB\&.solvables[\fR\fIsolvid\fR\fB]\fR
-\fIsolvable\fR \fB=\fR \fIpool\fR\fB\&.solvables[\fR\fIsolvid\fR\fB]\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Look up a Solvable by its id\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepo repos[];\fR /* read only */
-my \fI$repo\fR \fB=\fR \fI$pool\fR\fB\->{repos}\->[\fR\fI$repoid\fR\fB]\fR;
-\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.repos[\fR\fIrepoid\fR\fB]\fR
-\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.repos[\fR\fIrepoid\fR\fB]\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Look up a Repository by its id\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepo *installed;\fR /* read/write */
-\fI$pool\fR\fB\->{installed} =\fR \fI$repo\fR;
-\fIpool\fR\fB\&.installed =\fR \fIrepo\fR
-\fIpool\fR\fB\&.installed =\fR \fIrepo\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Define which repository contains all the installed packages\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *errstr;\fR /* read only */
-my \fI$err\fR \fB=\fR \fI$pool\fR\fB\->{errstr}\fR;
-\fIerr\fR \fB=\fR \fIpool\fR\fB\&.errstr\fR
-\fIerr\fR \fB=\fR \fIpool\fR\fB\&.errstr\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the last error string that was stored in the pool\&.
-.SS "CONSTANTS"
-.PP
-\fBPOOL_FLAG_PROMOTEEPOCH\fR
-.RS 4
-Promote the epoch of the providing dependency to the requesting dependency if it does not contain an epoch\&. Used at some time in old rpm versions, modern systems should never need this\&.
-.RE
-.PP
-\fBPOOL_FLAG_FORBIDSELFCONFLICTS\fR
-.RS 4
-Disallow the installation of packages that conflict with themselves\&. Debian always allows self\-conflicting packages, rpm used to forbid them but switched to also allowing them recently\&.
-.RE
-.PP
-\fBPOOL_FLAG_OBSOLETEUSESPROVIDES\fR
-.RS 4
-Make obsolete type dependency match against provides instead of just the name and version of packages\&. Very old versions of rpm used the name/version, then it got switched to provides and later switched back again to just name/version\&.
-.RE
-.PP
-\fBPOOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES\fR
-.RS 4
-An implicit obsoletes is the internal mechanism to remove the old package on an update\&. The default is to remove all packages with the same name, rpm\-5 switched to also removing packages providing the same name\&.
-.RE
-.PP
-\fBPOOL_FLAG_OBSOLETEUSESCOLORS\fR
-.RS 4
-Rpm\(cqs multilib implementation (used in RedHat and Fedora) distinguishes between 32bit and 64bit packages (the terminology is that they have a different color)\&. If obsoleteusescolors is set, packages with different colors will not obsolete each other\&.
-.RE
-.PP
-\fBPOOL_FLAG_IMPLICITOBSOLETEUSESCOLORS\fR
-.RS 4
-Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if packages of the same name can be installed in parallel\&. For current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true (this is the default if FEDORA is defined when libsolv is compiled)\&.
-.RE
-.PP
-\fBPOOL_FLAG_NOINSTALLEDOBSOLETES\fR
-.RS 4
-New versions of rpm consider the obsoletes of installed packages when checking for dependency, thus you may not install a package that is obsoleted by some other installed package, unless you also erase the other package\&.
-.RE
-.PP
-\fBPOOL_FLAG_HAVEDISTEPOCH\fR
-.RS 4
-Mandriva added a new field called distepoch that gets checked in version comparison if the epoch/version/release of two packages are the same\&.
-.RE
-.PP
-\fBPOOL_FLAG_NOOBSOLETESMULTIVERSION\fR
-.RS 4
-If a package is installed in multiversionmode, rpm used to ignore both the implicit obsoletes and the obsolete dependency of a package\&. This was changed to ignoring just the implicit obsoletes, thus you may install multiple versions of the same name, but obsoleted packages still get removed\&.
-.RE
-.PP
-\fBPOOL_FLAG_ADDFILEPROVIDESFILTERED\fR
-.RS 4
-Make the addfileprovides method only add files from the standard locations (i\&.e\&. the \(lqbin\(rq and \(lqetc\(rq directories)\&. This is useful if you have only few packages that use non\-standard file dependencies, but you still want the fast speed that addfileprovides() generates\&.
-.RE
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid free()\fR
-\fI$pool\fR\fB\->free()\fR;
-\fIpool\fR\fB\&.free()\fR
-\fIpool\fR\fB\&.free()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Force a free of the pool\&. After this call, you must not access any object that still references the pool\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid disown()\fR
-\fI$pool\fR\fB\->disown()\fR;
-\fIpool\fR\fB\&.disown()\fR
-\fIpool\fR\fB\&.disown()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Break the ownership relation between the binding object and the pool\&. After this call, the pool will not get freed even if the object goes out of scope\&. This also means that you must manually call the free method to free the pool data\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid setdebuglevel(int\fR \fIlevel\fR\fB)\fR
-\fI$pool\fR\fB\->setdebuglevel(\fR\fI$level\fR\fB)\fR;
-\fIpool\fR\fB\&.setdebuglevel(\fR\fIlevel\fR\fB)\fR
-\fIpool\fR\fB\&.setdebuglevel(\fR\fIlevel\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the debug level\&. A value of zero means no debug output, the higher the value, the more output is generated\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint set_flag(int\fR \fIflag\fR\fB, int\fR \fIvalue\fR\fB)\fR
-my \fI$oldvalue\fR \fB=\fR \fI$pool\fR\fB\->set_flag(\fR\fI$flag\fR\fB,\fR \fI$value\fR\fB)\fR;
-\fIoldvalue\fR \fB=\fR \fIpool\fR\fB\&.set_flag(\fR\fIflag\fR\fB,\fR \fIvalue\fR\fB)\fR
-\fIoldvalue\fR \fB=\fR \fIpool\fR\fB\&.set_flag(\fR\fIflag\fR\fB,\fR \fIvalue\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint get_flag(int\fR \fIflag\fR\fB)\fR
-my \fI$value\fR \fB=\fR \fI$pool\fR\fB\->get_flag(\fR\fI$flag\fR\fB)\fR;
-\fIvalue\fR \fB=\fR \fIpool\fR\fB\&.get_flag(\fR\fIflag\fR\fB)\fR
-\fIvalue\fR \fB=\fR \fIpool\fR\fB\&.get_flag(\fR\fIflag\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set/get a pool specific flag\&. The flags define how the system works, e\&.g\&. how the package manager treats obsoletes\&. The default flags should be sane for most applications, but in some cases you may want to tweak a flag, for example if you want to solv package dependencies for some other system than yours\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid set_rootdir(const char *\fR\fIrootdir\fR\fB)\fR
-\fI$pool\fR\fB\->set_rootdir(\fR\fIrootdir\fR\fB)\fR;
-\fIpool\fR\fB\&.set_rootdir(\fR\fIrootdir\fR\fB)\fR
-\fIpool\fR\fB\&.set_rootdir(\fR\fIrootdir\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *get_rootdir()\fR
-my \fI$rootdir\fR \fB=\fR \fI$pool\fR\fB\->get_rootdir()\fR;
-\fIrootdir\fR \fB=\fR \fIpool\fR\fB\&.get_rootdir()\fR
-\fIrootdir\fR \fB=\fR \fIpool\fR\fB\&.get_rootdir()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set/get the rootdir to use\&. This is useful if you want package management to work only in some directory, for example if you want to setup a chroot jail\&. Note that the rootdir will only be prepended to file paths if the \fBREPO_USE_ROOTDIR\fR flag is used\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid setarch(const char *\fR\fIarch\fR \fB= 0)\fR
-\fI$pool\fR\fB\->setarch()\fR;
-\fIpool\fR\fB\&.setarch()\fR
-\fIpool\fR\fB\&.setarch()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the architecture for your system\&. The architecture is used to determine which packages are installable\&. It defaults to the result of \(lquname \-m\(rq\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepo add_repo(const char *\fR\fIname\fR\fB)\fR
-\fI$repo\fR \fB=\fR \fI$pool\fR\fB\->add_repo(\fR\fI$name\fR\fB)\fR;
-\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.add_repo(\fR\fIname\fR\fB)\fR
-\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.add_repo(\fR\fIname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a Repository with the specified name to the pool\&. The repository is empty on creation, use the repository methods to populate it with packages\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepoiterator repos_iter()\fR
-\fBfor my\fR \fI$repo\fR \fB(\fR\fI@\fR\fB{\fR\fI$pool\fR\fB\->repos_iter()})\fR
-\fBfor\fR \fIrepo\fR \fBin\fR \fIpool\fR\fB\&.repos_iter():\fR
-\fBfor\fR \fIrepo\fR \fBin\fR \fIpool\fR\fB\&.repos_iter()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterate over the existing repositories\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvableiterator solvables_iter()\fR
-\fBfor my\fR \fI$solvable\fR \fB(\fR\fI@\fR\fB{\fR\fI$pool\fR\fB\->solvables_iter()})\fR
-\fBfor\fR \fIsolvable\fR \fBin\fR \fIpool\fR\fB\&.solvables_iter():\fR
-\fBfor\fR \fIsolvable\fR \fBin\fR \fIpool\fR\fB\&.solvables_iter()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterate over the existing solvables\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDep Dep(const char *\fR\fIstr\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
-my \fI$dep\fR \fB=\fR \fI$pool\fR\fB\->Dep(\fR\fI$string\fR\fB)\fR;
-\fIdep\fR \fB=\fR \fIpool\fR\fB\&.Dep(\fR\fIstring\fR\fB)\fR
-\fIdep\fR \fB=\fR \fIpool\fR\fB\&.Dep(\fR\fIstring\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create an object describing a string or dependency\&. If the string is currently not in the pool and \fIcreate\fR is false, \fBundef\fR/\fBNone\fR/\fBnil\fR is returned\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid addfileprovides()\fR
-\fI$pool\fR\fB\->addfileprovides()\fR;
-\fIpool\fR\fB\&.addfileprovides()\fR
-\fIpool\fR\fB\&.addfileprovides()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId *addfileprovides_queue()\fR
-my \fI@ids\fR \fB=\fR \fI$pool\fR\fB\->addfileprovides_queue()\fR;
-\fIids\fR \fB=\fR \fIpool\fR\fB\&.addfileprovides_queue()\fR
-\fIids\fR \fB=\fR \fIpool\fR\fB\&.addfileprovides_queue()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Some package managers like rpm allow dependencies on files contained in other packages\&. To allow libsolv to deal with those dependencies in an efficient way, you need to call the addfileprovides method after creating and reading all repositories\&. This method will scan all dependency for file names and then scan all packages for matching files\&. If a filename has been matched, it will be added to the provides list of the corresponding package\&. The addfileprovides_queue variant works the same way but returns an array containing all file dependencies\&. This information can be stored in the meta section of the repositories to speed up the next time the repository is loaded and addfileprovides is called\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid createwhatprovides()\fR
-\fI$pool\fR\fB\->createwhatprovides()\fR;
-\fIpool\fR\fB\&.createwhatprovides()\fR
-\fIpool\fR\fB\&.createwhatprovides()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create the internal \(lqwhatprovides\(rq hash over all of the provides of all packages\&. This method must be called before doing any lookups on provides\&. It\(cqs encouraged to do it right after all repos are set up, usually right after the call to addfileprovides()\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *whatprovides(DepId\fR \fIdep\fR\fB)\fR
-my \fI@solvables\fR \fB=\fR \fI$pool\fR\fB\->whatprovides(\fR\fI$dep\fR\fB)\fR;
-\fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatprovides(\fR\fIdep\fR\fB)\fR
-\fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatprovides(\fR\fIdep\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return all solvables that provide the specified dependency\&. You can use either a Dep object or a simple Id as argument\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId *matchprovidingids(const char *\fR\fImatch\fR\fB, int\fR \fIflags\fR\fB)\fR
-my \fI@ids\fR \fB=\fR \fI$pool\fR\fB\->matchprovidingids(\fR\fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIids\fR \fB=\fR \fIpool\fR\fB\&.matchprovidingids(\fR\fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIids\fR \fB=\fR \fIpool\fR\fB\&.matchprovidingids(\fR\fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Search the names of all provides and return the ones matching the specified string\&. See the Dataiterator class for the allowed flags\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId towhatprovides(Id *\fR\fIids\fR\fB)\fR
-my \fI$offset\fR \fB=\fR \fI$pool\fR\fB\->towhatprovides(\e\fR\fI@ids\fR\fB)\fR;
-\fIoffset\fR \fB=\fR \fIpool\fR\fB\&.towhatprovides(\fR\fIids\fR\fB)\fR
-\fIoffset\fR \fB=\fR \fIpool\fR\fB\&.towhatprovides(\fR\fIids\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-\(lqInternalize\(rq an array containing Ids\&. The returned value can be used to create solver jobs working on a specific set of packages\&. See the Solver class for more information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool isknownarch(DepId\fR \fIid\fR\fB)\fR
-my \fI$bool\fR \fB=\fR \fI$pool\fR\fB\->isknownarch(\fR\fI$id\fR\fB)\fR;
-\fIbool\fR \fB=\fR \fIpool\fR\fB\&.isknownarch(\fR\fIid\fR\fB)\fR
-\fIbool\fR \fB=\fR \fIpool\fR\fB\&.isknownarch?(\fR\fIid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return true if the specified Id describes a known architecture\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolver Solver()\fR
-my \fI$solver\fR \fB=\fR \fI$pool\fR\fB\->Solver()\fR;
-\fIsolver\fR \fB=\fR \fIpool\fR\fB\&.Solver()\fR
-\fIsolver\fR \fB=\fR \fIpool\fR\fB\&.Solver()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a new solver object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBJob Job(int\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR
-my \fI$job\fR \fB=\fR \fI$pool\fR\fB\->Job(\fR\fI$how\fR\fB,\fR \fI$what\fR\fB)\fR;
-\fIjob\fR \fB=\fR \fIpool\fR\fB\&.Job(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
-\fIjob\fR \fB=\fR \fIpool\fR\fB\&.Job(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a new Job object\&. Kind of low level, in most cases you would use a Selection or Dep job constructor instead\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSelection Selection()\fR
-my \fI$sel\fR \fB=\fR \fI$pool\fR\fB\->Selection()\fR;
-\fIsel\fR \fB=\fR \fIpool\fR\fB\&.Selection()\fR
-\fIsel\fR \fB=\fR \fIpool\fR\fB\&.Selection()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create an empty selection\&. Useful as a starting point for merging other selections\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSelection Selection_all()\fR
-my \fI$sel\fR \fB=\fR \fI$pool\fR\fB\->Selection_all()\fR;
-\fIsel\fR \fB=\fR \fIpool\fR\fB\&.Selection_all()\fR
-\fIsel\fR \fB=\fR \fIpool\fR\fB\&.Selection_all()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a selection containing all packages\&. Useful as starting point for intersecting other selections or for update/distupgrade jobs\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSelection select(const char *\fR\fIname\fR\fB, int\fR \fIflags\fR\fB)\fR
-my \fI$sel\fR \fB=\fR \fI$pool\fR\fB\->select(\fR\fI$name\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIsel\fR \fB=\fR \fIpool\fR\fB\&.select(\fR\fIname\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIsel\fR \fB=\fR \fIpool\fR\fB\&.select(\fR\fIname\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a selection by matching packages against the specified string\&. See the Selection class for a list of flags and how to create solver jobs from a selection\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid setpooljobs(Jobs *\fR\fIjobs\fR\fB)\fR
-\fI$pool\fR\fB\->setpooljobs(\e\fR\fI@jobs\fR\fB)\fR;
-\fIpool\fR\fB\&.setpooljobs(\fR\fIjobs\fR\fB)\fR
-\fIpool\fR\fB\&.setpooljobs(\fR\fIjobs\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBJob *getpooljobs()\fR
-\fI@jobs\fR \fB=\fR \fI$pool\fR\fB\->getpooljobs()\fR;
-\fIjobs\fR \fB=\fR \fIpool\fR\fB\&.getpooljobs()\fR
-\fIjobs\fR \fB=\fR \fIpool\fR\fB\&.getpooljobs()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Get/Set fixed jobs stored in the pool\&. Those jobs are automatically appended to all solver jobs, they are meant for fixed configurations like which packages can be multiversion installed, which packages were userinstalled or must not be erased\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid set_loadcallback(Callable *\fR\fIcallback\fR\fB)\fR
-\fI$pool\fR\fB\->setloadcallback(\e\fR\fI&callbackfunction\fR\fB)\fR;
-\fIpool\fR\fB\&.setloadcallback(\fR\fIcallbackfunction\fR\fB)\fR
-\fIpool\fR\fB\&.setloadcallback { |\fR\fIrepodata\fR\fB| \&.\&.\&. }\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the callback function called when repository metadata needs to be loaded on demand\&. To make use of this feature, you need to create repodata stubs that tell the library which data is available but not loaded\&. If later on the data needs to be accessed, the callback function is called with a repodata argument\&. You can then load the data (maybe fetching it first from a remote server)\&. The callback should return true if the data has been made available\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-/* bindings only */
-\fI$pool\fR\fB\->appdata_disown()\fR
-\fIpool\fR\fB\&.appdata_disown()\fR
-\fIpool\fR\fB\&.appdata_disown()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Decrement the reference count of the appdata object\&. This can be used to break circular references (e\&.g\&. if the pool\(cqs appdata value points to some meta data structure that contains a pool handle)\&. If used incorrectly, this method can lead to application crashes, so beware\&. (This method is a no\-op for ruby and tcl\&.)
-.SS "DATA RETRIEVAL METHODS"
-.sp
-In the following functions, the \fIkeyname\fR argument describes what to retrieve\&. For the standard cases you can use the available Id constants\&. For example,
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB$solv::SOLVABLE_SUMMARY\fR
-\fBsolv\&.SOLVABLE_SUMMARY\fR
-\fBSolv::SOLVABLE_SUMMARY\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-selects the \(lqSummary\(rq entry of a solvable\&. The \fIsolvid\fR argument selects the desired solvable by Id\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *lookup_str(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI$string\fR \fB=\fR \fI$pool\fR\fB\->lookup_str(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fIstring\fR \fB=\fR \fIpool\fR\fB\&.lookup_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fIstring\fR \fB=\fR \fIpool\fR\fB\&.lookup_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId lookup_id(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI$id\fR \fB=\fR \fI$pool\fR\fB\->lookup_id(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fIid\fR \fB=\fR \fIpool\fR\fB\&.lookup_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fIid\fR \fB=\fR \fIpool\fR\fB\&.lookup_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBunsigned long long lookup_num(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInotfound\fR \fB= 0)\fR
-my \fI$num\fR \fB=\fR \fI$pool\fR\fB\->lookup_num(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fInum\fR \fB=\fR \fIpool\fR\fB\&.lookup_num(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fInum\fR \fB=\fR \fIpool\fR\fB\&.lookup_num(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool lookup_void(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI$bool\fR \fB=\fR \fI$pool\fR\fB\->lookup_void(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fIbool\fR \fB=\fR \fIpool\fR\fB\&.lookup_void(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fIbool\fR \fB=\fR \fIpool\fR\fB\&.lookup_void(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId *lookup_idarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI@ids\fR \fB=\fR \fI$pool\fR\fB\->lookup_idarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fIids\fR \fB=\fR \fIpool\fR\fB\&.lookup_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fIids\fR \fB=\fR \fIpool\fR\fB\&.lookup_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBChksum lookup_checksum(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI$chksum\fR \fB=\fR \fI$pool\fR\fB\->lookup_checksum(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fIchksum\fR \fB=\fR \fIpool\fR\fB\&.lookup_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fIchksum\fR \fB=\fR \fIpool\fR\fB\&.lookup_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Lookup functions\&. Return the data element stored in the specified solvable\&. You should probably use the methods of the Solvable class instead\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDataiterator Dataiterator(Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$di\fR \fB=\fR \fI$pool\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIdi\fR \fB=\fR \fIpool\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIdi\fR \fB=\fR \fIpool\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDataiterator Dataiterator_solvid(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$di\fR \fB=\fR \fI$pool\fR\fB\->Dataiterator(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIdi\fR \fB=\fR \fIpool\fR\fB\&.Dataiterator(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIdi\fR \fB=\fR \fIpool\fR\fB\&.Dataiterator(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterate over the matching data elements\&. See the Dataiterator class for more information\&. The Dataiterator method iterates over all solvables in the pool, whereas the Dataiterator_solvid only iterates over the specified solvable\&.
-.SS "ID METHODS"
-.sp
-The following methods deal with Ids, i\&.e\&. integers representing objects in the pool\&. They are considered \(lqlow level\(rq, in most cases you would not use them but instead the object orientated methods\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepo id2repo(Id\fR \fIid\fR\fB)\fR
-\fI$repo\fR \fB=\fR \fI$pool\fR\fB\->id2repo(\fR\fI$id\fR\fB)\fR;
-\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.id2repo(\fR\fIid\fR\fB)\fR
-\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.id2repo(\fR\fIid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Lookup an existing Repository by id\&. You can also do this by using the \fBrepos\fR attribute\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable id2solvable(Id\fR \fIid\fR\fB)\fR
-\fI$solvable\fR \fB=\fR \fI$pool\fR\fB\->id2solvable(\fR\fI$id\fR\fB)\fR;
-\fIsolvable\fR \fB=\fR \fIpool\fR\fB\&.id2solvable(\fR\fIid\fR\fB)\fR
-\fIsolvable\fR \fB=\fR \fIpool\fR\fB\&.id2solvable(\fR\fIid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Lookup an existing Repository by id\&. You can also do this by using the \fBsolvables\fR attribute\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *solvid2str(Id\fR \fIid\fR\fB)\fR
-my \fI$str\fR \fB=\fR \fI$pool\fR\fB\->solvid2str(\fR\fI$id\fR\fB)\fR;
-\fIstr\fR \fB=\fR \fIpool\fR\fB\&.solvid2str(\fR\fIid\fR\fB)\fR
-\fIstr\fR \fB=\fR \fIpool\fR\fB\&.solvid2str(\fR\fIid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a string describing the Solvable with the specified id\&. The string consists of the name, version, and architecture of the Solvable\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId str2id(const char *\fR\fIstr\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
-my \fI$id\fR \fB=\fR \fIpool\fR\fB\->str2id(\fR\fI$string\fR\fB)\fR;
-\fIid\fR \fB=\fR \fIpool\fR\fB\&.str2id(\fR\fIstring\fR\fB)\fR
-\fIid\fR \fB=\fR \fIpool\fR\fB\&.str2id(\fR\fIstring\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *id2str(Id\fR \fIid\fR\fB)\fR
-\fI$string\fR \fB=\fR \fIpool\fR\fB\->id2str(\fR\fI$id\fR\fB)\fR;
-\fIstring\fR \fB=\fR \fIpool\fR\fB\&.id2str(\fR\fIid\fR\fB)\fR
-\fIstring\fR \fB=\fR \fIpool\fR\fB\&.id2str(\fR\fIid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert a string into an Id and back\&. If the string is currently not in the pool and \fIcreate\fR is false, zero is returned\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId rel2id(Id\fR \fIname\fR\fB, Id\fR \fIevr\fR\fB, int\fR \fIflags\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
-my \fI$id\fR \fB=\fR \fIpool\fR\fB\->rel2id(\fR\fI$nameid\fR\fB,\fR \fI$evrid\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIid\fR \fB=\fR \fIpool\fR\fB\&.rel2id(\fR\fInameid\fR\fB,\fR \fIevrid\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIid\fR \fB=\fR \fIpool\fR\fB\&.rel2id(\fR\fInameid\fR\fB,\fR \fIevrid\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a \(lqrelational\(rq dependency\&. Such dependencies consist of a name part, the \fIflags\fR describing the relation, and a version part\&. The flags are:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB$solv::REL_EQ | $solv::REL_GT | $solv::REL_LT\fR
-\fBsolv\&.REL_EQ | solv\&.REL_GT | solv\&.REL_LT\fR
-\fBSolv::REL_EQ | Solv::REL_GT | Solv::REL_LT\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Thus, if you want a \(lq<=\(rq relation, you would use \fBREL_LT | REL_EQ\fR\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id2langid(Id\fR \fIid\fR\fB, const char *\fR\fIlang\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
-my \fI$id\fR \fB=\fR \fI$pool\fR\fB\->id2langid(\fR\fI$id\fR\fB,\fR \fI$language\fR\fB)\fR;
-\fIid\fR \fB=\fR \fIpool\fR\fB\&.id2langid(\fR\fIid\fR\fB,\fR \fIlanguage\fR\fB)\fR
-\fIid\fR \fB=\fR \fIpool\fR\fB\&.id2langid(\fR\fIid\fR\fB,\fR \fIlanguage\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a language specific Id from some other id\&. This function simply converts the id into a string, appends a dot and the specified language to the string and converts the result back into an Id\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *dep2str(Id\fR \fIid\fR\fB)\fR
-\fI$string\fR \fB=\fR \fIpool\fR\fB\->dep2str(\fR\fI$id\fR\fB)\fR;
-\fIstring\fR \fB=\fR \fIpool\fR\fB\&.dep2str(\fR\fIid\fR\fB)\fR
-\fIstring\fR \fB=\fR \fIpool\fR\fB\&.dep2str(\fR\fIid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert a dependency id into a string\&. If the id is just a string, this function has the same effect as id2str()\&. For relational dependencies, the result is the correct \(lqname relation evr\(rq string\&.
-.SH "THE DEPENDENCY CLASS"
-.sp
-The dependency class is an object orientated way to work with strings and dependencies\&. Internally, dependencies are represented as Ids, i\&.e\&. simple numbers\&. Dependency objects can be constructed by using the Pool\(cqs Dep() method\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool;\fR /* read only */
-\fI$dep\fR\fB\->{pool}\fR
-\fIdep\fR\fB\&.pool\fR
-\fIdep\fR\fB\&.pool\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back reference to the pool this dependency belongs to\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id;\fR /* read only */
-\fI$dep\fR\fB\->{id}\fR
-\fIdep\fR\fB\&.id\fR
-\fIdep\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The id of this dependency\&.
-.SH "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDep Rel(int\fR \fIflags\fR\fB, DepId\fR \fIevrid\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
-my \fI$reldep\fR \fB=\fR \fI$dep\fR\fB\->Rel(\fR\fI$flags\fR\fB,\fR \fI$evrdep\fR\fB)\fR;
-\fIreldep\fR \fB=\fR \fIdep\fR\fB\&.Rel(\fR\fIflags\fR\fB,\fR \fIevrdep\fR\fB)\fR
-\fIreldep\fR \fB=\fR \fIdep\fR\fB\&.Rel(\fR\fIflags\fR\fB,\fR \fIevrdep\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a relational dependency from to string dependencies and a flags argument\&. See the pool\(cqs rel2id method for a description of the flags\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSelection Selection_name(int\fR \fIsetflags\fR \fB= 0)\fR
-my \fI$sel\fR \fB=\fR \fI$dep\fR\fB\->Selection_name()\fR;
-\fIsel\fR \fB=\fR \fIdep\fR\fB\&.Selection_name()\fR
-\fIsel\fR \fB=\fR \fIdep\fR\fB\&.Selection_name()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a Selection from a dependency\&. The selection consists of all packages that have a name equal to the dependency\&. If the dependency is of a relational type, the packages version must also fulfill the dependency\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSelection Selection_provides(int\fR \fIsetflags\fR \fB= 0)\fR
-my \fI$sel\fR \fB=\fR \fI$dep\fR\fB\->Selection_provides()\fR;
-\fIsel\fR \fB=\fR \fIdep\fR\fB\&.Selection_provides()\fR
-\fIsel\fR \fB=\fR \fIdep\fR\fB\&.Selection_provides()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a Selection from a dependency\&. The selection consists of all packages that have at least one provides matching the dependency\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *str()\fR
-my \fI$str\fR \fB=\fR \fI$dep\fR\fB\->str()\fR;
-\fIstr\fR \fB=\fR \fI$dep\fR\fB\&.str()\fR
-\fIstr\fR \fB=\fR \fI$dep\fR\fB\&.str()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a string describing the dependency\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<stringification>\fR
-my \fI$str\fR \fB=\fR \fI$dep\fR\fB\->str\fR;
-\fIstr\fR \fB= str(\fR\fIdep\fR\fB)\fR
-\fIstr\fR \fB=\fR \fIdep\fR\fB\&.to_s\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Same as calling the str() method\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<equality>\fR
-\fBif (\fR\fI$dep1\fR \fB==\fR \fI$dep2\fR\fB)\fR
-\fBif\fR \fIdep1\fR \fB==\fR \fIdep2\fR\fB:\fR
-\fBif\fR \fIdep1\fR \fB==\fR \fIdep2\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The dependencies are equal if they are part of the same pool and have the same ids\&.
-.SH "THE REPOSITORY CLASS"
-.sp
-A Repository describes a group of packages, normally coming from the same source\&. Repositories are created by the Pool\(cqs add_repo() method\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool;\fR /* read only */
-\fI$repo\fR\fB\->{pool}\fR
-\fIrepo\fR\fB\&.pool\fR
-\fIrepo\fR\fB\&.pool\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back reference to the pool this dependency belongs to\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id;\fR /* read only */
-\fI$repo\fR\fB\->{id}\fR
-\fIrepo\fR\fB\&.id\fR
-\fIrepo\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The id of the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *name;\fR /* read/write */
-\fI$repo\fR\fB\->{name}\fR
-\fIrepo\fR\fB\&.name\fR
-\fIrepo\fR\fB\&.name\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The repositories name\&. To libsolv, the name is just a string with no specific meaning\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint priority;\fR /* read/write */
-\fI$repo\fR\fB\->{priority}\fR
-\fIrepo\fR\fB\&.priority\fR
-\fIrepo\fR\fB\&.priority\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The priority of the repository\&. A higher number means that packages of this repository will be chosen over other repositories, even if they have a greater package version\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint subpriority;\fR /* read/write */
-\fI$repo\fR\fB\->{subpriority}\fR
-\fIrepo\fR\fB\&.subpriority\fR
-\fIrepo\fR\fB\&.subpriority\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The sub\-priority of the repository\&. This value is compared when the priorities of two repositories are the same\&. It is useful to make the library prefer on\-disk repositories to remote ones\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint nsolvables;\fR /* read only */
-\fI$repo\fR\fB\->{nsolvables}\fR
-\fIrepo\fR\fB\&.nsolvables\fR
-\fIrepo\fR\fB\&.nsolvables\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The number of solvables in this repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid *appdata;\fR /* read/write */
-\fI$repo\fR\fB\->{appdata}\fR
-\fIrepo\fR\fB\&.appdata\fR
-\fIrepo\fR\fB\&.appdata\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Application specific data that may be used in any way by the code using the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDatapos *meta;\fR /* read only */
-\fI$repo\fR\fB\->{meta}\fR
-\fIrepo\fR\fB\&.meta\fR
-\fIrepo\fR\fB\&.meta\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a Datapos object of the repodata\(cqs metadata\&. You can use the lookup methods of the Datapos class to lookup metadata attributes, like the repository timestamp\&.
-.SS "CONSTANTS"
-.PP
-\fBREPO_REUSE_REPODATA\fR
-.RS 4
-Reuse the last repository data area (\(lqrepodata\(rq) instead of creating a new one\&.
-.RE
-.PP
-\fBREPO_NO_INTERNALIZE\fR
-.RS 4
-Do not internalize the added repository data\&. This is useful if you plan to add more data because internalization is a costly operation\&.
-.RE
-.PP
-\fBREPO_LOCALPOOL\fR
-.RS 4
-Use the repodata\(cqs pool for Id storage instead of the global pool\&. Useful if you don\(cqt want to pollute the global pool with many unneeded ids, like when storing the filelist\&.
-.RE
-.PP
-\fBREPO_USE_LOADING\fR
-.RS 4
-Use the repodata that is currently being loaded instead of creating a new one\&. This only makes sense if used in a load callback\&.
-.RE
-.PP
-\fBREPO_EXTEND_SOLVABLES\fR
-.RS 4
-Do not create new solvables for the new data, but match existing solvables and add the data to them\&. Repository metadata is often split into multiple parts, with one primary file describing all packages and other parts holding information that is normally not needed, like the changelog\&.
-.RE
-.PP
-\fBREPO_USE_ROOTDIR\fR
-.RS 4
-Prepend the pool\(cqs rootdir to the path when doing file operations\&.
-.RE
-.PP
-\fBREPO_NO_LOCATION\fR
-.RS 4
-Do not add a location element to the solvables\&. Useful if the solvables are not in the final position, so you can add the correct location later in your code\&.
-.RE
-.PP
-\fBSOLV_ADD_NO_STUBS\fR
-.RS 4
-Do not create stubs for repository parts that can be downloaded on demand\&.
-.RE
-.PP
-\fBSUSETAGS_RECORD_SHARES\fR
-.RS 4
-This is specific to the add_susetags() method\&. Susetags allows one to refer to already read packages to save disk space\&. If this data sharing needs to work over multiple calls to add_susetags, you need to specify this flag so that the share information is made available to subsequent calls\&.
-.RE
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid free(bool\fR \fIreuseids\fR \fB= 0)\fR
-\fI$repo\fR\fB\->free()\fR;
-\fIrepo\fR\fB\&.free()\fR
-\fIrepo\fR\fB\&.free()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Free the repository and all solvables it contains\&. If \fIreuseids\fR is set to true, the solvable ids and the repository id may be reused by the library when added new solvables\&. Thus you should leave it false if you are not sure that somebody holds a reference\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid empty(bool\fR \fIreuseids\fR \fB= 0)\fR
-\fI$repo\fR\fB\->empty()\fR;
-\fIrepo\fR\fB\&.empty()\fR
-\fIrepo\fR\fB\&.empty()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Free all the solvables in a repository\&. The repository will be empty after this call\&. See the free() method for the meaning of \fIreuseids\fR\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool isempty()\fR
-\fI$repo\fR\fB\->isempty()\fR
-\fIrepo\fR\fB\&.empty()\fR
-\fIrepo\fR\fB\&.empty?\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return true if there are no solvables in this repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid internalize()\fR
-\fI$repo\fR\fB\->internalize()\fR;
-\fIrepo\fR\fB\&.internalize()\fR
-\fIrepo\fR\fB\&.internalize()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Internalize added data\&. Data must be internalized before it is available to the lookup and data iterator functions\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool write(FILE *\fR\fIfp\fR\fB)\fR
-\fI$repo\fR\fB\->write(\fR\fI$fp\fR\fB)\fR
-\fIrepo\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Write a repo as a \(lqsolv\(rq file\&. These files can be read very fast and thus are a good way to cache repository data\&. Returns false if there was some error writing the file\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvableiterator solvables_iter()\fR
-\fBfor my\fR \fI$solvable\fR \fB(\fR\fI@\fR\fB{\fR\fI$repo\fR\fB\->solvables_iter()})\fR
-\fBfor\fR \fIsolvable\fR \fBin\fR \fIrepo\fR\fB\&.solvables_iter():\fR
-\fBfor\fR \fIsolvable\fR \fBin\fR \fIrepo\fR\fB\&.solvables_iter()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterate over all solvables in a repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepodata add_repodata(int\fR \fIflags\fR \fB= 0)\fR
-my \fI$repodata\fR \fB=\fR \fI$repo\fR\fB\->add_repodata()\fR;
-\fIrepodata\fR \fB=\fR \fIrepo\fR\fB\&.add_repodata()\fR
-\fIrepodata\fR \fB=\fR \fIrepo\fR\fB\&.add_repodata()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a new repodata area to the repository\&. This is normally automatically done by the repo_add methods, so you need this method only in very rare circumstances\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid create_stubs()\fR
-\fI$repo\fR\fB\->create_stubs()\fR;
-\fIrepo\fR\fB\&.create_stubs()\fR
-\fIrepo\fR\fB\&.create_stubs()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Calls the create_stubs() repodata method for the last repodata of the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool iscontiguous()\fR
-\fI$repo\fR\fB\->iscontiguous()\fR
-\fIrepo\fR\fB\&.iscontiguous()\fR
-\fIrepo\fR\fB\&.iscontiguous?\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return true if the solvables of this repository are all in a single block with no holes, i\&.e\&. they have consecutive ids\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepodata first_repodata()\fR
-my \fI$repodata\fR \fB=\fR \fI$repo\fR\fB\->first_repodata()\fR;
-\fIrepodata\fR \fB=\fR \fIrepo\fR\fB\&.first_repodata()\fR
-\fIrepodata\fR \fB=\fR \fIrepo\fR\fB\&.first_repodata()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Checks if all repodatas but the first repodata are extensions, and return the first repodata if this is the case\&. Useful if you want to do a store/retrieve sequence on the repository to reduce the memory using and enable paging, as this does not work if the repository contains multiple non\-extension repodata areas\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSelection Selection(int\fR \fIsetflags\fR \fB= 0)\fR
-my \fI$sel\fR \fB=\fR \fI$repo\fR\fB\->Selection()\fR;
-\fIsel\fR \fB=\fR \fIrepo\fR\fB\&.Selection()\fR
-\fIsel\fR \fB=\fR \fIrepo\fR\fB\&.Selection()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a Selection consisting of all packages in the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDataiterator Dataiterator(Id\fR \fIkey\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$di\fR \fB=\fR \fI$repo\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIdi\fR \fB=\fR \fIrepo\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIdi\fR \fB=\fR \fIrepo\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDataiterator Dataiterator_meta(Id\fR \fIkey\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$di\fR \fB=\fR \fI$repo\fR\fB\->Dataiterator_meta(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIdi\fR \fB=\fR \fIrepo\fR\fB\&.Dataiterator_meta(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIdi\fR \fB=\fR \fIrepo\fR\fB\&.Dataiterator_meta(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterate over the matching data elements in this repository\&. See the Dataiterator class for more information\&. The Dataiterator() method iterates over all solvables in a repository, whereas the Dataiterator_meta method only iterates over the repository\(cqs meta data\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<stringification>\fR
-my \fI$str\fR \fB=\fR \fI$repo\fR\fB\->str\fR;
-\fIstr\fR \fB= str(\fR\fIrepo\fR\fB)\fR
-\fIstr\fR \fB=\fR \fIrepo\fR\fB\&.to_s\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the name of the repository, or "Repo#<id>" if no name is set\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<equality>\fR
-\fBif (\fR\fI$repo1\fR \fB==\fR \fI$repo2\fR\fB)\fR
-\fBif\fR \fIrepo1\fR \fB==\fR \fIrepo2\fR\fB:\fR
-\fBif\fR \fIrepo1\fR \fB==\fR \fIrepo2\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Two repositories are equal if they belong to the same pool and have the same id\&.
-.SS "DATA ADD METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable add_solvable()\fR
-\fI$repo\fR\fB\->add_solvable()\fR;
-\fIrepo\fR\fB\&.add_solvable()\fR
-\fIrepo\fR\fB\&.add_solvable()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a single empty solvable to the repository\&. Returns a Solvable object, see the Solvable class for more information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_solv(const char *\fR\fIname\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_solv(\fR\fI$name\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_solv(\fR\fIname\fR\fB)\fR
-\fIrepo\fR\fB\&.add_solv(\fR\fIname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_solv(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_solv(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Read a \(lqsolv\(rq file and add its contents to the repository\&. These files can be written with the write() method and are normally used as fast cache for repository metadata\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_rpmdb(int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_rpmdb()\fR;
-\fIrepo\fR\fB\&.add_rpmdb()\fR
-\fIrepo\fR\fB\&.add_rpmdb()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_rpmdb_reffp(FILE *\fR\fIreffp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_rpmdb_reffp(\fR\fI$reffp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_rpmdb_reffp(\fR\fIreffp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_rpmdb_reffp(\fR\fIreffp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the contents of the rpm database to the repository\&. If a solv file containing an old version of the database is available, it can be passed as reffp to speed up reading\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable add_rpm(const char *\fR\fIfilename\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$solvable\fR \fB=\fR \fI$repo\fR\fB\->add_rpm(\fR\fI$filename\fR\fB)\fR;
-\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_rpm(\fR\fIfilename\fR\fB)\fR
-\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_rpm(\fR\fIfilename\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the metadata of a single rpm package to the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_rpmdb_pubkeys(int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_rpmdb_pubkeys()\fR;
-\fIrepo\fR\fB\&.add_rpmdb_pubkeys()\fR
-\fIrepo\fR\fB\&.add_rpmdb_pubkeys()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add all pubkeys contained in the rpm database to the repository\&. Note that newer rpm versions also allow to store the pubkeys in some directory instead of the rpm database\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable add_pubkey(const char *\fR\fIkeyfile\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$solvable\fR \fB=\fR \fI$repo\fR\fB\->add_pubkey(\fR\fI$keyfile\fR\fB)\fR;
-\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_pubkey(\fR\fIkeyfile\fR\fB)\fR
-\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_pubkey(\fR\fIkeyfile\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a pubkey from a file to the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_rpmmd(FILE *\fR\fIfp\fR\fB, const char *\fR\fIlanguage\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_rpmmd(\fR\fI$fp\fR\fB,\fR \fIundef\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_rpmmd(\fR\fIfp\fR\fB,\fR \fINone\fR\fB)\fR
-\fIrepo\fR\fB\&.add_rpmmd(\fR\fIfp\fR\fB,\fR \fInil\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add metadata stored in the "rpm\-md" format (i\&.e\&. from files in the \(lqrepodata\(rq directory) to a repository\&. Supported files are "primary", "filelists", "other", "suseinfo"\&. Do not forget to specify the \fBREPO_EXTEND_SOLVABLES\fR for extension files like "filelists" and "other"\&. Use the \fIlanguage\fR parameter if you have language extension files, otherwise simply use a \fBundef\fR/\fBNone\fR/\fBnil\fR parameter\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_repomdxml(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_repomdxml(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_repomdxml(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_repomdxml(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the repomd\&.xml meta description from the "rpm\-md" format to the repository\&. This file contains information about the repository like keywords, and also a list of all database files with checksums\&. The data is added to the "meta" section of the repository, i\&.e\&. no package gets created\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_updateinfoxml(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_updateinfoxml(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_updateinfoxml(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_updateinfoxml(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the updateinfo\&.xml file containing available maintenance updates to the repository\&. All updates are created as special packages that have a "patch:" prefix in their name\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_deltainfoxml(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_deltainfoxml(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_deltainfoxml(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_deltainfoxml(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the deltainfo\&.xml file (also called prestodelta\&.xml) containing available delta\-rpms to the repository\&. The data is added to the "meta" section, i\&.e\&. no package gets created\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_debdb(int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_debdb()\fR;
-\fIrepo\fR\fB\&.add_debdb()\fR
-\fIrepo\fR\fB\&.add_debdb()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the contents of the debian installed package database to the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_debpackages(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_debpackages(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_debpackages(\fR\fI$fp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_debpackages(\fR\fI$fp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the contents of the debian repository metadata (the "packages" file) to the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable add_deb(const char *\fR\fIfilename\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$solvable\fR \fB=\fR \fI$repo\fR\fB\->add_deb(\fR\fI$filename\fR\fB)\fR;
-\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_deb(\fR\fIfilename\fR\fB)\fR
-\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_deb(\fR\fIfilename\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the metadata of a single deb package to the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_mdk(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_mdk(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_mdk(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_mdk(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the contents of the mageia/mandriva repository metadata (the "synthesis\&.hdlist" file) to the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_mdk_info(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_mdk(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_mdk(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_mdk(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Extend the packages from the synthesis file with the info\&.xml and files\&.xml data\&. Do not forget to specify \fBREPO_EXTEND_SOLVABLES\fR\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_arch_repo(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_arch_repo(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_arch_repo(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_arch_repo(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the contents of the archlinux repository metadata (the "\&.db\&.tar" file) to the repository\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_arch_local(const char *\fR\fIdir\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_arch_local(\fR\fI$dir\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_arch_local(\fR\fIdir\fR\fB)\fR
-\fIrepo\fR\fB\&.add_arch_local(\fR\fIdir\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the contents of the archlinux installed package database to the repository\&. The \fIdir\fR parameter is usually set to "/var/lib/pacman/local"\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_content(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_content(\fR\fI$fp\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_content(\fR\fIfp\fR\fB)\fR
-\fIrepo\fR\fB\&.add_content(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the \(lqcontent\(rq meta description from the susetags format to the repository\&. This file contains information about the repository like keywords, and also a list of all database files with checksums\&. The data is added to the "meta" section of the repository, i\&.e\&. no package gets created\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_susetags(FILE *\fR\fIfp\fR\fB, Id\fR \fIdefvendor\fR\fB, const char *\fR\fIlanguage\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_susetags(\fR\fI$fp\fR\fB,\fR \fI$defvendor\fR\fB,\fR \fI$language\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_susetags(\fR\fIfp\fR\fB,\fR \fIdefvendor\fR\fB,\fR \fIlanguage\fR\fB)\fR
-\fIrepo\fR\fB\&.add_susetags(\fR\fIfp\fR\fB,\fR \fIdefvendor\fR\fB,\fR \fIlanguage\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add repository metadata in the susetags format to the repository\&. Like with add_rpmmd, you can specify a language if you have language extension files\&. The \fIdefvendor\fR parameter provides a default vendor for packages with missing vendors, it is usually provided in the content file\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_products(const char *\fR\fIdir\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
-\fI$repo\fR\fB\->add_products(\fR\fI$dir\fR\fB)\fR;
-\fIrepo\fR\fB\&.add_products(\fR\fIdir\fR\fB)\fR
-\fIrepo\fR\fB\&.add_products(\fR\fIdir\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the installed SUSE products database to the repository\&. The \fIdir\fR parameter is usually "/etc/products\&.d"\&.
-.SH "THE SOLVABLE CLASS"
-.sp
-A solvable describes all the information of one package\&. Each solvable belongs to one repository, it can be added and filled manually but in most cases solvables will get created by the repo_add methods\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepo *repo;\fR /* read only */
-\fI$solvable\fR\fB\->{repo}\fR
-\fIsolvable\fR\fB\&.repo\fR
-\fIsolvable\fR\fB\&.repo\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The repository this solvable belongs to\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool;\fR /* read only */
-\fI$solvable\fR\fB\->{pool}\fR
-\fIsolvable\fR\fB\&.pool\fR
-\fIsolvable\fR\fB\&.pool\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The pool this solvable belongs to, same as the pool of the repo\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id;\fR /* read only */
-\fI$solvable\fR\fB\->{id}\fR
-\fIsolvable\fR\fB\&.id\fR
-\fIsolvable\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The specific id of the solvable\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *name;\fR /* read/write */
-\fI$solvable\fR\fB\->{name}\fR
-\fIsolvable\fR\fB\&.name\fR
-\fIsolvable\fR\fB\&.name\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *evr;\fR /* read/write */
-\fI$solvable\fR\fB\->{evr}\fR
-\fIsolvable\fR\fB\&.evr\fR
-\fIsolvable\fR\fB\&.evr\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *arch;\fR /* read/write */
-\fI$solvable\fR\fB\->{arch}\fR
-\fIsolvable\fR\fB\&.arch\fR
-\fIsolvable\fR\fB\&.arch\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *vendor;\fR /* read/write */
-\fI$solvable\fR\fB\->{vendor}\fR
-\fIsolvable\fR\fB\&.vendor\fR
-\fIsolvable\fR\fB\&.vendor\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Easy access to often used attributes of solvables\&. They are internally stored as Ids\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId nameid;\fR /* read/write */
-\fI$solvable\fR\fB\->{nameid}\fR
-\fIsolvable\fR\fB\&.nameid\fR
-\fIsolvable\fR\fB\&.nameid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId evrid;\fR /* read/write */
-\fI$solvable\fR\fB\->{evrid}\fR
-\fIsolvable\fR\fB\&.evrid\fR
-\fIsolvable\fR\fB\&.evrid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId archid;\fR /* read/write */
-\fI$solvable\fR\fB\->{archid}\fR
-\fIsolvable\fR\fB\&.archid\fR
-\fIsolvable\fR\fB\&.archid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId vendorid;\fR /* read/write */
-\fI$solvable\fR\fB\->{vendorid}\fR
-\fIsolvable\fR\fB\&.vendorid\fR
-\fIsolvable\fR\fB\&.vendorid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Raw interface to the ids\&. Useful if you want to search for a specific id and want to avoid the string compare overhead\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *lookup_str(Id\fR \fIkeyname\fR\fB)\fR
-my \fI$string\fR \fB=\fR \fI$solvable\fR\fB\->lookup_str(\fR\fI$keyname\fR\fB)\fR;
-\fIstring\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_str(\fR\fIkeyname\fR\fB)\fR
-\fIstring\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_str(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId lookup_id(Id\fR \fIkeyname\fR\fB)\fR
-my \fI$id\fR \fB=\fR \fI$solvable\fR\fB\->lookup_id(\fR\fI$keyname\fR\fB)\fR;
-\fIid\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_id(\fR\fIsolvid\fR\fB)\fR
-\fIid\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_id(\fR\fIsolvid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBunsigned long long lookup_num(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInotfound\fR \fB= 0)\fR
-my \fI$num\fR \fB=\fR \fI$solvable\fR\fB\->lookup_num(\fR\fI$keyname\fR\fB)\fR;
-\fInum\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_num(\fR\fIkeyname\fR\fB)\fR
-\fInum\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_num(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool lookup_void(Id\fR \fIkeyname\fR\fB)\fR
-my \fI$bool\fR \fB=\fR \fI$solvable\fR\fB\->lookup_void(\fR\fI$keyname\fR\fB)\fR;
-\fIbool\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_void(\fR\fIkeyname\fR\fB)\fR
-\fIbool\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_void(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBChksum lookup_checksum(Id\fR \fIkeyname\fR\fB)\fR
-my \fI$chksum\fR \fB=\fR \fI$solvable\fR\fB\->lookup_checksum(\fR\fI$keyname\fR\fB)\fR;
-\fIchksum\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_checksum(\fR\fIkeyname\fR\fB)\fR
-\fIchksum\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_checksum(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId *lookup_idarray(Id\fR \fIkeyname\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
-my \fI@ids\fR \fB=\fR \fI$solvable\fR\fB\->lookup_idarray(\fR\fI$keyname\fR\fB)\fR;
-\fIids\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_idarray(\fR\fIkeyname\fR\fB)\fR
-\fIids\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_idarray(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDep *lookup_deparray(Id\fR \fIkeyname\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
-my \fI@deps\fR \fB=\fR \fI$solvable\fR\fB\->lookup_deparray(\fR\fI$keyname\fR\fB)\fR;
-\fIdeps\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_deparray(\fR\fIkeyname\fR\fB)\fR
-\fIdeps\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_deparray(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Generic lookup methods\&. Retrieve data stored for the specific keyname\&. The lookup_idarray() method will return an array of Ids, use lookup_deparray if you want an array of Dependency objects instead\&. Some Id arrays contain two parts of data divided by a specific marker, for example the provides array uses the SOLVABLE_FILEMARKER id to store both the ids provided by the package and the ids added by the addfileprovides method\&. The default, \-1, translates to the correct marker for the keyname and returns the first part of the array, use 1 to select the second part or 0 to retrieve all ids including the marker\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *lookup_location(unsigned int *\fR\fIOUTPUT\fR\fB)\fR;
-my \fB(\fR\fI$location\fR\fB,\fR \fI$medianr\fR\fB) =\fR \fI$solvable\fR\fB\->lookup_location()\fR;
-\fIlocation\fR\fB,\fR \fImedianr\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_location()\fR
-\fIlocation\fR\fB,\fR \fImedianr\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_location()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a tuple containing the on\-media location and an optional media number for multi\-part repositories (e\&.g\&. repositories spawning multiple DVDs)\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDataiterator Dataiterator(Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$di\fR \fB=\fR \fI$solvable\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIdi\fR \fB=\fR \fIsolvable\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIdi\fR \fB=\fR \fIsolvable\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterate over the matching data elements\&. See the Dataiterator class for more information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add_deparray(Id\fR \fIkeyname\fR\fB, DepId\fR \fIdep\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR;
-\fI$solvable\fR\fB\->add_deparray(\fR\fI$keyname\fR\fB,\fR \fI$dep\fR\fB)\fR;
-\fIsolvable\fR\fB\&.add_deparray(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
-\fIsolvable\fR\fB\&.add_deparray(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a new dependency to the attributes stored in keyname\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid unset(Id\fR \fIkeyname\fR\fB)\fR;
-\fI$solvable\fR\fB\->unset(\fR\fI$keyname\fR\fB)\fR;
-\fIsolvable\fR\fB\&.unset(\fR\fIkeyname\fR\fB)\fR
-\fIsolvable\fR\fB\&.unset(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Delete data stored for the specific keyname\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool installable()\fR;
-\fI$solvable\fR\fB\->installable()\fR
-\fIsolvable\fR\fB\&.installable()\fR
-\fIsolvable\fR\fB\&.installable?\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return true if the solvable is installable on the system\&. Solvables are not installable if the system does not support their architecture\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool isinstalled()\fR;
-\fI$solvable\fR\fB\->isinstalled()\fR
-\fIsolvable\fR\fB\&.isinstalled()\fR
-\fIsolvable\fR\fB\&.isinstalled?\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return true if the solvable is installed on the system\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool identical(Solvable *\fR\fIother\fR\fB)\fR
-\fI$solvable\fR\fB\->identical(\fR\fI$other\fR\fB)\fR
-\fI$solvable\fR\fB\&.identical(\fR\fIother\fR\fB)\fR
-\fI$solvable\fR\fB\&.identical?(\fR\fIother\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return true if the two solvables are identical\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint evrcmp(Solvable *\fR\fIother\fR\fB)\fR
-\fI$solvable\fR\fB\->evrcmp(\fR\fIother\fR\fB)\fR
-\fI$solvable\fR\fB\&.evrcmp(\fR\fIother\fR\fB)\fR
-\fI$solvable\fR\fB\&.evrcmp(\fR\fIother\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Returns \-1 if the epoch/version/release of the solvable is less than the one from the other solvable, 1 if it is greater, and 0 if they are equal\&. Note that "equal" does not mean that the evr is identical\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSelection Selection(int\fR \fIsetflags\fR \fB= 0)\fR
-my \fI$sel\fR \fB=\fR \fI$solvable\fR\fB\->Selection()\fR;
-\fIsel\fR \fB=\fR \fIsolvable\fR\fB\&.Selection()\fR
-\fIsel\fR \fB=\fR \fIsolvable\fR\fB\&.Selection()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a Selection containing just the single solvable\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *str()\fR
-my \fI$str\fR \fB=\fR \fI$solvable\fR\fB\->str()\fR;
-\fIstr\fR \fB=\fR \fI$solvable\fR\fB\&.str()\fR
-\fIstr\fR \fB=\fR \fI$solvable\fR\fB\&.str()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a string describing the solvable\&. The string consists of the name, version, and architecture of the Solvable\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<stringification>\fR
-my \fI$str\fR \fB=\fR \fI$solvable\fR\fB\->str\fR;
-\fIstr\fR \fB= str(\fR\fIsolvable\fR\fB)\fR
-\fIstr\fR \fB=\fR \fIsolvable\fR\fB\&.to_s\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Same as calling the str() method\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<equality>\fR
-\fBif (\fR\fI$solvable1\fR \fB==\fR \fI$solvable2\fR\fB)\fR
-\fBif\fR \fIsolvable1\fR \fB==\fR \fIsolvable2\fR\fB:\fR
-\fBif\fR \fIsolvable1\fR \fB==\fR \fIsolvable2\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Two solvables are equal if they are part of the same pool and have the same ids\&.
-.SH "THE DATAITERATOR CLASS"
-.sp
-Dataiterators can be used to do complex string searches or to iterate over arrays\&. They can be created via the constructors in the Pool, Repo, and Solvable classes\&. The Repo and Solvable constructors will limit the search to the repository or the specific package\&.
-.SS "CONSTANTS"
-.PP
-\fBSEARCH_STRING\fR
-.RS 4
-Return a match if the search string matches the value\&.
-.RE
-.PP
-\fBSEARCH_STRINGSTART\fR
-.RS 4
-Return a match if the value starts with the search string\&.
-.RE
-.PP
-\fBSEARCH_STRINGEND\fR
-.RS 4
-Return a match if the value ends with the search string\&.
-.RE
-.PP
-\fBSEARCH_SUBSTRING\fR
-.RS 4
-Return a match if the search string can be matched somewhere in the value\&.
-.RE
-.PP
-\fBSEARCH_GLOB\fR
-.RS 4
-Do a glob match of the search string against the value\&.
-.RE
-.PP
-\fBSEARCH_REGEX\fR
-.RS 4
-Do a regular expression match of the search string against the value\&.
-.RE
-.PP
-\fBSEARCH_NOCASE\fR
-.RS 4
-Ignore case when matching strings\&. Works for all the above match types\&.
-.RE
-.PP
-\fBSEARCH_FILES\fR
-.RS 4
-Match the complete filenames of the file list, not just the base name\&.
-.RE
-.PP
-\fBSEARCH_COMPLETE_FILELIST\fR
-.RS 4
-When matching the file list, check every file of the package not just the subset from the primary metadata\&.
-.RE
-.PP
-\fBSEARCH_CHECKSUMS\fR
-.RS 4
-Allow the matching of checksum entries\&.
-.RE
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid prepend_keyname(Id\fR \fIkeyname\fR\fB)\fR;
-\fI$di\fR\fB\->prepend_keyname(\fR\fI$keyname\fR\fB)\fR;
-\fIdi\fR\fB\&.prepend_keyname(\fR\fIkeyname\fR\fB)\fR
-\fIdi\fR\fB\&.prepend_keyname(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Do a sub\-search in the array stored in keyname\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid skip_solvable()\fR;
-\fI$di\fR\fB\->kip_solvable()\fR;
-\fIdi\fR\fB\&.skip_solvable()\fR
-\fIdi\fR\fB\&.skip_solvable()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Stop matching the current solvable and advance to the next one\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<iteration>\fR
-\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterate through the matches\&. If there is a match, the object in d will be of type Datamatch\&.
-.SH "THE DATAMATCH CLASS"
-.sp
-Objects of this type will be created for every value matched by a dataiterator\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool;\fR /* read only */
-\fI$d\fR\fB\->{pool}\fR
-\fId\fR\fB\&.pool\fR
-\fId\fR\fB\&.pool\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to pool\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepo *repo;\fR /* read only */
-\fI$d\fR\fB\->{repo}\fR
-\fId\fR\fB\&.repo\fR
-\fId\fR\fB\&.repo\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The repository containing the matched object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *solvable;\fR /* read only */
-\fI$d\fR\fB\->{solvable}\fR
-\fId\fR\fB\&.solvable\fR
-\fId\fR\fB\&.solvable\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The solvable containing the value that was matched\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId solvid;\fR /* read only */
-\fI$d\fR\fB\->{solvid}\fR
-\fId\fR\fB\&.solvid\fR
-\fId\fR\fB\&.solvid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The id of the solvable that matched\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId\fR \fIkey_id\fR;
-\fI$d\fR\fB\->{\fR\fIkey_id\fR\fB}\fR
-\fId\fR\fB\&.key_id\fR
-\fId\fR\fB\&.key_id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *\fR\fIkey_idstr\fR;
-\fI$d\fR\fB\->{\fR\fIkey_idstr\fR\fB}\fR
-\fId\fR\fB\&.key_idstr\fR
-\fId\fR\fB\&.key_idstr\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The keyname that matched, either as id or string\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId\fR \fItype_id\fR;
-\fI$d\fR\fB\->{\fR\fItype_id\fR\fB}\fR
-\fId\fR\fB\&.type_id\fR
-\fId\fR\fB\&.type_id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *\fR\fItype_idstr\fR;
-\fI$d\fR\fB\->{\fR\fItype_idstr\fR\fB}\fR;
-\fId\fR\fB\&.type_idstr\fR
-\fId\fR\fB\&.type_idstr\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The key type of the value that was matched, either as id or string\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId\fR \fIid\fR;
-\fI$d\fR\fB\->{id}\fR
-\fId\fR\fB\&.id\fR
-\fId\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId\fR \fIidstr\fR;
-\fI$d\fR\fB\->{idstr}\fR
-\fId\fR\fB\&.idstr\fR
-\fId\fR\fB\&.idstr\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The Id of the value that was matched (only valid for id types), either as id or string\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *\fR\fIstr\fR;
-\fI$d\fR\fB\->{str}\fR
-\fId\fR\fB\&.str\fR
-\fId\fR\fB\&.str\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The string value that was matched (only valid for string types)\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBunsigned long long\fR \fInum\fR;
-\fI$d\fR\fB\->{num}\fR
-\fId\fR\fB\&.num\fR
-\fId\fR\fB\&.num\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The numeric value that was matched (only valid for numeric types)\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBunsigned int\fR \fInum2\fR;
-\fI$d\fR\fB\->{num2}\fR
-\fId\fR\fB\&.num2\fR
-\fId\fR\fB\&.num2\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The secondary numeric value that was matched (only valid for types containing two values)\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBunsigned int\fR \fIbinary\fR;
-\fI$d\fR\fB\->{binary}\fR
-\fId\fR\fB\&.binary\fR
-\fId\fR\fB\&.binary\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The value in binary form, useful for checksums and other data that cannot be represented as a string\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDatapos pos()\fR;
-my \fI$pos\fR \fB=\fR \fI$d\fR\fB\->pos()\fR;
-\fIpos\fR \fB=\fR \fId\fR\fB\&.pos()\fR
-\fIpos\fR \fB=\fR \fId\fR\fB\&.pos()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The position object of the current match\&. It can be used to do sub\-searches starting at the match (if it is of an array type)\&. See the Datapos class for more information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDatapos parentpos()\fR;
-my \fI$pos\fR \fB=\fR \fI$d\fR\fB\->parentpos()\fR;
-\fIpos\fR \fB=\fR \fId\fR\fB\&.parentpos()\fR
-\fIpos\fR \fB=\fR \fId\fR\fB\&.parentpos()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The position object of the array containing the current match\&. It can be used to do sub\-searches, see the Datapos class for more information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<stringification>\fR
-my \fI$str\fR \fB=\fR \fI$d\fR\fB\->str\fR;
-\fIstr\fR \fB= str(\fR\fId\fR\fB)\fR
-\fIstr\fR \fB=\fR \fId\fR\fB\&.to_s\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the stringification of the matched value\&. Stringification depends on the search flags, for file list entries it will return just the base name unless SEARCH_FILES is used, for checksums it will return an empty string unless SEARCH_CHECKSUMS is used\&. Numeric values are currently stringified to an empty string\&.
-.SH "THE SELECTION CLASS"
-.sp
-Selections are a way to easily deal with sets of packages\&. There are multiple constructors to create them, the most useful is probably the select() method in the Pool class\&.
-.SS "CONSTANTS"
-.PP
-\fBSELECTION_NAME\fR
-.RS 4
-Create the selection by matching package names\&.
-.RE
-.PP
-\fBSELECTION_PROVIDES\fR
-.RS 4
-Create the selection by matching package provides\&.
-.RE
-.PP
-\fBSELECTION_FILELIST\fR
-.RS 4
-Create the selection by matching package files\&.
-.RE
-.PP
-\fBSELECTION_CANON\fR
-.RS 4
-Create the selection by matching the canonical representation of the package\&. This is normally a combination of the name, the version, and the architecture of a package\&.
-.RE
-.PP
-\fBSELECTION_DOTARCH\fR
-.RS 4
-Allow an \(lq\&.<architecture>\(rq suffix when matching names or provides\&.
-.RE
-.PP
-\fBSELECTION_REL\fR
-.RS 4
-Allow the specification of a relation when matching names or provides, e\&.g\&. "name >= 1\&.2"\&.
-.RE
-.PP
-\fBSELECTION_INSTALLED_ONLY\fR
-.RS 4
-Limit the package search to installed packages\&.
-.RE
-.PP
-\fBSELECTION_SOURCE_ONLY\fR
-.RS 4
-Limit the package search to source packages only\&.
-.RE
-.PP
-\fBSELECTION_WITH_SOURCE\fR
-.RS 4
-Extend the package search to also match source packages\&. The default is only to match binary packages\&.
-.RE
-.PP
-\fBSELECTION_GLOB\fR
-.RS 4
-Allow glob matching for package names, package provides, and file names\&.
-.RE
-.PP
-\fBSELECTION_NOCASE\fR
-.RS 4
-Ignore case when matching package names, package provides, and file names\&.
-.RE
-.PP
-\fBSELECTION_FLAT\fR
-.RS 4
-Return only one selection element describing the selected packages\&. The default is to create multiple elements for all globbed packages\&. Multiple elements are useful if you want to turn the selection into an install job, in that case you want an install job for every globbed package\&.
-.RE
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool;\fR /* read only */
-\fI$d\fR\fB\->{pool}\fR
-\fId\fR\fB\&.pool\fR
-\fId\fR\fB\&.pool\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to pool\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint flags()\fR;
-my \fI$flags\fR \fB=\fR \fI$sel\fR\fB\->flags()\fR;
-\fIflags\fR \fB=\fR \fIsel\fR\fB\&.flags()\fR
-\fIflags\fR \fB=\fR \fIsel\fR\fB\&.flags()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the result flags of the selection\&. The flags are a subset of the ones used when creating the selection, they describe which method was used to get the result\&. For example, if you create the selection with \(lqSELECTION_NAME | SELECTION_PROVIDES\(rq, the resulting flags will either be SELECTION_NAME or SELECTION_PROVIDES depending if there was a package that matched the name or not\&. If there was no match at all, the flags will be zero\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool isempty()\fR;
-\fI$sel\fR\fB\->isempty()\fR
-\fIsel\fR\fB\&.isempty()\fR
-\fIsel\fR\fB\&.isempty?\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return true if the selection is empty, i\&.e\&. no package could be matched\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid filter(Selection *\fR\fIother\fR\fB)\fR
-\fI$sel\fR\fB\->filter(\fR\fI$other\fR\fB)\fR;
-\fIsel\fR\fB\&.filter(\fR\fIother\fR\fB)\fR
-\fIsel\fR\fB\&.filter(\fR\fIother\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Intersect two selections\&. Packages will only stay in the selection if there are also included in the other selecting\&. Does an in\-place modification\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add(Selection *\fR\fIother\fR\fB)\fR
-\fI$sel\fR\fB\->add(\fR\fI$other\fR\fB)\fR;
-\fIsel\fR\fB\&.add(\fR\fIother\fR\fB)\fR
-\fIsel\fR\fB\&.add(\fR\fIother\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Build the union of two selections\&. All packages of the other selection will be added to the set of packages of the selection object\&. Does an in\-place modification\&. Note that the selection flags are no longer meaningful after the add operation\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add_raw(Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR
-\fI$sel\fR\fB\->add_raw(\fR\fI$how\fR\fB,\fR \fI$what\fR\fB)\fR;
-\fIsel\fR\fB\&.add_raw(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
-\fIsel\fR\fB\&.add_raw(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a raw element to the selection\&. Check the Job class for information about the how and what parameters\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBJob *jobs(int\fR \fIaction\fR\fB)\fR
-my \fI@jobs\fR \fB=\fR \fI$sel\fR\fB\->jobs(\fR\fI$action\fR\fB)\fR;
-\fIjobs\fR \fB=\fR \fIsel\fR\fB\&.jobs(\fR\fIaction\fR\fB)\fR
-\fIjobs\fR \fB=\fR \fIsel\fR\fB\&.jobs(\fR\fIaction\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert a selection into an array of Job objects\&. The action parameter is or\-ed to the \(lqhow\(rq part of the job, it describes the type of job (e\&.g\&. install, erase)\&. See the Job class for the action and action modifier constants\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *solvables()\fR
-my \fI@solvables\fR \fB=\fR \fI$sel\fR\fB\->solvables()\fR;
-\fIsolvables\fR \fB=\fR \fIsel\fR\fB\&.solvables()\fR
-\fIsolvables\fR \fB=\fR \fIsel\fR\fB\&.solvables()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert a selection into an array of Solvable objects\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<stringification>\fR
-my \fI$str\fR \fB=\fR \fI$sel\fR\fB\->str\fR;
-\fIstr\fR \fB= str(\fR\fIsel\fR\fB)\fR
-\fIstr\fR \fB=\fR \fIsel\fR\fB\&.to_s\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a string describing the selection\&.
-.SH "THE JOB CLASS"
-.sp
-Jobs are the way to specify to the dependency solver what to do\&. Most of the times jobs will get created by calling the jobs() method on a Selection object, but there is also a Job() constructor in the Pool class\&.
-.SS "CONSTANTS"
-.sp
-Selection constants:
-.PP
-\fBSOLVER_SOLVABLE\fR
-.RS 4
-The \(lqwhat\(rq part is the id of a solvable\&.
-.RE
-.PP
-\fBSOLVER_SOLVABLE_NAME\fR
-.RS 4
-The \(lqwhat\(rq part is the id of a package name\&.
-.RE
-.PP
-\fBSOLVER_SOLVABLE_PROVIDES\fR
-.RS 4
-The \(lqwhat\(rq part is the id of a package provides\&.
-.RE
-.PP
-\fBSOLVER_SOLVABLE_ONE_OF\fR
-.RS 4
-The \(lqwhat\(rq part is an offset into the \(lqwhatprovides\(rq data, created by calling the towhatprovides() pool method\&.
-.RE
-.PP
-\fBSOLVER_SOLVABLE_REPO\fR
-.RS 4
-The \(lqwhat\(rq part is the id of a repository\&.
-.RE
-.PP
-\fBSOLVER_SOLVABLE_ALL\fR
-.RS 4
-The \(lqwhat\(rq part is ignored, all packages are selected\&.
-.RE
-.PP
-\fBSOLVER_SOLVABLE_SELECTMASK\fR
-.RS 4
-A mask containing all the above selection bits\&.
-.RE
-.sp
-Action constants:
-.PP
-\fBSOLVER_NOOP\fR
-.RS 4
-Do nothing\&.
-.RE
-.PP
-\fBSOLVER_INSTALL\fR
-.RS 4
-Install a package of the specified set of packages\&. It tries to install the best matching package (i\&.e\&. the highest version of the packages from the repositories with the highest priority)\&.
-.RE
-.PP
-\fBSOLVER_ERASE\fR
-.RS 4
-Erase all of the packages from the specified set\&. If a package is not installed, erasing it will keep it from getting installed\&.
-.RE
-.PP
-\fBSOLVER_UPDATE\fR
-.RS 4
-Update the matching installed packages to their best version\&. If none of the specified packages are installed, try to update the installed packages to the specified versions\&. See the section about targeted updates about more information\&.
-.RE
-.PP
-\fBSOLVER_WEAKENDEPS\fR
-.RS 4
-Allow to break the dependencies of the matching packages\&. Handle with care\&.
-.RE
-.PP
-\fBSOLVER_MULTIVERSION\fR
-.RS 4
-Mark the matched packages for multiversion install\&. If they get to be installed because of some other job, the installation will keep the old version of the package installed (for rpm this is done by using \(lq\-i\(rq instead of \(lq\-U\(rq)\&.
-.RE
-.PP
-\fBSOLVER_LOCK\fR
-.RS 4
-Do not change the state of the matched packages, i\&.e\&. when they are installed they stay installed, if not they are not selected for installation\&.
-.RE
-.PP
-\fBSOLVER_DISTUPGRADE\fR
-.RS 4
-Update the matching installed packages to the best version included in one of the repositories\&. After this operation, all come from one of the available repositories except orphaned packages\&. Orphaned packages are packages that have no relation to the packages in the repositories, i\&.e\&. no package in the repositories have the same name or obsolete the orphaned package\&. This action brings the installed packages in sync with the ones in the repository\&. By default it also turns of arch/vendor/version locking for the affected packages to simulate a fresh installation\&. This means that distupgrade can actually downgrade packages if only lower versions of a package are available in the repositories\&. You can tweak this behavior with the SOLVER_FLAG_DUP_ solver flags\&.
-.RE
-.PP
-\fBSOLVER_DROP_ORPHANED\fR
-.RS 4
-Erase all the matching installed packages if they are orphaned\&. This only makes sense if there is a \(lqdistupgrade all packages\(rq job\&. The default is to erase orphaned packages only if they block the installation of other packages\&.
-.RE
-.PP
-\fBSOLVER_VERIFY\fR
-.RS 4
-Fix dependency problems of matching installed packages\&. The default is to ignore dependency problems for installed packages\&.
-.RE
-.PP
-\fBSOLVER_USERINSTALLED\fR
-.RS 4
-The matching installed packages are considered to be installed by a user, thus not installed to fulfill some dependency\&. This is needed input for the calculation of unneeded packages for jobs that have the SOLVER_CLEANDEPS flag set\&.
-.RE
-.PP
-\fBSOLVER_ALLOWUNINSTALL\fR
-.RS 4
-Allow the solver to deinstall the matching installed packages if they get into the way of resolving a dependency\&. This is like the SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages\&.
-.RE
-.PP
-\fBSOLVER_JOBMASK\fR
-.RS 4
-A mask containing all the above action bits\&.
-.RE
-.sp
-Action modifier constants:
-.PP
-\fBSOLVER_WEAK\fR
-.RS 4
-Makes the job a weak job\&. The solver tries to fulfill weak jobs, but does not report a problem if it is not possible to do so\&.
-.RE
-.PP
-\fBSOLVER_ESSENTIAL\fR
-.RS 4
-Makes the job an essential job\&. If there is a problem with the job, the solver will not propose to remove the job as one solution (unless all other solutions are also to remove essential jobs)\&.
-.RE
-.PP
-\fBSOLVER_CLEANDEPS\fR
-.RS 4
-The solver will try to also erase all packages dragged in through dependencies when erasing the package\&. This needs SOLVER_USERINSTALLED jobs to maximize user satisfaction\&.
-.RE
-.PP
-\fBSOLVER_FORCEBEST\fR
-.RS 4
-Insist on the best package for install, update, and distupgrade jobs\&. If this flag is not used, the solver will use the second\-best package if the best package cannot be installed for some reason\&. When this flag is used, the solver will generate a problem instead\&.
-.RE
-.PP
-\fBSOLVER_TARGETED\fR
-.RS 4
-Forces targeted operation update and distupgrade jobs\&. See the section about targeted updates about more information\&.
-.RE
-.sp
-Set constants\&.
-.PP
-\fBSOLVER_SETEV\fR
-.RS 4
-The job specified the exact epoch and version of the package set\&.
-.RE
-.PP
-\fBSOLVER_SETEVR\fR
-.RS 4
-The job specified the exact epoch, version, and release of the package set\&.
-.RE
-.PP
-\fBSOLVER_SETARCH\fR
-.RS 4
-The job specified the exact architecture of the packages from the set\&.
-.RE
-.PP
-\fBSOLVER_SETVENDOR\fR
-.RS 4
-The job specified the exact vendor of the packages from the set\&.
-.RE
-.PP
-\fBSOLVER_SETREPO\fR
-.RS 4
-The job specified the exact repository of the packages from the set\&.
-.RE
-.PP
-\fBSOLVER_SETNAME\fR
-.RS 4
-The job specified the exact name of the packages from the set\&.
-.RE
-.PP
-\fBSOLVER_NOAUTOSET\fR
-.RS 4
-Turn of automatic set flag generation for SOLVER_SOLVABLE jobs\&.
-.RE
-.PP
-\fBSOLVER_SETMASK\fR
-.RS 4
-A mask containing all the above set bits\&.
-.RE
-.sp
-See the section about set bits for more information\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool;\fR /* read only */
-\fI$job\fR\fB\->{pool}\fR
-\fId\fR\fB\&.pool\fR
-\fId\fR\fB\&.pool\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to pool\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId how;\fR /* read/write */
-\fI$job\fR\fB\->{how}\fR
-\fId\fR\fB\&.how\fR
-\fId\fR\fB\&.how\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Union of the selection, action, action modifier, and set flags\&. The selection part describes the semantics of the \(lqwhat\(rq Id\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId what;\fR /* read/write */
-\fI$job\fR\fB\->{what}\fR
-\fId\fR\fB\&.what\fR
-\fId\fR\fB\&.what\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Id describing the set of packages, the meaning depends on the selection part of the \(lqhow\(rq attribute\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *solvables()\fR
-my \fI@solvables\fR \fB=\fR \fI$job\fR\fB\->solvables()\fR;
-\fIsolvables\fR \fB=\fR \fIjob\fR\fB\&.solvables()\fR
-\fIsolvables\fR \fB=\fR \fIjob\fR\fB\&.solvables()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the set of solvables of the job as an array of Solvable objects\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool isemptyupdate()\fR;
-\fI$job\fR\fB\->isemptyupdate()\fR
-\fIjob\fR\fB\&.isemptyupdate()\fR
-\fIjob\fR\fB\&.isemptyupdate?\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convenience function to find out if the job describes an update job with no matching packages, i\&.e\&. a job that does nothing\&. Some package managers like \(lqzypper\(rq like to turn those jobs into install jobs, i\&.e\&. an update of a not\-installed package will result into the installation of the package\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<stringification>\fR
-my \fI$str\fR \fB=\fR \fI$job\fR\fB\->str\fR;
-\fIstr\fR \fB= str(\fR\fIjob\fR\fB)\fR
-\fIstr\fR \fB=\fR \fIjob\fR\fB\&.to_s\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a string describing the job\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<equality>\fR
-\fBif (\fR\fI$job1\fR \fB==\fR \fI$job2\fR\fB)\fR
-\fBif\fR \fIjob1\fR \fB==\fR \fIjob2\fR\fB:\fR
-\fBif\fR \fIjob1\fR \fB==\fR \fIjob2\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Two jobs are equal if they belong to the same pool and both the \(lqhow\(rq and the \(lqwhat\(rq attributes are the same\&.
-.SS "TARGETED UPDATES"
-.sp
-Libsolv has two modes for upgrades and distupgrade: targeted and untargeted\&. Untargeted mode means that the installed packages from the specified set will be updated to the best version\&. Targeted means that packages that can be updated to a package in the specified set will be updated to the best package of the set\&.
-.sp
-Here\(cqs an example to explain the subtle difference\&. Suppose that you have package A installed in version "1\&.1", "A\-1\&.2" is available in one of the repositories and there is also package "B" that obsoletes package A\&.
-.sp
-An untargeted update of "A" will update the installed "A\-1\&.1" to package "B", because that is the newest version (B obsoletes A and is thus newer)\&.
-.sp
-A targeted update of "A" will update "A\-1\&.1" to "A\-1\&.2", as the set of packages contains both "A\-1\&.1" and "A\-1\&.2", and "A\-1\&.2" is the newer one\&.
-.sp
-An untargeted update of "B" will do nothing, as "B" is not installed\&.
-.sp
-An targeted update of "B" will update "A\-1\&.1" to "B"\&.
-.sp
-Note that the default is to do "auto\-targeting", thus if the specified set of packages does not include an installed package, the solver will assume targeted operation even if SOLVER_TARGETED is not used\&.
-.sp
-This mostly matches the intent of the user, with one exception: In the example above, an update of "A\-1\&.2" will update "A\-1\&.1" to "A\-1\&.2" (targeted mode), but a second update of "A\-1\&.2" will suddenly update to "B", as untargeted mode is chosen because "A\-1\&.2" is now installed\&.
-.sp
-If you want to have full control over when targeting mode is chosen, turn off auto\-targeting with the SOLVER_FLAG_NO_AUTOTARGET solver option\&. In that case, all updates are considered to be untargeted unless they include the SOLVER_TARGETED flag\&.
-.SS "SET BITS"
-.sp
-Set bits specify which parts of the specified packages where specified by the user\&. It is used by the solver when checking if an operation is allowed or not\&. For example, the solver will normally not allow the downgrade of an installed package\&. But it will not report a problem if the SOLVER_SETEVR flag is used, as it then assumes that the user specified the exact version and thus knows what he is doing\&.
-.sp
-So if a package "screen\-1\-1" is installed for the x86_64 architecture and version "2\-1" is only available for the i586 architecture, installing package "screen\-2\&.1" will ask the user for confirmation because of the different architecture\&. When using the Selection class to create jobs the set bits are automatically added, e\&.g\&. selecting \(lqscreen\&.i586\(rq will automatically add SOLVER_SETARCH, and thus no problem will be reported\&.
-.SH "THE SOLVER CLASS"
-.sp
-Dependency solving is what this library is about\&. A solver object is needed for solving to store the result of the solver run\&. The solver object can be used multiple times for different jobs, reusing it allows the solver to re\-use the dependency rules it already computed\&.
-.SS "CONSTANTS"
-.sp
-Flags to modify some of the solver\(cqs behavior:
-.PP
-\fBSOLVER_FLAG_ALLOW_DOWNGRADE\fR
-.RS 4
-Allow the solver to downgrade packages without asking for confirmation (i\&.e\&. reporting a problem)\&.
-.RE
-.PP
-\fBSOLVER_FLAG_ALLOW_ARCHCHANGE\fR
-.RS 4
-Allow the solver to change the architecture of an installed package without asking for confirmation\&. Note that changes to/from noarch are always considered to be allowed\&.
-.RE
-.PP
-\fBSOLVER_FLAG_ALLOW_VENDORCHANGE\fR
-.RS 4
-Allow the solver to change the vendor of an installed package without asking for confirmation\&. Each vendor is part of one or more vendor equivalence classes, normally installed packages may only change their vendor if the new vendor shares at least one equivalence class\&.
-.RE
-.PP
-\fBSOLVER_FLAG_ALLOW_NAMECHANGE\fR
-.RS 4
-Allow the solver to change the name of an installed package, i\&.e\&. install a package with a different name that obsoletes the installed package\&. This option is on by default\&.
-.RE
-.PP
-\fBSOLVER_FLAG_ALLOW_UNINSTALL\fR
-.RS 4
-Allow the solver to erase installed packages to fulfill the jobs\&. This flag also includes the above flags\&. You may want to set this flag if you only have SOLVER_ERASE jobs, as in that case it\(cqs better for the user to check the transaction overview instead of approving every single package that needs to be erased\&.
-.RE
-.PP
-\fBSOLVER_FLAG_DUP_ALLOW_DOWNGRADE\fR
-.RS 4
-Like SOLVER_FLAG_ALLOW_DOWNGRADE, but used in distupgrade mode\&.
-.RE
-.PP
-\fBSOLVER_FLAG_DUP_ALLOW_ARCHCHANGE\fR
-.RS 4
-Like SOLVER_FLAG_ALLOW_ARCHCHANGE, but used in distupgrade mode\&.
-.RE
-.PP
-\fBSOLVER_FLAG_DUP_ALLOW_VENDORCHANGE\fR
-.RS 4
-Like SOLVER_FLAG_ALLOW_VENDORCHANGE, but used in distupgrade mode\&.
-.RE
-.PP
-\fBSOLVER_FLAG_DUP_ALLOW_NAMECHANGE\fR
-.RS 4
-Like SOLVER_FLAG_ALLOW_NAMECHANGE, but used in distupgrade mode\&.
-.RE
-.PP
-\fBSOLVER_FLAG_NO_UPDATEPROVIDE\fR
-.RS 4
-If multiple packages obsolete an installed package, the solver checks the provides of every such package and ignores all packages that do not provide the installed package name\&. Thus, you can have an official update candidate that provides the old name, and other packages that also obsolete the package but are not considered for updating\&. If you cannot use this feature, you can turn it off by setting this flag\&.
-.RE
-.PP
-\fBSOLVER_FLAG_SPLITPROVIDES\fR
-.RS 4
-Make the solver aware of special provides of the form \(lq<packagename>:<path>\(rq used in SUSE systems to support package splits\&.
-.RE
-.PP
-\fBSOLVER_FLAG_IGNORE_RECOMMENDED\fR
-.RS 4
-Do not process optional (aka weak) dependencies\&.
-.RE
-.PP
-\fBSOLVER_FLAG_ADD_ALREADY_RECOMMENDED\fR
-.RS 4
-Install recommended or supplemented packages even if they have no connection to the current transaction\&. You can use this feature to implement a simple way for the user to install new recommended packages that were not available in the past\&.
-.RE
-.PP
-\fBSOLVER_FLAG_NO_INFARCHCHECK\fR
-.RS 4
-Turn off the inferior architecture checking that is normally done by the solver\&. Normally, the solver allows only the installation of packages from the "best" architecture if a package is available for multiple architectures\&.
-.RE
-.PP
-\fBSOLVER_FLAG_BEST_OBEY_POLICY\fR
-.RS 4
-Make the SOLVER_FORCEBEST job option consider only packages that meet the policies for installed packages, i\&.e\&. no downgrades, no architecture change, no vendor change (see the first flags of this section)\&. If the flag is not specified, the solver will enforce the installation of the best package ignoring the installed packages, which may conflict with the set policy\&.
-.RE
-.PP
-\fBSOLVER_FLAG_NO_AUTOTARGET\fR
-.RS 4
-Do not enable auto\-targeting up update and distupgrade jobs\&. See the section on targeted updates for more information\&.
-.RE
-.PP
-\fBSOLVER_FLAG_KEEP_ORPHANS\fR
-.RS 4
-Do not allow orphaned packages to be deinstalled if they get in the way of resolving other packages\&.
-.RE
-.PP
-\fBSOLVER_FLAG_BREAK_ORPHANS\fR
-.RS 4
-Ignore dependencies of orphaned packages that get in the way of resolving non\-orphaned ones\&. Setting the flag might result in no longer working packages in case they are orphaned\&.
-.RE
-.PP
-\fBSOLVER_FLAG_FOCUS_INSTALLED\fR
-.RS 4
-Resolve installed packages before resolving the given job\&. Setting this flag means that the solver will prefer picking a package version that fits the other installed packages over updating installed packages\&.
-.RE
-.sp
-Basic rule types:
-.PP
-\fBSOLVER_RULE_UNKNOWN\fR
-.RS 4
-A rule of an unknown class\&. You should never encounter those\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG\fR
-.RS 4
-A package dependency rule\&.
-.RE
-.PP
-\fBSOLVER_RULE_UPDATE\fR
-.RS 4
-A rule to implement the update policy of installed packages\&. Every installed package has an update rule that consists of the packages that may replace the installed package\&.
-.RE
-.PP
-\fBSOLVER_RULE_FEATURE\fR
-.RS 4
-Feature rules are fallback rules used when an update rule is disabled\&. They include all packages that may replace the installed package ignoring the update policy, i\&.e\&. they contain downgrades, arch changes and so on\&. Without them, the solver would simply erase installed packages if their update rule gets disabled\&.
-.RE
-.PP
-\fBSOLVER_RULE_JOB\fR
-.RS 4
-Job rules implement the job given to the solver\&.
-.RE
-.PP
-\fBSOLVER_RULE_DISTUPGRADE\fR
-.RS 4
-These are simple negative assertions that make sure that only packages are kept that are also available in one of the repositories\&.
-.RE
-.PP
-\fBSOLVER_RULE_INFARCH\fR
-.RS 4
-Infarch rules are also negative assertions, they disallow the installation of packages when there are packages of the same name but with a better architecture\&.
-.RE
-.PP
-\fBSOLVER_RULE_CHOICE\fR
-.RS 4
-Choice rules are used to make sure that the solver prefers updating to installing different packages when some dependency is provided by multiple packages with different names\&. The solver may always break choice rules, so you will not see them when a problem is found\&.
-.RE
-.PP
-\fBSOLVER_RULE_LEARNT\fR
-.RS 4
-These rules are generated by the solver to keep it from running into the same problem multiple times when it has to backtrack\&. They are the main reason why a sat solver is faster than other dependency solver implementations\&.
-.RE
-.sp
-Special dependency rule types:
-.PP
-\fBSOLVER_RULE_PKG_NOT_INSTALLABLE\fR
-.RS 4
-This rule was added to prevent the installation of a package of an architecture that does not work on the system\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG_NOTHING_PROVIDES_DEP\fR
-.RS 4
-The package contains a required dependency which was not provided by any package\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG_REQUIRES\fR
-.RS 4
-Similar to SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP, but in this case some packages provided the dependency but none of them could be installed due to other dependency issues\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG_SELF_CONFLICT\fR
-.RS 4
-The package conflicts with itself\&. This is not allowed by older rpm versions\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG_CONFLICTS\fR
-.RS 4
-To fulfill the dependencies two packages need to be installed, but one of the packages contains a conflict with the other one\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG_SAME_NAME\fR
-.RS 4
-The dependencies can only be fulfilled by multiple versions of a package, but installing multiple versions of the same package is not allowed\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG_OBSOLETES\fR
-.RS 4
-To fulfill the dependencies two packages need to be installed, but one of the packages obsoletes the other one\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG_IMPLICIT_OBSOLETES\fR
-.RS 4
-To fulfill the dependencies two packages need to be installed, but one of the packages has provides a dependency that is obsoleted by the other one\&. See the POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES flag\&.
-.RE
-.PP
-\fBSOLVER_RULE_PKG_INSTALLED_OBSOLETES\fR
-.RS 4
-To fulfill the dependencies a package needs to be installed that is obsoleted by an installed package\&. See the POOL_FLAG_NOINSTALLEDOBSOLETES flag\&.
-.RE
-.PP
-\fBSOLVER_RULE_JOB_NOTHING_PROVIDES_DEP\fR
-.RS 4
-The user asked for installation of a package providing a specific dependency, but no available package provides it\&.
-.RE
-.PP
-\fBSOLVER_RULE_JOB_UNKNOWN_PACKAGE\fR
-.RS 4
-The user asked for installation of a package with a specific name, but no available package has that name\&.
-.RE
-.PP
-\fBSOLVER_RULE_JOB_PROVIDED_BY_SYSTEM\fR
-.RS 4
-The user asked for the erasure of a dependency that is provided by the system (i\&.e\&. for special hardware or language dependencies), this cannot be done with a job\&.
-.RE
-.PP
-\fBSOLVER_RULE_JOB_UNSUPPORTED\fR
-.RS 4
-The user asked for something that is not yet implemented, e\&.g\&. the installation of all packages at once\&.
-.RE
-.sp
-Policy error constants
-.PP
-\fBPOLICY_ILLEGAL_DOWNGRADE\fR
-.RS 4
-The solver ask for permission before downgrading packages\&.
-.RE
-.PP
-\fBPOLICY_ILLEGAL_ARCHCHANGE\fR
-.RS 4
-The solver ask for permission before changing the architecture of installed packages\&.
-.RE
-.PP
-\fBPOLICY_ILLEGAL_VENDORCHANGE\fR
-.RS 4
-The solver ask for permission before changing the vendor of installed packages\&.
-.RE
-.PP
-\fBPOLICY_ILLEGAL_NAMECHANGE\fR
-.RS 4
-The solver ask for permission before replacing an installed packages with a package that has a different name\&.
-.RE
-.sp
-Solution element type constants
-.PP
-\fBSOLVER_SOLUTION_JOB\fR
-.RS 4
-The problem can be solved by removing the specified job\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_POOLJOB\fR
-.RS 4
-The problem can be solved by removing the specified job that is defined in the pool\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_INFARCH\fR
-.RS 4
-The problem can be solved by allowing the installation of the specified package with an inferior architecture\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_DISTUPGRADE\fR
-.RS 4
-The problem can be solved by allowing to keep the specified package installed\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_BEST\fR
-.RS 4
-The problem can be solved by allowing to install the specified package that is not the best available package\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_ERASE\fR
-.RS 4
-The problem can be solved by allowing to erase the specified package\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_REPLACE\fR
-.RS 4
-The problem can be solved by allowing to replace the package with some other package\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_REPLACE_DOWNGRADE\fR
-.RS 4
-The problem can be solved by allowing to replace the package with some other package that has a lower version\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_REPLACE_ARCHCHANGE\fR
-.RS 4
-The problem can be solved by allowing to replace the package with some other package that has a different architecture\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_REPLACE_VENDORCHANGE\fR
-.RS 4
-The problem can be solved by allowing to replace the package with some other package that has a different vendor\&.
-.RE
-.PP
-\fBSOLVER_SOLUTION_REPLACE_NAMECHANGE\fR
-.RS 4
-The problem can be solved by allowing to replace the package with some other package that has a different name\&.
-.RE
-.sp
-Reason constants
-.PP
-\fBSOLVER_REASON_UNRELATED\fR
-.RS 4
-The package status did not change as it was not related to any job\&.
-.RE
-.PP
-\fBSOLVER_REASON_UNIT_RULE\fR
-.RS 4
-The package was installed/erased/kept because of a unit rule, i\&.e\&. a rule where all literals but one were false\&.
-.RE
-.PP
-\fBSOLVER_REASON_KEEP_INSTALLED\fR
-.RS 4
-The package was chosen when trying to keep as many packages installed as possible\&.
-.RE
-.PP
-\fBSOLVER_REASON_RESOLVE_JOB\fR
-.RS 4
-The decision happened to fulfill a job rule\&.
-.RE
-.PP
-\fBSOLVER_REASON_UPDATE_INSTALLED\fR
-.RS 4
-The decision happened to fulfill a package update request\&.
-.RE
-.PP
-\fBSOLVER_REASON_CLEANDEPS_ERASE\fR
-.RS 4
-The package was erased when cleaning up dependencies from other erased packages\&.
-.RE
-.PP
-\fBSOLVER_REASON_RESOLVE\fR
-.RS 4
-The package was installed to fulfill package dependencies\&.
-.RE
-.PP
-\fBSOLVER_REASON_WEAKDEP\fR
-.RS 4
-The package was installed because of a weak dependency (Recommends or Supplements)\&.
-.RE
-.PP
-\fBSOLVER_REASON_RESOLVE_ORPHAN\fR
-.RS 4
-The decision about the package was made when deciding the fate of orphaned packages\&.
-.RE
-.PP
-\fBSOLVER_REASON_RECOMMENDED\fR
-.RS 4
-This is a special case of SOLVER_REASON_WEAKDEP\&.
-.RE
-.PP
-\fBSOLVER_REASON_SUPPLEMENTED\fR
-.RS 4
-This is a special case of SOLVER_REASON_WEAKDEP\&.
-.RE
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool;\fR /* read only */
-\fI$job\fR\fB\->{pool}\fR
-\fId\fR\fB\&.pool\fR
-\fId\fR\fB\&.pool\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to pool\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint set_flag(int\fR \fIflag\fR\fB, int\fR \fIvalue\fR\fB)\fR
-my \fI$oldvalue\fR \fB=\fR \fI$solver\fR\fB\->set_flag(\fR\fI$flag\fR\fB,\fR \fI$value\fR\fB)\fR;
-\fIoldvalue\fR \fB=\fR \fIsolver\fR\fB\&.set_flag(\fR\fIflag\fR\fB,\fR \fIvalue\fR\fB)\fR
-\fIoldvalue\fR \fB=\fR \fIsolver\fR\fB\&.set_flag(\fR\fIflag\fR\fB,\fR \fIvalue\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint get_flag(int\fR \fIflag\fR\fB)\fR
-my \fI$value\fR \fB=\fR \fI$solver\fR\fB\->get_flag(\fR\fI$flag\fR\fB)\fR;
-\fIvalue\fR \fB=\fR \fIsolver\fR\fB\&.get_flag(\fR\fIflag\fR\fB)\fR
-\fIvalue\fR \fB=\fR \fIsolver\fR\fB\&.get_flag(\fR\fIflag\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set/get a solver specific flag\&. The flags define the policies the solver has to obey\&. The flags are explained in the CONSTANTS section of this class\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBProblem *solve(Job *\fR\fIjobs\fR\fB)\fR
-my \fI@problems\fR \fB=\fR \fI$solver\fR\fB\->solve(\e\fR\fI@jobs\fR\fB)\fR;
-\fIproblems\fR \fB=\fR \fIsolver\fR\fB\&.solve(\fR\fIjobs\fR\fB)\fR
-\fIproblems\fR \fB=\fR \fIsolver\fR\fB\&.solve(\fR\fIjobs\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Solve a problem specified in the job list (plus the jobs defined in the pool)\&. Returns an array of problems that need user interaction, or an empty array if no problems were encountered\&. See the Problem class on how to deal with problems\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBTransaction transaction()\fR
-my \fI$trans\fR \fB=\fR \fI$solver\fR\fB\->transaction()\fR;
-\fItrans\fR \fB=\fR \fIsolver\fR\fB\&.transaction()\fR
-\fItrans\fR \fB=\fR \fIsolver\fR\fB\&.transaction()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the transaction to implement the calculated package changes\&. A transaction is available even if problems were found, this is useful for interactive user interfaces that show both the job result and the problems\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint\fR \fIreason\fR \fB= describe_decision(Solvable *\fR\fIs\fR\fB, Rule *\fR\fIOUTPUT\fR\fB)\fR
-my \fB(\fR\fI$reason\fR\fB,\fR \fI$rule\fR\fB) =\fR \fI$solver\fR\fB\->describe_decision(\fR\fI$solvable\fR\fB)\fR;
-\fB(\fR\fIreason\fR\fB,\fR \fIrule\fR\fB) =\fR \fIsolver\fR\fB\&.describe_decision(\fR\fIsolvable\fR\fB)\fR
-\fB(\fR\fIreason\fR\fB,\fR \fIrule\fR\fB) =\fR \fIsolver\fR\fB\&.describe_decision(\fR\fIsolvable\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the reason why a specific solvable was installed or erased\&. For most of the reasons the rule that triggered the decision is also returned\&.
-.SH "THE PROBLEM CLASS"
-.sp
-Problems are the way of the solver to interact with the user\&. You can simply list all problems and terminate your program, but a better way is to present solutions to the user and let him pick the ones he likes\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolver *solv;\fR /* read only */
-\fI$problem\fR\fB\->{solv}\fR
-\fIproblem\fR\fB\&.solv\fR
-\fIproblem\fR\fB\&.solv\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to solver object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id;\fR /* read only */
-\fI$problem\fR\fB\->{id}\fR
-\fIproblem\fR\fB\&.id\fR
-\fIproblem\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Id of the problem\&. The first problem has Id 1, they are numbered consecutively\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRule findproblemrule()\fR
-my \fI$probrule\fR \fB=\fR \fI$problem\fR\fB\->findproblemrule()\fR;
-\fIprobrule\fR \fB=\fR \fIproblem\fR\fB\&.findproblemrule()\fR
-\fIprobrule\fR \fB=\fR \fIproblem\fR\fB\&.findproblemrule()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the rule that caused the problem\&. Of course in most situations there is no single responsible rule, but many rules that interconnect with each created the problem\&. Nevertheless, the solver uses some heuristic approach to find a rule that somewhat describes the problem best to the user\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRule *findallproblemrules(bool\fR \fIunfiltered\fR \fB= 0)\fR
-my \fI@probrules\fR \fB=\fR \fI$problem\fR\fB\->findallproblemrules()\fR;
-\fIprobrules\fR \fB=\fR \fIproblem\fR\fB\&.findallproblemrule()\fR
-\fIprobrules\fR \fB=\fR \fIproblem\fR\fB\&.findallproblemrule()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return all rules responsible for the problem\&. The returned set of rules contains all the needed information why there was a problem, but it\(cqs hard to present them to the user in a sensible way\&. The default is to filter out all update and job rules (unless the returned rules only consist of those types)\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolution *solutions()\fR
-my \fI@solutions\fR \fB=\fR \fI$problem\fR\fB\->solutions()\fR;
-\fIsolutions\fR \fB=\fR \fIproblem\fR\fB\&.solutions()\fR
-\fIsolutions\fR \fB=\fR \fIproblem\fR\fB\&.solutions()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return an array containing multiple possible solutions to fix the problem\&. See the solution class for more information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint solution_count()\fR
-my \fI$cnt\fR \fB=\fR \fI$problem\fR\fB\->solution_count()\fR;
-\fIcnt\fR \fB=\fR \fIproblem\fR\fB\&.solution_count()\fR
-\fIcnt\fR \fB=\fR \fIproblem\fR\fB\&.solution_count()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the number of solutions without creating solution objects\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<stringification>\fR
-my \fI$str\fR \fB=\fR \fI$problem\fR\fB\->str\fR;
-\fIstr\fR \fB= str(\fR\fIproblem\fR\fB)\fR
-\fIstr\fR \fB=\fR \fIproblem\fR\fB\&.to_s\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a string describing the problem\&. This is a convenience function, it is a shorthand for calling findproblemrule(), then ruleinfo() on the problem rule and problemstr() on the ruleinfo object\&.
-.SH "THE RULE CLASS"
-.sp
-Rules are the basic block of sat solving\&. Each package dependency gets translated into one or multiple rules\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolver *solv;\fR /* read only */
-\fI$rule\fR\fB\->{solv}\fR
-\fIrule\fR\fB\&.solv\fR
-\fIrule\fR\fB\&.solv\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to solver object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id;\fR /* read only */
-\fI$rule\fR\fB\->{id}\fR
-\fIrule\fR\fB\&.id\fR
-\fIrule\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The id of the rule\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint type;\fR /* read only */
-\fI$rule\fR\fB\->{type}\fR
-\fIrule\fR\fB\&.type\fR
-\fIrule\fR\fB\&.type\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The basic type of the rule\&. See the constant section of the solver class for the type list\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRuleinfo info()\fR
-my \fI$ruleinfo\fR \fB=\fR \fI$rule\fR\fB\->info()\fR;
-\fIruleinfo\fR \fB=\fR \fIrule\fR\fB\&.info()\fR
-\fIruleinfo\fR \fB=\fR \fIrule\fR\fB\&.info()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a Ruleinfo object that contains information about why the rule was created\&. But see the allinfos() method below\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRuleinfo *allinfos()\fR
-my \fI@ruleinfos\fR \fB=\fR \fI$rule\fR\fB\->allinfos()\fR;
-\fIruleinfos\fR \fB=\fR \fIrule\fR\fB\&.allinfos()\fR
-\fIruleinfos\fR \fB=\fR \fIrule\fR\fB\&.allinfos()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-As the same dependency rule can get created because of multiple dependencies, one Ruleinfo is not enough to describe the reason\&. Thus the allinfos() method returns an array of all infos about a rule\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<equality>\fR
-\fBif (\fR\fI$rule1\fR \fB==\fR \fI$rule2\fR\fB)\fR
-\fBif\fR \fIrule1\fR \fB==\fR \fIrule2\fR\fB:\fR
-\fBif\fR \fIrule1\fR \fB==\fR \fIrule2\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Two rules are equal if they belong to the same solver and have the same id\&.
-.SH "THE RULEINFO CLASS"
-.sp
-A Ruleinfo describes one reason why a rule was created\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolver *solv;\fR /* read only */
-\fI$ruleinfo\fR\fB\->{solv}\fR
-\fIruleinfo\fR\fB\&.solv\fR
-\fIruleinfo\fR\fB\&.solv\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to solver object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint type;\fR /* read only */
-\fI$ruleinfo\fR\fB\->{type}\fR
-\fIruleinfo\fR\fB\&.type\fR
-\fIruleinfo\fR\fB\&.type\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The type of the ruleinfo\&. See the constant section of the solver class for the rule type list and the special type list\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDep *dep;\fR /* read only */
-\fI$ruleinfo\fR\fB\->{dep}\fR
-\fIruleinfo\fR\fB\&.dep\fR
-\fIruleinfo\fR\fB\&.dep\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The dependency leading to the creation of the rule\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDep *dep_id;\fR /* read only */
-\fI$ruleinfo\fR\fB\->{\*(Aqdep_id\*(Aq}\fR
-\fIruleinfo\fR\fB\&.dep_id\fR
-\fIruleinfo\fR\fB\&.dep_id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The Id of the dependency leading to the creation of the rule, or zero\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *solvable;\fR /* read only */
-\fI$ruleinfo\fR\fB\->{solvable}\fR
-\fIruleinfo\fR\fB\&.solvable\fR
-\fIruleinfo\fR\fB\&.solvable\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The involved Solvable, e\&.g\&. the one containing the dependency\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *othersolvable;\fR /* read only */
-\fI$ruleinfo\fR\fB\->{othersolvable}\fR
-\fIruleinfo\fR\fB\&.othersolvable\fR
-\fIruleinfo\fR\fB\&.othersolvable\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The other involved Solvable (if any), e\&.g\&. the one containing providing the dependency for conflicts\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *problemstr()\fR;
-my \fI$str\fR \fB=\fR \fI$ruleinfo\fR\fB\->problemstr()\fR;
-\fIstr\fR \fB=\fR \fIruleinfo\fR\fB\&.problemstr()\fR
-\fIstr\fR \fB=\fR \fIruleinfo\fR\fB\&.problemstr()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-A string describing the ruleinfo from a problem perspective\&. This probably only makes sense if the rule is part of a problem\&.
-.SH "THE SOLUTION CLASS"
-.sp
-A solution solves one specific problem\&. It consists of multiple solution elements that all need to be executed\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolver *solv;\fR /* read only */
-\fI$solution\fR\fB\->{solv}\fR
-\fIsolution\fR\fB\&.solv\fR
-\fIsolution\fR\fB\&.solv\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to solver object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId problemid;\fR /* read only */
-\fI$solution\fR\fB\->{problemid}\fR
-\fIsolution\fR\fB\&.problemid\fR
-\fIsolution\fR\fB\&.problemid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Id of the problem the solution solves\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id;\fR /* read only */
-\fI$solution\fR\fB\->{id}\fR
-\fIsolution\fR\fB\&.id\fR
-\fIsolution\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Id of the solution\&. The first solution has Id 1, they are numbered consecutively\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolutionelement *elements(bool\fR \fIexpandreplaces\fR \fB= 0)\fR
-my \fI@solutionelements\fR \fB=\fR \fI$solution\fR\fB\->elements()\fR;
-\fIsolutionelements\fR \fB=\fR \fIsolution\fR\fB\&.elements()\fR
-\fIsolutionelements\fR \fB=\fR \fIsolution\fR\fB\&.elements()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return an array containing the elements describing what needs to be done to implement the specific solution\&. If expandreplaces is true, elements of type SOLVER_SOLUTION_REPLACE will be replaced by one or more elements replace elements describing the policy mismatches\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint element_count()\fR
-my \fI$cnt\fR \fB=\fR \fI$solution\fR\fB\->solution_count()\fR;
-\fIcnt\fR \fB=\fR \fIsolution\fR\fB\&.element_count()\fR
-\fIcnt\fR \fB=\fR \fIsolution\fR\fB\&.element_count()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the number of solution elements without creating objects\&. Note that the count does not match the number of objects returned by the elements() method of expandreplaces is set to true\&.
-.SH "THE SOLUTIONELEMENT CLASS"
-.sp
-A solution element describes a single action of a solution\&. The action is always either to remove one specific job or to add a new job that installs or erases a single specific package\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolver *solv;\fR /* read only */
-\fI$solutionelement\fR\fB\->{solv}\fR
-\fIsolutionelement\fR\fB\&.solv\fR
-\fIsolutionelement\fR\fB\&.solv\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to solver object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId problemid;\fR /* read only */
-\fI$solutionelement\fR\fB\->{problemid}\fR
-\fIsolutionelement\fR\fB\&.problemid\fR
-\fIsolutionelement\fR\fB\&.problemid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Id of the problem the element (partly) solves\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId solutionid;\fR /* read only */
-\fI$solutionelement\fR\fB\->{solutionid}\fR
-\fIsolutionelement\fR\fB\&.solutionid\fR
-\fIsolutionelement\fR\fB\&.solutionid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Id of the solution the element is a part of\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id;\fR /* read only */
-\fI$solutionelement\fR\fB\->{id}\fR
-\fIsolutionelement\fR\fB\&.id\fR
-\fIsolutionelement\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Id of the solution element\&. The first element has Id 1, they are numbered consecutively\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId type;\fR /* read only */
-\fI$solutionelement\fR\fB\->{type}\fR
-\fIsolutionelement\fR\fB\&.type\fR
-\fIsolutionelement\fR\fB\&.type\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Type of the solution element\&. See the constant section of the solver class for the existing types\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *solvable;\fR /* read only */
-\fI$solutionelement\fR\fB\->{solvable}\fR
-\fIsolutionelement\fR\fB\&.solvable\fR
-\fIsolutionelement\fR\fB\&.solvable\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The installed solvable that needs to be replaced for replacement elements\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *replacement;\fR /* read only */
-\fI$solutionelement\fR\fB\->{replacement}\fR
-\fIsolutionelement\fR\fB\&.replacement\fR
-\fIsolutionelement\fR\fB\&.replacement\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The solvable that needs to be installed to fix the problem\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint jobidx;\fR /* read only */
-\fI$solutionelement\fR\fB\->{jobidx}\fR
-\fIsolutionelement\fR\fB\&.jobidx\fR
-\fIsolutionelement\fR\fB\&.jobidx\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The index of the job that needs to be removed to fix the problem, or \-1 if the element is of another type\&. Note that it\(cqs better to change the job to SOLVER_NOOP type so that the numbering of other elements does not get disturbed\&. This method works both for types SOLVER_SOLUTION_JOB and SOLVER_SOLUTION_POOLJOB\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolutionelement *replaceelements()\fR
-my \fI@solutionelements\fR \fB=\fR \fI$solutionelement\fR\fB\->replaceelements()\fR;
-\fIsolutionelements\fR \fB=\fR \fIsolutionelement\fR\fB\&.replaceelements()\fR
-\fIsolutionelements\fR \fB=\fR \fIsolutionelement\fR\fB\&.replaceelements()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-If the solution element is of type SOLVER_SOLUTION_REPLACE, return an array of elements describing the policy mismatches, otherwise return a copy of the element\&. See also the \(lqexpandreplaces\(rq option in the solution\(cqs elements() method\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint illegalreplace()\fR
-my \fI$illegal\fR \fB=\fR \fI$solutionelement\fR\fB\->illegalreplace()\fR;
-\fIillegal\fR \fB=\fR \fIsolutionelement\fR\fB\&.illegalreplace()\fR
-\fIillegal\fR \fB=\fR \fIsolutionelement\fR\fB\&.illegalreplace()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return an integer that contains the policy mismatch bits or\-ed together, or zero if there was no policy mismatch\&. See the policy error constants in the solver class\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBJob Job()\fR
-my \fI$job\fR \fB=\fR \fI$solutionelement\fR\fB\->Job()\fR;
-\fIillegal\fR \fB=\fR \fIsolutionelement\fR\fB\&.Job()\fR
-\fIillegal\fR \fB=\fR \fIsolutionelement\fR\fB\&.Job()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a job that implements the solution element\&. Add this job to the array of jobs for all elements of type different to SOLVER_SOLUTION_JOB and SOLVER_SOLUTION_POOLJOB\&. For the later two, a SOLVER_NOOB Job is created, you should replace the old job with the new one\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *str()\fR
-my \fI$str\fR \fB=\fR \fI$solutionelement\fR\fB\->str()\fR;
-\fIstr\fR \fB=\fR \fIsolutionelement\fR\fB\&.str()\fR
-\fIstr\fR \fB=\fR \fIsolutionelement\fR\fB\&.str()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-A string describing the change the solution element consists of\&.
-.SH "THE TRANSACTION CLASS"
-.sp
-Transactions describe the output of a solver run\&. A transaction contains a number of transaction elements, each either the installation of a new package or the removal of an already installed package\&. The Transaction class supports a classify() method that puts the elements into different groups so that a transaction can be presented to the user in a meaningful way\&.
-.SS "CONSTANTS"
-.sp
-Transaction element types, both active and passive
-.PP
-\fBSOLVER_TRANSACTION_IGNORE\fR
-.RS 4
-This element does nothing\&. Used to map element types that do not match the view mode\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_INSTALL\fR
-.RS 4
-This element installs a package\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_ERASE\fR
-.RS 4
-This element erases a package\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_MULTIINSTALL\fR
-.RS 4
-This element installs a package with a different version keeping the other versions installed\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_MULTIREINSTALL\fR
-.RS 4
-This element reinstalls an installed package keeping the other versions installed\&.
-.RE
-.sp
-Transaction element types, active view
-.PP
-\fBSOLVER_TRANSACTION_REINSTALL\fR
-.RS 4
-This element re\-installs a package, i\&.e\&. installs the same package again\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_CHANGE\fR
-.RS 4
-This element installs a package with same name, version, architecture but different content\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_UPGRADE\fR
-.RS 4
-This element installs a newer version of an installed package\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_DOWNGRADE\fR
-.RS 4
-This element installs an older version of an installed package\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_OBSOLETES\fR
-.RS 4
-This element installs a package that obsoletes an installed package\&.
-.RE
-.sp
-Transaction element types, passive view
-.PP
-\fBSOLVER_TRANSACTION_REINSTALLED\fR
-.RS 4
-This element re\-installs a package, i\&.e\&. installs the same package again\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_CHANGED\fR
-.RS 4
-This element replaces an installed package with one of the same name, version, architecture but different content\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_UPGRADED\fR
-.RS 4
-This element replaces an installed package with a new version\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_DOWNGRADED\fR
-.RS 4
-This element replaces an installed package with an old version\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_OBSOLETED\fR
-.RS 4
-This element replaces an installed package with a package that obsoletes it\&.
-.RE
-.sp
-Pseudo element types for showing extra information used by classify()
-.PP
-\fBSOLVER_TRANSACTION_ARCHCHANGE\fR
-.RS 4
-This element replaces an installed package with a package of a different architecture\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_VENDORCHANGE\fR
-.RS 4
-This element replaces an installed package with a package of a different vendor\&.
-.RE
-.sp
-Transaction mode flags
-.PP
-\fBSOLVER_TRANSACTION_SHOW_ACTIVE\fR
-.RS 4
-Filter for active view types\&. The default is to return passive view type, i\&.e\&. to show how the installed packages get changed\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_SHOW_OBSOLETES\fR
-.RS 4
-Do not map the obsolete view type into INSTALL/ERASE elements\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_SHOW_ALL\fR
-.RS 4
-If multiple packages replace an installed package, only the best of them is kept as OBSOLETE element, the other ones are mapped to INSTALL/ERASE elements\&. This is because most applications want to show just one package replacing the installed one\&. The SOLVER_TRANSACTION_SHOW_ALL makes the library keep all OBSOLETE elements\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_SHOW_MULTIINSTALL\fR
-.RS 4
-The library maps MULTIINSTALL elements to simple INSTALL elements\&. This flag can be used to disable the mapping\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_CHANGE_IS_REINSTALL\fR
-.RS 4
-Use this flag if you want to map CHANGE elements to the REINSTALL type\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE\fR
-.RS 4
-Use this flag if you want to map OBSOLETE elements to the UPGRADE type\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_MERGE_ARCHCHANGES\fR
-.RS 4
-Do not add extra categories for every architecture change, instead cumulate them in one category\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_MERGE_VENDORCHANGES\fR
-.RS 4
-Do not add extra categories for every vendor change, instead cumulate them in one category\&.
-.RE
-.PP
-\fBSOLVER_TRANSACTION_RPM_ONLY\fR
-.RS 4
-Special view mode that just returns IGNORE, ERASE, INSTALL, MULTIINSTALL elements\&. Useful if you want to find out what to feed to the underlying package manager\&.
-.RE
-.sp
-Transaction order flags
-.PP
-\fBSOLVER_TRANSACTION_KEEP_ORDERDATA\fR
-.RS 4
-Do not throw away the dependency graph used for ordering the transaction\&. This flag is needed if you want to do manual ordering\&.
-.RE
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool;\fR /* read only */
-\fI$trans\fR\fB\->{pool}\fR
-\fItrans\fR\fB\&.pool\fR
-\fItrans\fR\fB\&.pool\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to pool\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool isempty()\fR;
-\fI$trans\fR\fB\->isempty()\fR
-\fItrans\fR\fB\&.isempty()\fR
-\fItrans\fR\fB\&.isempty?\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Returns true if the transaction does not do anything, i\&.e\&. has no elements\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *newsolvables()\fR;
-my \fI@newsolvables\fR \fB=\fR \fI$trans\fR\fB\->newsolvables()\fR;
-\fInewsolvables\fR \fB=\fR \fItrans\fR\fB\&.newsolvables()\fR
-\fInewsolvables\fR \fB=\fR \fItrans\fR\fB\&.newsolvables()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return all packages that are to be installed by the transaction\&. These are the packages that need to be downloaded from the repositories\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *keptsolvables()\fR;
-my \fI@keptsolvables\fR \fB=\fR \fI$trans\fR\fB\->keptsolvables()\fR;
-\fIkeptsolvables\fR \fB=\fR \fItrans\fR\fB\&.keptsolvables()\fR
-\fIkeptsolvables\fR \fB=\fR \fItrans\fR\fB\&.keptsolvables()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return all installed packages that the transaction will keep installed\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *steps()\fR;
-my \fI@steps\fR \fB=\fR \fI$trans\fR\fB\->steps()\fR;
-\fIsteps\fR \fB=\fR \fItrans\fR\fB\&.steps()\fR
-\fIsteps\fR \fB=\fR \fItrans\fR\fB\&.steps()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return all solvables that need to be installed (if the returned solvable is not already installed) or erased (if the returned solvable is installed)\&. A step is also called a transaction element\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint steptype(Solvable *\fR\fIsolvable\fR\fB, int\fR \fImode\fR\fB)\fR
-my \fI$type\fR \fB=\fR \fI$trans\fR\fB\->steptype(\fR\fI$solvable\fR\fB,\fR \fI$mode\fR\fB)\fR;
-\fItype\fR \fB=\fR \fItrans\fR\fB\&.steptype(\fR\fIsolvable\fR\fB,\fR \fImode\fR\fB)\fR
-\fItype\fR \fB=\fR \fItrans\fR\fB\&.steptype(\fR\fIsolvable\fR\fB,\fR \fImode\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the transaction type of the specified solvable\&. See the CONSTANTS sections for the mode argument flags and the list of returned types\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBTransactionClass *classify(int\fR \fImode\fR \fB= 0)\fR
-my \fI@classes\fR \fB=\fR \fI$trans\fR\fB\->classify()\fR;
-\fIclasses\fR \fB=\fR \fItrans\fR\fB\&.classify()\fR
-\fIclasses\fR \fB=\fR \fItrans\fR\fB\&.classify()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Group the transaction elements into classes so that they can be displayed in a structured way\&. You can use various mapping mode flags to tweak the result to match your preferences, see the mode argument flag in the CONSTANTS section\&. See the TransactionClass class for how to deal with the returned objects\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable othersolvable(Solvable *\fR\fIsolvable\fR\fB)\fR;
-my \fI$other\fR \fB=\fR \fI$trans\fR\fB\->othersolvable(\fR\fI$solvable\fR\fB)\fR;
-\fIother\fR \fB=\fR \fItrans\fR\fB\&.othersolvable(\fR\fIsolvable\fR\fB)\fR
-\fIother\fR \fB=\fR \fItrans\fR\fB\&.othersolvable(\fR\fIsolvable\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the \(lqother\(rq solvable for a given solvable\&. For installed packages the other solvable is the best package with the same name that replaces the installed package, or the best package of the obsoleting packages if the package does not get replaced by one with the same name\&.
-.sp
-For to be installed packages, the \(lqother\(rq solvable is the best installed package with the same name that will be replaced, or the best packages of all the packages that are obsoleted if the new package does not replace a package with the same name\&.
-.sp
-Thus, the \(lqother\(rq solvable is normally the package that is also shown for a given package\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *allothersolvables(Solvable *\fR\fIsolvable\fR\fB)\fR;
-my \fI@others\fR \fB=\fR \fI$trans\fR\fB\->allothersolvables(\fR\fI$solvable\fR\fB)\fR;
-\fIothers\fR \fB=\fR \fItrans\fR\fB\&.allothersolvables(\fR\fIsolvable\fR\fB)\fR
-\fIothers\fR \fB=\fR \fItrans\fR\fB\&.allothersolvables(\fR\fIsolvable\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-For installed packages, returns all of the packages that replace us\&. For to be installed packages, returns all of the packages that the new package replaces\&. The special \(lqother\(rq solvable is always the first entry of the returned array\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint calc_installsizechange()\fR;
-my \fI$change\fR \fB=\fR \fI$trans\fR\fB\->calc_installsizechange()\fR;
-\fIchange\fR \fB=\fR \fItrans\fR\fB\&.calc_installsizechange()\fR
-\fIchange\fR \fB=\fR \fItrans\fR\fB\&.calc_installsizechange()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the size change of the installed system in kilobytes (kibibytes)\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid order(int\fR \fIflags\fR \fB= 0)\fR;
-\fI$trans\fR\fB\->order()\fR;
-\fItrans\fR\fB\&.order()\fR
-\fItrans\fR\fB\&.order()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Order the steps in the transactions so that dependent packages are updated before packages that depend on them\&. For rpm, you can also use rpmlib\(cqs ordering functionality, debian\(cqs dpkg does not provide a way to order a transaction\&.
-.SS "ACTIVE/PASSIVE VIEW"
-.sp
-Active view lists what new packages get installed, while passive view shows what happens to the installed packages\&. Most often there\(cqs not much difference between the two modes, but things get interesting if multiple packages get replaced by one new package\&. Say you have installed packages A\-1\-1 and B\-1\-1, and now install A\-2\-1 which has a new dependency that obsoletes B\&. The transaction elements will be
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-updated A\-1\-1 (other: A\-2\-1)
-obsoleted B\-1\-1 (other: A\-2\-1)
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-in passive mode, but
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-update A\-2\-1 (other: A\-1\-1)
-erase B
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-in active mode\&. If the mode contains SOLVER_TRANSACTION_SHOW_ALL, the passive mode list will be unchanged but the active mode list will just contain A\-2\-1\&.
-.SH "THE TRANSACTIONCLASS CLASS"
-.sp
-Objects of this type are returned by the classify() Transaction method\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBTransaction *transaction;\fR /* read only */
-\fI$class\fR\fB\->{transaction}\fR
-\fIclass\fR\fB\&.transaction\fR
-\fIclass\fR\fB\&.transaction\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to transaction object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint type;\fR /* read only */
-\fI$class\fR\fB\->{type}\fR
-\fIclass\fR\fB\&.type\fR
-\fIclass\fR\fB\&.type\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The type of the transaction elements in the class\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint count;\fR /* read only */
-\fI$class\fR\fB\->{count}\fR
-\fIclass\fR\fB\&.count\fR
-\fIclass\fR\fB\&.count\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The number of elements in the class\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *\fR\fIfromstr\fR;
-\fI$class\fR\fB\->{fromstr}\fR
-\fIclass\fR\fB\&.fromstr\fR
-\fIclass\fR\fB\&.fromstr\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The old vendor or architecture\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *\fR\fItostr\fR;
-\fI$class\fR\fB\->{tostr}\fR
-\fIclass\fR\fB\&.tostr\fR
-\fIclass\fR\fB\&.tostr\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The new vendor or architecture\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId\fR \fIfromid\fR;
-\fI$class\fR\fB\->{fromid}\fR
-\fIclass\fR\fB\&.fromid\fR
-\fIclass\fR\fB\&.fromid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The id of the old vendor or architecture\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId\fR \fItoid\fR;
-\fI$class\fR\fB\->{toid}\fR
-\fIclass\fR\fB\&.toid\fR
-\fIclass\fR\fB\&.toid\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The id of the new vendor or architecture\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid solvables()\fR;
-my \fI@solvables\fR \fB=\fR \fI$class\fR\fB\->solvables()\fR;
-\fIsolvables\fR \fB=\fR \fIclass\fR\fB\&.solvables()\fR
-\fIsolvables\fR \fB=\fR \fIclass\fR\fB\&.solvables()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the solvables for all transaction elements in the class\&.
-.SH "CHECKSUMS"
-.sp
-Checksums (also called hashes) are used to make sure that downloaded data is not corrupt and also as a fingerprint mechanism to check if data has changed\&.
-.SS "CLASS METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBChksum Chksum(Id\fR \fItype\fR\fB)\fR
-my \fI$chksum\fR \fB= solv::Chksum\->new(\fR\fI$type\fR\fB)\fR;
-\fIchksum\fR \fB= solv\&.Chksum(\fR\fItype\fR\fB)\fR
-\fIchksum\fR \fB= Solv::Chksum\&.new(\fR\fItype\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a checksum object\&. Currently the following types are supported:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBREPOKEY_TYPE_MD5\fR
-\fBREPOKEY_TYPE_SHA1\fR
-\fBREPOKEY_TYPE_SHA256\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-These keys are constants in the \fBsolv\fR class\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBChksum Chksum(Id\fR \fItype\fR\fB, const char *\fR\fIhex\fR\fB)\fR
-my \fI$chksum\fR \fB= solv::Chksum\->new(\fR\fI$type\fR\fB,\fR \fI$hex\fR\fB)\fR;
-\fIchksum\fR \fB= solv\&.Chksum(\fR\fItype\fR\fB,\fR \fIhex\fR\fB)\fR
-\fIchksum\fR \fB= Solv::Chksum\&.new(\fR\fItype\fR\fB,\fR \fIhex\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create an already finalized checksum object from a hex string\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBChksum Chksum_from_bin(Id\fR \fItype\fR\fB, char *\fR\fIbin\fR\fB)\fR
-my \fI$chksum\fR \fB= solv::Chksum\->from_bin(\fR\fI$type\fR\fB,\fR \fI$bin\fR\fB)\fR;
-\fIchksum\fR \fB= solv\&.Chksum\&.from_bin(\fR\fItype\fR\fB,\fR \fIbin\fR\fB)\fR
-\fIchksum\fR \fB= Solv::Chksum\&.from_bin(\fR\fItype\fR\fB,\fR \fIbin\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create an already finalized checksum object from a binary checksum\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId type;\fR /* read only */
-\fI$chksum\fR\fB\->{type}\fR
-\fIchksum\fR\fB\&.type\fR
-\fIchksum\fR\fB\&.type\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the type of the checksum object\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add(const char *\fR\fIstr\fR\fB)\fR
-\fI$chksum\fR\fB\->add(\fR\fI$str\fR\fB)\fR;
-\fIchksum\fR\fB\&.add(\fR\fIstr\fR\fB)\fR
-\fIchksum\fR\fB\&.add(\fR\fIstr\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a (binary) string to the checksum\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add_fp(FILE *\fR\fIfp\fR\fB)\fR
-\fI$chksum\fR\fB\->add_fp(\fR\fI$file\fR\fB)\fR;
-\fIchksum\fR\fB\&.add_fp(\fR\fIfile\fR\fB)\fR
-\fIchksum\fR\fB\&.add_fp(\fR\fIfile\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the contents of a file to the checksum\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add_stat(const char *\fR\fIfilename\fR\fB)\fR
-\fI$chksum\fR\fB\->add_stat(\fR\fI$filename\fR\fB)\fR;
-\fIchksum\fR\fB\&.add_stat(\fR\fIfilename\fR\fB)\fR
-\fIchksum\fR\fB\&.add_stat(\fR\fIfilename\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Stat the file and add the dev/ino/size/mtime member to the checksum\&. If the stat fails, the members are zeroed\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add_fstat(int\fR \fIfd\fR\fB)\fR
-\fI$chksum\fR\fB\->add_fstat(\fR\fI$fd\fR\fB)\fR;
-\fIchksum\fR\fB\&.add_fstat(\fR\fIfd\fR\fB)\fR
-\fIchksum\fR\fB\&.add_fstat(\fR\fIfd\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Same as add_stat, but instead of the filename a file descriptor is used\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBunsigned char *raw()\fR
-my \fI$raw\fR \fB=\fR \fI$chksum\fR\fB\->raw()\fR;
-\fIraw\fR \fB=\fR \fIchksum\fR\fB\&.raw()\fR
-\fIraw\fR \fB=\fR \fIchksum\fR\fB\&.raw()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Finalize the checksum and return the result as raw bytes\&. This means that the result can contain NUL bytes or unprintable characters\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *hex()\fR
-my \fI$raw\fR \fB=\fR \fI$chksum\fR\fB\->hex()\fR;
-\fIraw\fR \fB=\fR \fIchksum\fR\fB\&.hex()\fR
-\fIraw\fR \fB=\fR \fIchksum\fR\fB\&.hex()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Finalize the checksum and return the result as hex string\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *typestr()\fR
-my \fI$typestr\fR \fB=\fR \fI$chksum\fR\fB\->typestr()\fR;
-\fItypestr\fR \fB=\fR \fIchksum\fR\fB\&.typestr\fR
-\fItypestr\fR \fB=\fR \fIchksum\fR\fB\&.typestr\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the type of the checksum as a string, e\&.g\&. "sha256"\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<equality>\fR
-\fBif (\fR\fI$chksum1\fR \fB==\fR \fI$chksum2\fR\fB)\fR
-\fBif\fR \fIchksum1\fR \fB==\fR \fIchksum2\fR\fB:\fR
-\fBif\fR \fIchksum1\fR \fB==\fR \fIchksum2\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Checksums are equal if they are of the same type and the finalized results are the same\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<stringification>\fR
-my \fI$str\fR \fB=\fR \fI$chksum\fR\fB\->str\fR;
-\fIstr\fR \fB= str(\fR\fIchksum\fR\fB)\fR
-\fIstr\fR \fB=\fR \fIchksum\fR\fB\&.to_s\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-If the checksum is finished, the checksum is returned as "<type>:<hex>" string\&. Otherwise "<type>:unfinished" is returned\&.
-.SH "FILE MANAGEMENT"
-.sp
-This functions were added because libsolv uses standard \fBFILE\fR pointers to read/write files, but languages like perl have their own implementation of files\&. The libsolv functions also support decompression and compression, the algorithm is selected by looking at the file name extension\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBFILE *xfopen(char *\fR\fIfn\fR\fB, char *\fR\fImode\fR \fB= "r")\fR
-my \fI$file\fR \fB= solv::xfopen(\fR\fI$path\fR\fB)\fR;
-\fIfile\fR \fB= solv\&.xfopen(\fR\fIpath\fR\fB)\fR
-\fIfile\fR \fB= Solv::xfopen(\fR\fIpath\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Open a file at the specified path\&. The mode argument is passed on to the stdio library\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBFILE *xfopen_fd(char *\fR\fIfn\fR\fB, int\fR \fIfileno\fR\fB)\fR
-my \fI$file\fR \fB= solv::xfopen_fd(\fR\fI$path\fR\fB,\fR \fI$fileno\fR\fB)\fR;
-\fIfile\fR \fB= solv\&.xfopen_fd(\fR\fIpath\fR\fB,\fR \fIfileno\fR\fB)\fR
-\fIfile\fR \fB= Solv::xfopen_fd(\fR\fIpath\fR\fB,\fR \fIfileno\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a file handle from the specified file descriptor\&. The path argument is only used to select the correct (de\-)compression algorithm, use an empty path if you want to make sure to read/write raw data\&. The file descriptor is dup()ed before the file handle is created\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint fileno()\fR
-my \fI$fileno\fR \fB=\fR \fI$file\fR\fB\->fileno()\fR;
-\fIfileno\fR \fB=\fR \fIfile\fR\fB\&.fileno()\fR
-\fIfileno\fR \fB=\fR \fIfile\fR\fB\&.fileno()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return file file descriptor of the file\&. If the file is not open, \-1 is returned\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid cloexec(bool\fR \fIstate\fR\fB)\fR
-\fI$file\fR\fB\->cloexec(\fR\fI$state\fR\fB)\fR
-\fIfile\fR\fB\&.cloexec(\fR\fIstate\fR\fB)\fR
-\fIfile\fR\fB\&.cloexec(\fR\fIstate\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the close\-on\-exec flag of the file descriptor\&. The xfopen function returns files with close\-on\-exec turned on, so if you want to pass a file to some other process you need to call cloexec(0) before calling exec\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint dup()\fR
-my \fI$fileno\fR \fB=\fR \fI$file\fR\fB\->dup()\fR;
-\fIfileno\fR \fB=\fR \fIfile\fR\fB\&.dup()\fR
-\fIfileno\fR \fB=\fR \fIfile\fR\fB\&.dup()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a copy of the descriptor of the file\&. If the file is not open, \-1 is returned\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool flush()\fR
-\fI$file\fR\fB\->flush()\fR;
-\fIfile\fR\fB\&.flush()\fR
-\fIfile\fR\fB\&.flush()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Flush the file\&. Returns false if there was an error\&. Flushing a closed file always returns true\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool close()\fR
-\fI$file\fR\fB\->close()\fR;
-\fIfile\fR\fB\&.close()\fR
-\fIfile\fR\fB\&.close()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Close the file\&. This is needed for languages like Ruby that do not destruct objects right after they are no longer referenced\&. In that case, it is good style to close open files so that the file descriptors are freed right away\&. Returns false if there was an error\&.
-.SH "THE REPODATA CLASS"
-.sp
-The Repodata stores attributes for packages and the repository itself, each repository can have multiple repodata areas\&. You normally only need to directly access them if you implement lazy downloading of repository data\&. Repodata areas are created by calling the repository\(cqs add_repodata() method or by using repo_add methods without the REPO_REUSE_REPODATA or REPO_USE_LOADING flag\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepo *repo;\fR /* read only */
-\fI$data\fR\fB\->{repo}\fR
-\fIdata\fR\fB\&.repo\fR
-\fIdata\fR\fB\&.repo\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to repository object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId id;\fR /* read only */
-\fI$data\fR\fB\->{id}\fR
-\fIdata\fR\fB\&.id\fR
-\fIdata\fR\fB\&.id\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-The id of the repodata area\&. Repodata ids of different repositories overlap\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBinternalize()\fR;
-\fI$data\fR\fB\->internalize()\fR;
-\fIdata\fR\fB\&.internalize()\fR
-\fIdata\fR\fB\&.internalize()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Internalize newly added data\&. The lookup functions will only see the new data after it has been internalized\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool write(FILE *\fR\fIfp\fR\fB)\fR;
-\fI$data\fR\fB\->write(\fR\fI$fp\fR\fB)\fR;
-\fIdata\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
-\fIdata\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Write the contents of the repodata area as solv file\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool add_solv(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR;
-\fI$data\fR\fB\->add_solv(\fR\fI$fp\fR\fB)\fR;
-\fIdata\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
-\fIdata\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Replace a stub repodata object with the data from a solv file\&. This method automatically adds the REPO_USE_LOADING flag\&. It should only be used from a load callback\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid create_stubs()\fR;
-\fI$data\fR\fB\->create_stubs()\fR
-\fIdata\fR\fB\&.create_stubs()\fR
-\fIdata\fR\fB\&.create_stubs()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create stub repodatas from the information stored in the repodata meta area\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid extend_to_repo()\fR;
-\fI$data\fR\fB\->extend_to_repo()\fR;
-\fIdata\fR\fB\&.extend_to_repo()\fR
-\fIdata\fR\fB\&.extend_to_repo()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Extend the repodata so that it has the same size as the repo it belongs to\&. This method is only needed when switching to a just written repodata extension to make the repodata match the written extension (which is always of the size of the repo)\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fB<equality>\fR
-\fBif (\fR\fI$data1\fR \fB==\fR \fI$data2\fR\fB)\fR
-\fBif\fR \fIdata1\fR \fB==\fR \fIdata2\fR\fB:\fR
-\fBif\fR \fIdata1\fR \fB==\fR \fIdata2\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Two repodata objects are equal if they belong to the same repository and have the same id\&.
-.SS "DATA RETRIEVAL METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *lookup_str(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI$string\fR \fB=\fR \fI$data\fR\fB\->lookup_str(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fIstring\fR \fB=\fR \fIdata\fR\fB\&.lookup_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fIstring\fR \fB=\fR \fIdata\fR\fB\&.lookup_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId *lookup_idarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI@ids\fR \fB=\fR \fI$data\fR\fB\->lookup_idarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fIids\fR \fB=\fR \fIdata\fR\fB\&.lookup_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fIids\fR \fB=\fR \fIdata\fR\fB\&.lookup_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBChksum lookup_checksum(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI$chksum\fR \fB=\fR \fI$data\fR\fB\->lookup_checksum(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
-\fIchksum\fR \fB=\fR \fIdata\fR\fB\&.lookup_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-\fIchksum\fR \fB=\fR \fIdata\fR\fB\&.lookup_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Lookup functions\&. Return the data element stored in the specified solvable\&. The methods probably only make sense to retrieve data from the special SOLVID_META solvid that stores repodata meta information\&.
-.SS "DATA STORAGE METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid set_id(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, DepId\fR \fIid\fR\fB)\fR;
-\fI$data\fR\fB\->set_id(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$id\fR\fB)\fR;
-\fIdata\fR\fB\&.set_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
-\fIdata\fR\fB\&.set_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid set_str(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fIstr\fR\fB)\fR;
-\fI$data\fR\fB\->set_str(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$str\fR\fB)\fR;
-\fIdata\fR\fB\&.set_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
-\fIdata\fR\fB\&.set_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid set_poolstr(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fIstr\fR\fB)\fR;
-\fI$data\fR\fB\->set_poolstr(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$str\fR\fB)\fR;
-\fIdata\fR\fB\&.set_poolstr(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
-\fIdata\fR\fB\&.set_poolstr(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid set_checksum(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Chksum *\fR\fIchksum\fR\fB)\fR;
-\fI$data\fR\fB\->set_checksum(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$chksum\fR\fB)\fR;
-\fIdata\fR\fB\&.set_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIchksum\fR\fB)\fR
-\fIdata\fR\fB\&.set_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIchksum\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add_idarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, DepId\fR \fIid\fR\fB)\fR;
-\fI$data\fR\fB\->add_idarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$id\fR\fB)\fR;
-\fIdata\fR\fB\&.add_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
-\fIdata\fR\fB\&.add_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId new_handle()\fR;
-my \fI$handle\fR \fB=\fR \fI$data\fR\fB\->new_handle()\fR;
-\fIhandle\fR \fB=\fR \fIdata\fR\fB\&.new_handle()\fR
-\fIhandle\fR \fB=\fR \fIdata\fR\fB\&.new_handle()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid add_flexarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fIhandle\fR\fB)\fR;
-\fI$data\fR\fB\->add_flexarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$handle\fR\fB)\fR;
-\fIdata\fR\fB\&.add_flexarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIhandle\fR\fB)\fR
-\fIdata\fR\fB\&.add_flexarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIhandle\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Data storage methods\&. Probably only useful to store data in the special SOLVID_META solvid that stores repodata meta information\&. Note that repodata areas can have their own Id pool (see the REPO_LOCALPOOL flag), so be careful if you need to store ids\&. Arrays are created by calling the add function for every element\&. A flexarray is an array of sub\-structures, call new_handle to create a new structure, use the handle as solvid to fill the structure with data and call add_flexarray to put the structure in an array\&.
-.SH "THE DATAPOS CLASS"
-.sp
-Datapos objects describe a specific position in the repository data area\&. Thus they are only valid until the repository is modified in some way\&. Datapos objects can be created by the pos() and parentpos() methods of a Datamatch object or by accessing the \(lqmeta\(rq attribute of a repository\&.
-.SS "ATTRIBUTES"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBRepo *repo;\fR /* read only */
-\fI$data\fR\fB\->{repo}\fR
-\fIdata\fR\fB\&.repo\fR
-\fIdata\fR\fB\&.repo\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Back pointer to repository object\&.
-.SS "METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDataiterator(Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR\fB, int\fR \fIflags\fR\fB)\fR
-my \fI$di\fR \fB=\fR \fI$datapos\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIdi\fR \fB=\fR \fIdatapos\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIdi\fR \fB=\fR \fIdatapos\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a Dataiterator at the position of the datapos object\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *lookup_deltalocation(unsigned int *\fR\fIOUTPUT\fR\fB)\fR;
-my \fB(\fR\fI$location\fR\fB,\fR \fI$medianr\fR\fB) =\fR \fI$datapos\fR\fB\->lookup_deltalocation()\fR;
-\fIlocation\fR\fB,\fR \fImedianr\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltalocation()\fR
-\fIlocation\fR\fB,\fR \fImedianr\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltalocation()\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a tuple containing the on\-media location and an optional media number for a delta rpm\&. This obviously only works if the data position points to structure describing a delta rpm\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *lookup_deltaseq()\fR;
-my \fI$seq\fR \fB=\fR \fI$datapos\fR\fB\->lookup_deltaseq()\fR;
-\fIseq\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltaseq()\fR;
-\fIseq\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltaseq()\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the delta rpm sequence from the structure describing a delta rpm\&.
-.SS "DATA RETRIEVAL METHODS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *lookup_str(Id\fR \fIkeyname\fR\fB)\fR
-my \fI$string\fR \fB=\fR \fI$datapos\fR\fB\->lookup_str(\fR\fI$keyname\fR\fB)\fR;
-\fIstring\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_str(\fR\fIkeyname\fR\fB)\fR
-\fIstring\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_str(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId lookup_id(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
-my \fI$id\fR \fB=\fR \fI$datapos\fR\fB\->lookup_id(\fR\fI$keyname\fR\fB)\fR;
-\fIid\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_id(\fR\fIkeyname\fR\fB)\fR
-\fIid\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_id(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBunsigned long long lookup_num(Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInotfound\fR \fB= 0)\fR
-my \fI$num\fR \fB=\fR \fI$datapos\fR\fB\->lookup_num(\fR\fI$keyname\fR\fB)\fR;
-\fInum\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_num(\fR\fIkeyname\fR\fB)\fR
-\fInum\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_num(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBbool lookup_void(Id\fR \fIkeyname\fR\fB)\fR
-my \fI$bool\fR \fB=\fR \fI$datapos\fR\fB\->lookup_void(\fR\fI$keyname\fR\fB)\fR;
-\fIbool\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_void(\fR\fIkeyname\fR\fB)\fR
-\fIbool\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_void(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId *lookup_idarray(Id\fR \fIkeyname\fR\fB)\fR
-my \fI@ids\fR \fB=\fR \fI$datapos\fR\fB\->lookup_idarray(\fR\fI$keyname\fR\fB)\fR;
-\fIids\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_idarray(\fR\fIkeyname\fR\fB)\fR
-\fIids\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_idarray(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBChksum lookup_checksum(Id\fR \fIkeyname\fR\fB)\fR
-my \fI$chksum\fR \fB=\fR \fI$datapos\fR\fB\->lookup_checksum(\fR\fI$keyname\fR\fB)\fR;
-\fIchksum\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_checksum(\fR\fIkeyname\fR\fB)\fR
-\fIchksum\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_checksum(\fR\fIkeyname\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Lookup functions\&. Note that the returned Ids are always translated into the Ids of the global pool even if the repodata area contains its own pool\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBDataiterator Dataiterator(Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
-my \fI$di\fR \fB=\fR \fI$datapos\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
-\fIdi\fR \fB=\fR \fIdatapos\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-\fIdi\fR \fB=\fR \fIdatapos\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
-\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Iterate over the matching data elements\&. See the Dataiterator class for more information\&.
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-Libsolv-Bindings(3)
-===================
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-libsolv-bindings - access libsolv from perl/python/ruby
-
-
-Description
------------
-Libsolv's language bindings offer an abstract, object orientated interface
-to the library. The supported languages are currently perl, python, and ruby.
-All example code (except in the specifics sections, of course) lists first
-the ``C-ish'' interface, then the syntax for perl, python, and ruby (in that
-order).
-
-
-Perl Specifics
---------------
-Libsolv's perl bindings can be loaded with the following statement:
-
- use solv;
-
-Objects are either created by calling the new() method on a class or they
-are returned by calling methods on other objects.
-
- my $pool = solv::Pool->new();
- my $repo = $pool->add_repo("my_first_repo");
-
-Swig encapsulates all objects as tied hashes, thus the attributes can be
-accessed by treating the object as standard hash reference:
-
- $pool->{appdata} = 42;
- printf "appdata is %d\n", $pool->{appdata};
-
-A special exception to this are iterator objects, they are encapsulated as
-tied arrays so that it is possible to iterate with a for() statement:
-
- my $iter = $pool->solvables_iter();
- for my $solvable (@$iter) { ... };
-
-As a downside of this approach, iterator objects cannot have attributes.
-
-If an array needs to be passed to a method it is usually done by reference,
-if a method returns an array it returns it on the stack:
-
- my @problems = $solver->solve(\@jobs);
-
-Due to a bug in swig, stringification does not work for libsolv's objects.
-Instead, you have to call the object's str() method.
-
- print $dep->str() . "\n";
-
-Swig implements all constants as numeric variables (instead of the more
-natural constant subs), so don't forget the leading ``$'' when accessing a
-constant. Also do not forget to prepend the namespace of the constant:
-
- $pool->set_flag($solv::Pool::POOL_FLAG_OBSOLETEUSESCOLORS, 1);
-
-
-Python Specifics
-----------------
-The python bindings can be loaded with:
-
- import solv
-
-Objects are either created by calling the constructor method for a class or they
-are returned by calling methods on other objects.
-
- pool = solv.Pool()
- repo = pool.add_repo("my_first_repo")
-
-Attributes can be accessed as usual:
-
- pool.appdata = 42
- print "appdata is %d" % (pool.appdata)
-
-Iterators also work as expected:
-
- for solvable in pool.solvables_iter():
-
-Arrays are passed and returned as list objects:
-
- jobs = []
- problems = solver.solve(jobs)
-
-The bindings define stringification for many classes, some also have a
-__repr__ method to ease debugging.
-
- print dep
- print repr(repo)
-
-Constants are attributes of the classes:
-
- pool.set_flag(solv.Pool.POOL_FLAG_OBSOLETEUSESCOLORS, 1);
-
-
-Ruby Specifics
---------------
-The ruby bindings can be loaded with:
-
- require 'solv'
-
-Objects are either created by calling the new method on a class or they
-are returned by calling methods on other objects. Note that all classes start
-with an uppercase letter in ruby, so the class is called ``Solv''.
-
- pool = Solv::Pool.new
- repo = pool.add_repo("my_first_repo")
-
-Attributes can be accessed as usual:
-
- pool.appdata = 42
- puts "appdata is #{pool.appdata}"
-
-Iterators also work as expected:
-
- for solvable in pool.solvables_iter() do ...
-
-Arrays are passed and returned as array objects:
-
- jobs = []
- problems = solver.solve(jobs)
-
-Most classes define a to_s method, so objects can be easily stringified.
-Many also define an inspect() method.
-
- puts dep
- puts repo.inspect
-
-Constants live in the namespace of the class they belong to:
-
- pool.set_flag(Solv::Pool::POOL_FLAG_OBSOLETEUSESCOLORS, 1);
-
-Note that boolean methods have an added trailing ``?'', to be consistent with
-other ruby modules:
-
- puts "empty" if repo.isempty?
-
-
-Tcl Specifics
--------------
-Libsolv's tcl bindings can be loaded with the following statement:
-
- TCL package require solv
-
-Objects are either created by calling class name prefixed with ``new_'',
-or they are returned by calling methods on other objects.
-
- TCL set pool [solv::new_Pool]
- TCL set repo [$pool add_repo "my_first_repo"]
-
-Swig provides a ``cget'' method to read object attributes, and a
-``configure'' method to write them:
-
- TCL $pool configure -appdata 42
- TCL puts "appdata is [$pool cget -appdata]"
-
-The tcl bindings provide a little helper to work with iterators in
-a foreach style:
-
- TCL set iter [$pool solvables_iter]
- TCL solv::iter s $iter { ... }
-
-libsolv's arrays are mapped to tcl's lists:
-
- TCL set jobs [list $job1 $job2]
- TCL set problems [$solver solve $jobs]
- TCL puts "We have [llength $problems] problems..."
-
-Stringification is done by calling the object's ``str'' method.
-
- TCL puts [$dep str]
-
-There is one exception: you have to use ``stringify'' for Datamatch
-objects, as swig reports a clash with the ``str'' attribute.
-Some objects also support a ``=='' method for equality tests, and a
-``!='' method.
-
-Swig implements all constants as numeric variables, constants belonging
-to a libsolv class are prefixed with the class name:
-
- TCL $pool set_flag $solv::Pool_POOL_FLAG_OBSOLETEUSESCOLORS 1
- TCL puts [$solvable lookup_str $solv::SOLVABLE_SUMMARY]
-
-
-The Solv Class
---------------
-This is the main namespace of the library, you cannot create objects of this
-type but it contains some useful constants.
-
-=== CONSTANTS ===
-
-Relational flag constants, the first three can be or-ed together
-
-*REL_LT*::
-the ``less than'' bit
-
-*REL_EQ*::
-the ``equals to'' bit
-
-*REL_GT*::
-the ``greater than'' bit
-
-*REL_ARCH*::
-used for relations that describe an extra architecture filter, the
-version part of the relation is interpreted as architecture.
-
-Special Solvable Ids
-
-*SOLVID_META*::
-Access the meta section of a repository or repodata area. This is
-like an extra Solvable that has the Id SOLVID_META.
-
-*SOLVID_POS*::
-Use the data position stored inside of the pool instead of accessing
-some solvable by Id. The bindings have the Datapos objects as an
-abstraction mechanism, so you do not need this constant.
-
-Constant string Ids
-
-*ID_NULL*::
-Always zero
-
-*ID_EMPTY*::
-Always one, describes the empty string
-
-*SOLVABLE_NAME*::
-The keyname Id of the name of the solvable.
-
-*...*::
-see the libsolv-constantids manpage for a list of fixed Ids.
-
-
-The Pool Class
---------------
-The pool is libsolv's central resource manager. A pool consists of Solvables,
-Repositories, Dependencies, each indexed by Ids.
-
-=== CLASS METHODS ===
-
- Pool *Pool()
- my $pool = solv::Pool->new();
- pool = solv.Pool()
- pool = Solv::Pool.new()
-
-Create a new pool instance. In most cases you just need one pool.
-Note that the returned object "owns" the pool, i.e. if the object is
-freed, the pool is also freed. You can use the disown method to
-break this ownership relation.
-
-=== ATTRIBUTES ===
-
- void *appdata; /* read/write */
- $pool->{appdata}
- pool.appdata
- pool.appdata
-
-Application specific data that may be used in any way by the code using the
-pool.
-
- Solvable solvables[]; /* read only */
- my $solvable = $pool->{solvables}->[$solvid];
- solvable = pool.solvables[solvid]
- solvable = pool.solvables[solvid]
-
-Look up a Solvable by its id.
-
- Repo repos[]; /* read only */
- my $repo = $pool->{repos}->[$repoid];
- repo = pool.repos[repoid]
- repo = pool.repos[repoid]
-
-Look up a Repository by its id.
-
- Repo *installed; /* read/write */
- $pool->{installed} = $repo;
- pool.installed = repo
- pool.installed = repo
-
-Define which repository contains all the installed packages.
-
- const char *errstr; /* read only */
- my $err = $pool->{errstr};
- err = pool.errstr
- err = pool.errstr
-
-Return the last error string that was stored in the pool.
-
-=== CONSTANTS ===
-
-*POOL_FLAG_PROMOTEEPOCH*::
-Promote the epoch of the providing dependency to the requesting
-dependency if it does not contain an epoch. Used at some time
-in old rpm versions, modern systems should never need this.
-
-*POOL_FLAG_FORBIDSELFCONFLICTS*::
-Disallow the installation of packages that conflict with themselves.
-Debian always allows self-conflicting packages, rpm used to forbid
-them but switched to also allowing them recently.
-
-*POOL_FLAG_OBSOLETEUSESPROVIDES*::
-Make obsolete type dependency match against provides instead of
-just the name and version of packages. Very old versions of rpm
-used the name/version, then it got switched to provides and later
-switched back again to just name/version.
-
-*POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES*::
-An implicit obsoletes is the internal mechanism to remove the
-old package on an update. The default is to remove all packages
-with the same name, rpm-5 switched to also removing packages
-providing the same name.
-
-*POOL_FLAG_OBSOLETEUSESCOLORS*::
-Rpm's multilib implementation (used in RedHat and Fedora)
-distinguishes between 32bit and 64bit packages (the terminology
-is that they have a different color). If obsoleteusescolors is
-set, packages with different colors will not obsolete each other.
-
-*POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS*::
-Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if
-packages of the same name can be installed in parallel. For
-current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be
-false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true
-(this is the default if FEDORA is defined when libsolv is compiled).
-
-*POOL_FLAG_NOINSTALLEDOBSOLETES*::
-New versions of rpm consider the obsoletes of installed packages
-when checking for dependency, thus you may not install a package
-that is obsoleted by some other installed package, unless you
-also erase the other package.
-
-*POOL_FLAG_HAVEDISTEPOCH*::
-Mandriva added a new field called distepoch that gets checked in
-version comparison if the epoch/version/release of two packages
-are the same.
-
-*POOL_FLAG_NOOBSOLETESMULTIVERSION*::
-If a package is installed in multiversionmode, rpm used to ignore
-both the implicit obsoletes and the obsolete dependency of a
-package. This was changed to ignoring just the implicit obsoletes,
-thus you may install multiple versions of the same name, but
-obsoleted packages still get removed.
-
-*POOL_FLAG_ADDFILEPROVIDESFILTERED*::
-Make the addfileprovides method only add files from the standard
-locations (i.e. the ``bin'' and ``etc'' directories). This is
-useful if you have only few packages that use non-standard file
-dependencies, but you still want the fast speed that addfileprovides()
-generates.
-
-=== METHODS ===
-
- void free()
- $pool->free();
- pool.free()
- pool.free()
-
-Force a free of the pool. After this call, you must not access any object
-that still references the pool.
-
- void disown()
- $pool->disown();
- pool.disown()
- pool.disown()
-
-Break the ownership relation between the binding object and the pool. After
-this call, the pool will not get freed even if the object goes out of
-scope. This also means that you must manually call the free method to free
-the pool data.
-
- void setdebuglevel(int level)
- $pool->setdebuglevel($level);
- pool.setdebuglevel(level)
- pool.setdebuglevel(level)
-
-Set the debug level. A value of zero means no debug output, the higher the
-value, the more output is generated.
-
- int set_flag(int flag, int value)
- my $oldvalue = $pool->set_flag($flag, $value);
- oldvalue = pool.set_flag(flag, value)
- oldvalue = pool.set_flag(flag, value)
-
- int get_flag(int flag)
- my $value = $pool->get_flag($flag);
- value = pool.get_flag(flag)
- value = pool.get_flag(flag)
-
-Set/get a pool specific flag. The flags define how the system works, e.g. how
-the package manager treats obsoletes. The default flags should be sane for most
-applications, but in some cases you may want to tweak a flag, for example if
-you want to solv package dependencies for some other system than yours.
-
- void set_rootdir(const char *rootdir)
- $pool->set_rootdir(rootdir);
- pool.set_rootdir(rootdir)
- pool.set_rootdir(rootdir)
-
- const char *get_rootdir()
- my $rootdir = $pool->get_rootdir();
- rootdir = pool.get_rootdir()
- rootdir = pool.get_rootdir()
-
-Set/get the rootdir to use. This is useful if you want package management
-to work only in some directory, for example if you want to setup a chroot
-jail. Note that the rootdir will only be prepended to file paths if the
-*REPO_USE_ROOTDIR* flag is used.
-
- void setarch(const char *arch = 0)
- $pool->setarch();
- pool.setarch()
- pool.setarch()
-
-Set the architecture for your system. The architecture is used to determine
-which packages are installable. It defaults to the result of ``uname -m''.
-
- Repo add_repo(const char *name)
- $repo = $pool->add_repo($name);
- repo = pool.add_repo(name)
- repo = pool.add_repo(name)
-
-Add a Repository with the specified name to the pool. The repository is empty
-on creation, use the repository methods to populate it with packages.
-
- Repoiterator repos_iter()
- for my $repo (@{$pool->repos_iter()})
- for repo in pool.repos_iter():
- for repo in pool.repos_iter()
-
-Iterate over the existing repositories.
-
- Solvableiterator solvables_iter()
- for my $solvable (@{$pool->solvables_iter()})
- for solvable in pool.solvables_iter():
- for solvable in pool.solvables_iter()
-
-Iterate over the existing solvables.
-
- Dep Dep(const char *str, bool create = 1)
- my $dep = $pool->Dep($string);
- dep = pool.Dep(string)
- dep = pool.Dep(string)
-
-Create an object describing a string or dependency. If the string is currently
-not in the pool and _create_ is false, *undef*/*None*/*nil* is returned.
-
- void addfileprovides()
- $pool->addfileprovides();
- pool.addfileprovides()
- pool.addfileprovides()
-
- Id *addfileprovides_queue()
- my @ids = $pool->addfileprovides_queue();
- ids = pool.addfileprovides_queue()
- ids = pool.addfileprovides_queue()
-
-Some package managers like rpm allow dependencies on files contained in other
-packages. To allow libsolv to deal with those dependencies in an efficient way,
-you need to call the addfileprovides method after creating and reading all
-repositories. This method will scan all dependency for file names and then scan
-all packages for matching files. If a filename has been matched, it will be
-added to the provides list of the corresponding package. The
-addfileprovides_queue variant works the same way but returns an array
-containing all file dependencies. This information can be stored in the
-meta section of the repositories to speed up the next time the
-repository is loaded and addfileprovides is called.
-
- void createwhatprovides()
- $pool->createwhatprovides();
- pool.createwhatprovides()
- pool.createwhatprovides()
-
-Create the internal ``whatprovides'' hash over all of the provides of all
-packages. This method must be called before doing any lookups on provides.
-It's encouraged to do it right after all repos are set up, usually right after
-the call to addfileprovides().
-
- Solvable *whatprovides(DepId dep)
- my @solvables = $pool->whatprovides($dep);
- solvables = pool.whatprovides(dep)
- solvables = pool.whatprovides(dep)
-
-Return all solvables that provide the specified dependency. You can use either
-a Dep object or a simple Id as argument.
-
- Id *matchprovidingids(const char *match, int flags)
- my @ids = $pool->matchprovidingids($match, $flags);
- ids = pool.matchprovidingids(match, flags)
- ids = pool.matchprovidingids(match, flags)
-
-Search the names of all provides and return the ones matching the specified
-string. See the Dataiterator class for the allowed flags.
-
- Id towhatprovides(Id *ids)
- my $offset = $pool->towhatprovides(\@ids);
- offset = pool.towhatprovides(ids)
- offset = pool.towhatprovides(ids)
-
-``Internalize'' an array containing Ids. The returned value can be used to
-create solver jobs working on a specific set of packages. See the Solver class
-for more information.
-
- bool isknownarch(DepId id)
- my $bool = $pool->isknownarch($id);
- bool = pool.isknownarch(id)
- bool = pool.isknownarch?(id)
-
-Return true if the specified Id describes a known architecture.
-
- Solver Solver()
- my $solver = $pool->Solver();
- solver = pool.Solver()
- solver = pool.Solver()
-
-Create a new solver object.
-
- Job Job(int how, Id what)
- my $job = $pool->Job($how, $what);
- job = pool.Job(how, what)
- job = pool.Job(how, what)
-
-Create a new Job object. Kind of low level, in most cases you would use a
-Selection or Dep job constructor instead.
-
- Selection Selection()
- my $sel = $pool->Selection();
- sel = pool.Selection()
- sel = pool.Selection()
-
-Create an empty selection. Useful as a starting point for merging other
-selections.
-
- Selection Selection_all()
- my $sel = $pool->Selection_all();
- sel = pool.Selection_all()
- sel = pool.Selection_all()
-
-Create a selection containing all packages. Useful as starting point for
-intersecting other selections or for update/distupgrade jobs.
-
- Selection select(const char *name, int flags)
- my $sel = $pool->select($name, $flags);
- sel = pool.select(name, flags)
- sel = pool.select(name, flags)
-
-Create a selection by matching packages against the specified string. See the
-Selection class for a list of flags and how to create solver jobs from a
-selection.
-
- void setpooljobs(Jobs *jobs)
- $pool->setpooljobs(\@jobs);
- pool.setpooljobs(jobs)
- pool.setpooljobs(jobs)
-
- Job *getpooljobs()
- @jobs = $pool->getpooljobs();
- jobs = pool.getpooljobs()
- jobs = pool.getpooljobs()
-
-Get/Set fixed jobs stored in the pool. Those jobs are automatically appended to
-all solver jobs, they are meant for fixed configurations like which packages
-can be multiversion installed, which packages were userinstalled or must not be
-erased.
-
- void set_loadcallback(Callable *callback)
- $pool->setloadcallback(\&callbackfunction);
- pool.setloadcallback(callbackfunction)
- pool.setloadcallback { |repodata| ... }
-
-Set the callback function called when repository metadata needs to be loaded on
-demand. To make use of this feature, you need to create repodata stubs that
-tell the library which data is available but not loaded. If later on the data
-needs to be accessed, the callback function is called with a repodata argument.
-You can then load the data (maybe fetching it first from a remote server).
-The callback should return true if the data has been made available.
-
- /* bindings only */
- $pool->appdata_disown()
- pool.appdata_disown()
- pool.appdata_disown()
-
-Decrement the reference count of the appdata object. This can be used to break
-circular references (e.g. if the pool's appdata value points to some meta data
-structure that contains a pool handle). If used incorrectly, this method can
-lead to application crashes, so beware. (This method is a no-op for ruby and tcl.)
-
-=== DATA RETRIEVAL METHODS ===
-
-In the following functions, the _keyname_ argument describes what to retrieve.
-For the standard cases you can use the available Id constants. For example,
-
- $solv::SOLVABLE_SUMMARY
- solv.SOLVABLE_SUMMARY
- Solv::SOLVABLE_SUMMARY
-
-selects the ``Summary'' entry of a solvable. The _solvid_ argument selects the
-desired solvable by Id.
-
- const char *lookup_str(Id solvid, Id keyname)
- my $string = $pool->lookup_str($solvid, $keyname);
- string = pool.lookup_str(solvid, keyname)
- string = pool.lookup_str(solvid, keyname)
-
- Id lookup_id(Id solvid, Id keyname)
- my $id = $pool->lookup_id($solvid, $keyname);
- id = pool.lookup_id(solvid, keyname)
- id = pool.lookup_id(solvid, keyname)
-
- unsigned long long lookup_num(Id solvid, Id keyname, unsigned long long notfound = 0)
- my $num = $pool->lookup_num($solvid, $keyname);
- num = pool.lookup_num(solvid, keyname)
- num = pool.lookup_num(solvid, keyname)
-
- bool lookup_void(Id solvid, Id keyname)
- my $bool = $pool->lookup_void($solvid, $keyname);
- bool = pool.lookup_void(solvid, keyname)
- bool = pool.lookup_void(solvid, keyname)
-
- Id *lookup_idarray(Id solvid, Id keyname)
- my @ids = $pool->lookup_idarray($solvid, $keyname);
- ids = pool.lookup_idarray(solvid, keyname)
- ids = pool.lookup_idarray(solvid, keyname)
-
- Chksum lookup_checksum(Id solvid, Id keyname)
- my $chksum = $pool->lookup_checksum($solvid, $keyname);
- chksum = pool.lookup_checksum(solvid, keyname)
- chksum = pool.lookup_checksum(solvid, keyname)
-
-Lookup functions. Return the data element stored in the specified solvable.
-You should probably use the methods of the Solvable class instead.
-
- Dataiterator Dataiterator(Id keyname, const char *match = 0, int flags = 0)
- my $di = $pool->Dataiterator($keyname, $match, $flags);
- di = pool.Dataiterator(keyname, match, flags)
- di = pool.Dataiterator(keyname, match, flags)
-
- Dataiterator Dataiterator_solvid(Id solvid, Id keyname, const char *match = 0, int flags = 0)
- my $di = $pool->Dataiterator($solvid, $keyname, $match, $flags);
- di = pool.Dataiterator(solvid, keyname, match, flags)
- di = pool.Dataiterator(solvid, keyname, match, flags)
-
- for my $d (@$di)
- for d in di:
- for d in di
-
-Iterate over the matching data elements. See the Dataiterator class for more
-information. The Dataiterator method iterates over all solvables in the pool,
-whereas the Dataiterator_solvid only iterates over the specified solvable.
-
-=== ID METHODS ===
-
-The following methods deal with Ids, i.e. integers representing objects in the
-pool. They are considered ``low level'', in most cases you would not use them
-but instead the object orientated methods.
-
- Repo id2repo(Id id)
- $repo = $pool->id2repo($id);
- repo = pool.id2repo(id)
- repo = pool.id2repo(id)
-
-Lookup an existing Repository by id. You can also do this by using the *repos*
-attribute.
-
- Solvable id2solvable(Id id)
- $solvable = $pool->id2solvable($id);
- solvable = pool.id2solvable(id)
- solvable = pool.id2solvable(id)
-
-Lookup an existing Repository by id. You can also do this by using the
-*solvables* attribute.
-
- const char *solvid2str(Id id)
- my $str = $pool->solvid2str($id);
- str = pool.solvid2str(id)
- str = pool.solvid2str(id)
-
-Return a string describing the Solvable with the specified id. The string
-consists of the name, version, and architecture of the Solvable.
-
- Id str2id(const char *str, bool create = 1)
- my $id = pool->str2id($string);
- id = pool.str2id(string)
- id = pool.str2id(string)
-
- const char *id2str(Id id)
- $string = pool->id2str($id);
- string = pool.id2str(id)
- string = pool.id2str(id)
-
-Convert a string into an Id and back. If the string is currently not in the
-pool and _create_ is false, zero is returned.
-
- Id rel2id(Id name, Id evr, int flags, bool create = 1)
- my $id = pool->rel2id($nameid, $evrid, $flags);
- id = pool.rel2id(nameid, evrid, flags)
- id = pool.rel2id(nameid, evrid, flags)
-
-Create a ``relational'' dependency. Such dependencies consist of a name part,
-the _flags_ describing the relation, and a version part. The flags are:
-
- $solv::REL_EQ | $solv::REL_GT | $solv::REL_LT
- solv.REL_EQ | solv.REL_GT | solv.REL_LT
- Solv::REL_EQ | Solv::REL_GT | Solv::REL_LT
-
-Thus, if you want a ``\<='' relation, you would use *REL_LT | REL_EQ*.
-
- Id id2langid(Id id, const char *lang, bool create = 1)
- my $id = $pool->id2langid($id, $language);
- id = pool.id2langid(id, language)
- id = pool.id2langid(id, language)
-
-Create a language specific Id from some other id. This function simply converts
-the id into a string, appends a dot and the specified language to the string
-and converts the result back into an Id.
-
- const char *dep2str(Id id)
- $string = pool->dep2str($id);
- string = pool.dep2str(id)
- string = pool.dep2str(id)
-
-Convert a dependency id into a string. If the id is just a string, this
-function has the same effect as id2str(). For relational dependencies, the
-result is the correct ``name relation evr'' string.
-
-
-The Dependency Class
---------------------
-The dependency class is an object orientated way to work with strings and
-dependencies. Internally, dependencies are represented as Ids, i.e. simple
-numbers. Dependency objects can be constructed by using the Pool's Dep()
-method.
-
-=== ATTRIBUTES ===
-
- Pool *pool; /* read only */
- $dep->{pool}
- dep.pool
- dep.pool
-
-Back reference to the pool this dependency belongs to.
-
- Id id; /* read only */
- $dep->{id}
- dep.id
- dep.id
-
-The id of this dependency.
-
-== Methods ==
-
- Dep Rel(int flags, DepId evrid, bool create = 1)
- my $reldep = $dep->Rel($flags, $evrdep);
- reldep = dep.Rel(flags, evrdep)
- reldep = dep.Rel(flags, evrdep)
-
-Create a relational dependency from to string dependencies and a flags
-argument. See the pool's rel2id method for a description of the flags.
-
- Selection Selection_name(int setflags = 0)
- my $sel = $dep->Selection_name();
- sel = dep.Selection_name()
- sel = dep.Selection_name()
-
-Create a Selection from a dependency. The selection consists of all packages
-that have a name equal to the dependency. If the dependency is of a relational
-type, the packages version must also fulfill the dependency.
-
- Selection Selection_provides(int setflags = 0)
- my $sel = $dep->Selection_provides();
- sel = dep.Selection_provides()
- sel = dep.Selection_provides()
-
-Create a Selection from a dependency. The selection consists of all packages
-that have at least one provides matching the dependency.
-
- const char *str()
- my $str = $dep->str();
- str = $dep.str()
- str = $dep.str()
-
-Return a string describing the dependency.
-
- <stringification>
- my $str = $dep->str;
- str = str(dep)
- str = dep.to_s
-
-Same as calling the str() method.
-
- <equality>
- if ($dep1 == $dep2)
- if dep1 == dep2:
- if dep1 == dep2
-
-The dependencies are equal if they are part of the same pool and have the same
-ids.
-
-
-The Repository Class
---------------------
-A Repository describes a group of packages, normally coming from the same
-source. Repositories are created by the Pool's add_repo() method.
-
-=== ATTRIBUTES ===
-
- Pool *pool; /* read only */
- $repo->{pool}
- repo.pool
- repo.pool
-
-Back reference to the pool this dependency belongs to.
-
- Id id; /* read only */
- $repo->{id}
- repo.id
- repo.id
-
-The id of the repository.
-
- const char *name; /* read/write */
- $repo->{name}
- repo.name
- repo.name
-
-The repositories name. To libsolv, the name is just a string with no specific
-meaning.
-
- int priority; /* read/write */
- $repo->{priority}
- repo.priority
- repo.priority
-
-The priority of the repository. A higher number means that packages of this
-repository will be chosen over other repositories, even if they have a greater
-package version.
-
- int subpriority; /* read/write */
- $repo->{subpriority}
- repo.subpriority
- repo.subpriority
-
-The sub-priority of the repository. This value is compared when the priorities
-of two repositories are the same. It is useful to make the library prefer
-on-disk repositories to remote ones.
-
- int nsolvables; /* read only */
- $repo->{nsolvables}
- repo.nsolvables
- repo.nsolvables
-
-The number of solvables in this repository.
-
- void *appdata; /* read/write */
- $repo->{appdata}
- repo.appdata
- repo.appdata
-
-Application specific data that may be used in any way by the code using the
-repository.
-
- Datapos *meta; /* read only */
- $repo->{meta}
- repo.meta
- repo.meta
-
-Return a Datapos object of the repodata's metadata. You can use the lookup
-methods of the Datapos class to lookup metadata attributes, like the repository
-timestamp.
-
-=== CONSTANTS ===
-
-*REPO_REUSE_REPODATA*::
-Reuse the last repository data area (``repodata'') instead of creating a
-new one.
-
-*REPO_NO_INTERNALIZE*::
-Do not internalize the added repository data. This is useful if
-you plan to add more data because internalization is a costly
-operation.
-
-*REPO_LOCALPOOL*::
-Use the repodata's pool for Id storage instead of the global pool. Useful
-if you don't want to pollute the global pool with many unneeded ids, like
-when storing the filelist.
-
-*REPO_USE_LOADING*::
-Use the repodata that is currently being loaded instead of creating a new
-one. This only makes sense if used in a load callback.
-
-*REPO_EXTEND_SOLVABLES*::
-Do not create new solvables for the new data, but match existing solvables
-and add the data to them. Repository metadata is often split into multiple
-parts, with one primary file describing all packages and other parts
-holding information that is normally not needed, like the changelog.
-
-*REPO_USE_ROOTDIR*::
-Prepend the pool's rootdir to the path when doing file operations.
-
-*REPO_NO_LOCATION*::
-Do not add a location element to the solvables. Useful if the solvables
-are not in the final position, so you can add the correct location later
-in your code.
-
-*SOLV_ADD_NO_STUBS*::
-Do not create stubs for repository parts that can be downloaded on demand.
-
-*SUSETAGS_RECORD_SHARES*::
-This is specific to the add_susetags() method. Susetags allows one to refer to
-already read packages to save disk space. If this data sharing needs to
-work over multiple calls to add_susetags, you need to specify this flag so
-that the share information is made available to subsequent calls.
-
-=== METHODS ===
-
- void free(bool reuseids = 0)
- $repo->free();
- repo.free()
- repo.free()
-
-Free the repository and all solvables it contains. If _reuseids_ is set to
-true, the solvable ids and the repository id may be reused by the library when
-added new solvables. Thus you should leave it false if you are not sure that
-somebody holds a reference.
-
- void empty(bool reuseids = 0)
- $repo->empty();
- repo.empty()
- repo.empty()
-
-Free all the solvables in a repository. The repository will be empty after this
-call. See the free() method for the meaning of _reuseids_.
-
- bool isempty()
- $repo->isempty()
- repo.empty()
- repo.empty?
-
-Return true if there are no solvables in this repository.
-
- void internalize()
- $repo->internalize();
- repo.internalize()
- repo.internalize()
-
-Internalize added data. Data must be internalized before it is available to the
-lookup and data iterator functions.
-
- bool write(FILE *fp)
- $repo->write($fp)
- repo.write(fp)
- repo.write(fp)
-
-Write a repo as a ``solv'' file. These files can be read very fast and thus are
-a good way to cache repository data. Returns false if there was some error
-writing the file.
-
- Solvableiterator solvables_iter()
- for my $solvable (@{$repo->solvables_iter()})
- for solvable in repo.solvables_iter():
- for solvable in repo.solvables_iter()
-
-Iterate over all solvables in a repository.
-
- Repodata add_repodata(int flags = 0)
- my $repodata = $repo->add_repodata();
- repodata = repo.add_repodata()
- repodata = repo.add_repodata()
-
-Add a new repodata area to the repository. This is normally automatically
-done by the repo_add methods, so you need this method only in very
-rare circumstances.
-
- void create_stubs()
- $repo->create_stubs();
- repo.create_stubs()
- repo.create_stubs()
-
-Calls the create_stubs() repodata method for the last repodata of the
-repository.
-
- bool iscontiguous()
- $repo->iscontiguous()
- repo.iscontiguous()
- repo.iscontiguous?
-
-Return true if the solvables of this repository are all in a single block with
-no holes, i.e. they have consecutive ids.
-
- Repodata first_repodata()
- my $repodata = $repo->first_repodata();
- repodata = repo.first_repodata()
- repodata = repo.first_repodata()
-
-Checks if all repodatas but the first repodata are extensions, and return the
-first repodata if this is the case. Useful if you want to do a store/retrieve
-sequence on the repository to reduce the memory using and enable paging, as
-this does not work if the repository contains multiple non-extension repodata
-areas.
-
- Selection Selection(int setflags = 0)
- my $sel = $repo->Selection();
- sel = repo.Selection()
- sel = repo.Selection()
-
-Create a Selection consisting of all packages in the repository.
-
- Dataiterator Dataiterator(Id key, const char *match = 0, int flags = 0)
- my $di = $repo->Dataiterator($keyname, $match, $flags);
- di = repo.Dataiterator(keyname, match, flags)
- di = repo.Dataiterator(keyname, match, flags)
-
- Dataiterator Dataiterator_meta(Id key, const char *match = 0, int flags = 0)
- my $di = $repo->Dataiterator_meta($keyname, $match, $flags);
- di = repo.Dataiterator_meta(keyname, match, flags)
- di = repo.Dataiterator_meta(keyname, match, flags)
-
- for my $d (@$di)
- for d in di:
- for d in di
-
-Iterate over the matching data elements in this repository. See the
-Dataiterator class for more information. The Dataiterator() method
-iterates over all solvables in a repository, whereas the Dataiterator_meta
-method only iterates over the repository's meta data.
-
- <stringification>
- my $str = $repo->str;
- str = str(repo)
- str = repo.to_s
-
-Return the name of the repository, or "Repo#<id>" if no name is set.
-
- <equality>
- if ($repo1 == $repo2)
- if repo1 == repo2:
- if repo1 == repo2
-
-Two repositories are equal if they belong to the same pool and have the same id.
-
-=== DATA ADD METHODS ===
-
- Solvable add_solvable()
- $repo->add_solvable();
- repo.add_solvable()
- repo.add_solvable()
-
-Add a single empty solvable to the repository. Returns a Solvable object, see
-the Solvable class for more information.
-
- bool add_solv(const char *name, int flags = 0)
- $repo->add_solv($name);
- repo.add_solv(name)
- repo.add_solv(name)
-
- bool add_solv(FILE *fp, int flags = 0)
- $repo->add_solv($fp);
- repo.add_solv(fp)
- repo.add_solv(fp)
-
-Read a ``solv'' file and add its contents to the repository. These files can be
-written with the write() method and are normally used as fast cache for
-repository metadata.
-
- bool add_rpmdb(int flags = 0)
- $repo->add_rpmdb();
- repo.add_rpmdb()
- repo.add_rpmdb()
-
- bool add_rpmdb_reffp(FILE *reffp, int flags = 0)
- $repo->add_rpmdb_reffp($reffp);
- repo.add_rpmdb_reffp(reffp)
- repo.add_rpmdb_reffp(reffp)
-
-Add the contents of the rpm database to the repository. If a solv file
-containing an old version of the database is available, it can be passed as
-reffp to speed up reading.
-
- Solvable add_rpm(const char *filename, int flags = 0)
- my $solvable = $repo->add_rpm($filename);
- solvable = repo.add_rpm(filename)
- solvable = repo.add_rpm(filename)
-
-Add the metadata of a single rpm package to the repository.
-
- bool add_rpmdb_pubkeys(int flags = 0)
- $repo->add_rpmdb_pubkeys();
- repo.add_rpmdb_pubkeys()
- repo.add_rpmdb_pubkeys()
-
-Add all pubkeys contained in the rpm database to the repository. Note that
-newer rpm versions also allow to store the pubkeys in some directory instead
-of the rpm database.
-
- Solvable add_pubkey(const char *keyfile, int flags = 0)
- my $solvable = $repo->add_pubkey($keyfile);
- solvable = repo.add_pubkey(keyfile)
- solvable = repo.add_pubkey(keyfile)
-
-Add a pubkey from a file to the repository.
-
- bool add_rpmmd(FILE *fp, const char *language, int flags = 0)
- $repo->add_rpmmd($fp, undef);
- repo.add_rpmmd(fp, None)
- repo.add_rpmmd(fp, nil)
-
-Add metadata stored in the "rpm-md" format (i.e. from files in the ``repodata''
-directory) to a repository. Supported files are "primary", "filelists",
-"other", "suseinfo". Do not forget to specify the *REPO_EXTEND_SOLVABLES* for
-extension files like "filelists" and "other". Use the _language_ parameter if
-you have language extension files, otherwise simply use a *undef*/*None*/*nil*
-parameter.
-
- bool add_repomdxml(FILE *fp, int flags = 0)
- $repo->add_repomdxml($fp);
- repo.add_repomdxml(fp)
- repo.add_repomdxml(fp)
-
-Add the repomd.xml meta description from the "rpm-md" format to the repository.
-This file contains information about the repository like keywords, and also a
-list of all database files with checksums. The data is added to the "meta"
-section of the repository, i.e. no package gets created.
-
- bool add_updateinfoxml(FILE *fp, int flags = 0)
- $repo->add_updateinfoxml($fp);
- repo.add_updateinfoxml(fp)
- repo.add_updateinfoxml(fp)
-
-Add the updateinfo.xml file containing available maintenance updates to the
-repository. All updates are created as special packages that have a "patch:"
-prefix in their name.
-
- bool add_deltainfoxml(FILE *fp, int flags = 0)
- $repo->add_deltainfoxml($fp);
- repo.add_deltainfoxml(fp)
- repo.add_deltainfoxml(fp)
-
-Add the deltainfo.xml file (also called prestodelta.xml) containing available
-delta-rpms to the repository. The data is added to the "meta" section, i.e. no
-package gets created.
-
- bool add_debdb(int flags = 0)
- $repo->add_debdb();
- repo.add_debdb()
- repo.add_debdb()
-
-Add the contents of the debian installed package database to the repository.
-
- bool add_debpackages(FILE *fp, int flags = 0)
- $repo->add_debpackages($fp);
- repo.add_debpackages($fp)
- repo.add_debpackages($fp)
-
-Add the contents of the debian repository metadata (the "packages" file)
-to the repository.
-
- Solvable add_deb(const char *filename, int flags = 0)
- my $solvable = $repo->add_deb($filename);
- solvable = repo.add_deb(filename)
- solvable = repo.add_deb(filename)
-
-Add the metadata of a single deb package to the repository.
-
- bool add_mdk(FILE *fp, int flags = 0)
- $repo->add_mdk($fp);
- repo.add_mdk(fp)
- repo.add_mdk(fp)
-
-Add the contents of the mageia/mandriva repository metadata (the
-"synthesis.hdlist" file) to the repository.
-
- bool add_mdk_info(FILE *fp, int flags = 0)
- $repo->add_mdk($fp);
- repo.add_mdk(fp)
- repo.add_mdk(fp)
-
-Extend the packages from the synthesis file with the info.xml and files.xml
-data. Do not forget to specify *REPO_EXTEND_SOLVABLES*.
-
- bool add_arch_repo(FILE *fp, int flags = 0)
- $repo->add_arch_repo($fp);
- repo.add_arch_repo(fp)
- repo.add_arch_repo(fp)
-
-Add the contents of the archlinux repository metadata (the ".db.tar" file) to
-the repository.
-
- bool add_arch_local(const char *dir, int flags = 0)
- $repo->add_arch_local($dir);
- repo.add_arch_local(dir)
- repo.add_arch_local(dir)
-
-Add the contents of the archlinux installed package database to the repository.
-The _dir_ parameter is usually set to "/var/lib/pacman/local".
-
- bool add_content(FILE *fp, int flags = 0)
- $repo->add_content($fp);
- repo.add_content(fp)
- repo.add_content(fp)
-
-Add the ``content'' meta description from the susetags format to the repository.
-This file contains information about the repository like keywords, and also
-a list of all database files with checksums. The data is added to the "meta"
-section of the repository, i.e. no package gets created.
-
- bool add_susetags(FILE *fp, Id defvendor, const char *language, int flags = 0)
- $repo->add_susetags($fp, $defvendor, $language);
- repo.add_susetags(fp, defvendor, language)
- repo.add_susetags(fp, defvendor, language)
-
-Add repository metadata in the susetags format to the repository. Like with
-add_rpmmd, you can specify a language if you have language extension files. The
-_defvendor_ parameter provides a default vendor for packages with missing
-vendors, it is usually provided in the content file.
-
- bool add_products(const char *dir, int flags = 0)
- $repo->add_products($dir);
- repo.add_products(dir)
- repo.add_products(dir)
-
-Add the installed SUSE products database to the repository. The _dir_ parameter
-is usually "/etc/products.d".
-
-
-The Solvable Class
-------------------
-A solvable describes all the information of one package. Each solvable
-belongs to one repository, it can be added and filled manually but in
-most cases solvables will get created by the repo_add methods.
-
-=== ATTRIBUTES ===
-
- Repo *repo; /* read only */
- $solvable->{repo}
- solvable.repo
- solvable.repo
-
-The repository this solvable belongs to.
-
- Pool *pool; /* read only */
- $solvable->{pool}
- solvable.pool
- solvable.pool
-
-The pool this solvable belongs to, same as the pool of the repo.
-
- Id id; /* read only */
- $solvable->{id}
- solvable.id
- solvable.id
-
-The specific id of the solvable.
-
- char *name; /* read/write */
- $solvable->{name}
- solvable.name
- solvable.name
-
- char *evr; /* read/write */
- $solvable->{evr}
- solvable.evr
- solvable.evr
-
- char *arch; /* read/write */
- $solvable->{arch}
- solvable.arch
- solvable.arch
-
- char *vendor; /* read/write */
- $solvable->{vendor}
- solvable.vendor
- solvable.vendor
-
-Easy access to often used attributes of solvables. They are
-internally stored as Ids.
-
- Id nameid; /* read/write */
- $solvable->{nameid}
- solvable.nameid
- solvable.nameid
-
- Id evrid; /* read/write */
- $solvable->{evrid}
- solvable.evrid
- solvable.evrid
-
- Id archid; /* read/write */
- $solvable->{archid}
- solvable.archid
- solvable.archid
-
- Id vendorid; /* read/write */
- $solvable->{vendorid}
- solvable.vendorid
- solvable.vendorid
-
-Raw interface to the ids. Useful if you want to search for
-a specific id and want to avoid the string compare overhead.
-
-=== METHODS ===
-
- const char *lookup_str(Id keyname)
- my $string = $solvable->lookup_str($keyname);
- string = solvable.lookup_str(keyname)
- string = solvable.lookup_str(keyname)
-
- Id lookup_id(Id keyname)
- my $id = $solvable->lookup_id($keyname);
- id = solvable.lookup_id(solvid)
- id = solvable.lookup_id(solvid)
-
- unsigned long long lookup_num(Id solvid, Id keyname, unsigned long long notfound = 0)
- my $num = $solvable->lookup_num($keyname);
- num = solvable.lookup_num(keyname)
- num = solvable.lookup_num(keyname)
-
- bool lookup_void(Id keyname)
- my $bool = $solvable->lookup_void($keyname);
- bool = solvable.lookup_void(keyname)
- bool = solvable.lookup_void(keyname)
-
- Chksum lookup_checksum(Id keyname)
- my $chksum = $solvable->lookup_checksum($keyname);
- chksum = solvable.lookup_checksum(keyname)
- chksum = solvable.lookup_checksum(keyname)
-
- Id *lookup_idarray(Id keyname, Id marker = -1)
- my @ids = $solvable->lookup_idarray($keyname);
- ids = solvable.lookup_idarray(keyname)
- ids = solvable.lookup_idarray(keyname)
-
- Dep *lookup_deparray(Id keyname, Id marker = -1)
- my @deps = $solvable->lookup_deparray($keyname);
- deps = solvable.lookup_deparray(keyname)
- deps = solvable.lookup_deparray(keyname)
-
-Generic lookup methods. Retrieve data stored for the specific keyname.
-The lookup_idarray() method will return an array of Ids, use
-lookup_deparray if you want an array of Dependency objects instead.
-Some Id arrays contain two parts of data divided by a specific marker,
-for example the provides array uses the SOLVABLE_FILEMARKER id to
-store both the ids provided by the package and the ids added by
-the addfileprovides method. The default, -1, translates to the
-correct marker for the keyname and returns the first part of the
-array, use 1 to select the second part or 0 to retrieve all ids
-including the marker.
-
- const char *lookup_location(unsigned int *OUTPUT);
- my ($location, $medianr) = $solvable->lookup_location();
- location, medianr = solvable.lookup_location()
- location, medianr = solvable.lookup_location()
-
-Return a tuple containing the on-media location and an optional
-media number for multi-part repositories (e.g. repositories
-spawning multiple DVDs).
-
- Dataiterator Dataiterator(Id keyname, const char *match = 0, int flags = 0)
- my $di = $solvable->Dataiterator($keyname, $match, $flags);
- di = solvable.Dataiterator(keyname, match, flags)
- di = solvable.Dataiterator(keyname, match, flags)
-
- for my $d (@$di)
- for d in di:
- for d in di
-
-Iterate over the matching data elements. See the Dataiterator class for more
-information.
-
- void add_deparray(Id keyname, DepId dep, Id marker = -1);
- $solvable->add_deparray($keyname, $dep);
- solvable.add_deparray(keyname, dep)
- solvable.add_deparray(keyname, dep)
-
-Add a new dependency to the attributes stored in keyname.
-
- void unset(Id keyname);
- $solvable->unset($keyname);
- solvable.unset(keyname)
- solvable.unset(keyname)
-
-Delete data stored for the specific keyname.
-
- bool installable();
- $solvable->installable()
- solvable.installable()
- solvable.installable?
-
-Return true if the solvable is installable on the system. Solvables
-are not installable if the system does not support their architecture.
-
- bool isinstalled();
- $solvable->isinstalled()
- solvable.isinstalled()
- solvable.isinstalled?
-
-Return true if the solvable is installed on the system.
-
- bool identical(Solvable *other)
- $solvable->identical($other)
- $solvable.identical(other)
- $solvable.identical?(other)
-
-Return true if the two solvables are identical.
-
- int evrcmp(Solvable *other)
- $solvable->evrcmp(other)
- $solvable.evrcmp(other)
- $solvable.evrcmp(other)
-
-Returns -1 if the epoch/version/release of the solvable is less than the
-one from the other solvable, 1 if it is greater, and 0 if they are equal.
-Note that "equal" does not mean that the evr is identical.
-
- Selection Selection(int setflags = 0)
- my $sel = $solvable->Selection();
- sel = solvable.Selection()
- sel = solvable.Selection()
-
-Create a Selection containing just the single solvable.
-
- const char *str()
- my $str = $solvable->str();
- str = $solvable.str()
- str = $solvable.str()
-
-Return a string describing the solvable. The string consists of the name,
-version, and architecture of the Solvable.
-
- <stringification>
- my $str = $solvable->str;
- str = str(solvable)
- str = solvable.to_s
-
-Same as calling the str() method.
-
- <equality>
- if ($solvable1 == $solvable2)
- if solvable1 == solvable2:
- if solvable1 == solvable2
-
-Two solvables are equal if they are part of the same pool and have the same
-ids.
-
-
-The Dataiterator Class
-----------------------
-Dataiterators can be used to do complex string searches or
-to iterate over arrays. They can be created via the
-constructors in the Pool, Repo, and Solvable classes. The
-Repo and Solvable constructors will limit the search to
-the repository or the specific package.
-
-=== CONSTANTS ===
-
-*SEARCH_STRING*::
-Return a match if the search string matches the value.
-
-*SEARCH_STRINGSTART*::
-Return a match if the value starts with the search string.
-
-*SEARCH_STRINGEND*::
-Return a match if the value ends with the search string.
-
-*SEARCH_SUBSTRING*::
-Return a match if the search string can be matched somewhere in the value.
-
-*SEARCH_GLOB*::
-Do a glob match of the search string against the value.
-
-*SEARCH_REGEX*::
-Do a regular expression match of the search string against the value.
-
-*SEARCH_NOCASE*::
-Ignore case when matching strings. Works for all the above match types.
-
-*SEARCH_FILES*::
-Match the complete filenames of the file list, not just the base name.
-
-*SEARCH_COMPLETE_FILELIST*::
-When matching the file list, check every file of the package not just the
-subset from the primary metadata.
-
-*SEARCH_CHECKSUMS*::
-Allow the matching of checksum entries.
-
-=== METHODS ===
-
- void prepend_keyname(Id keyname);
- $di->prepend_keyname($keyname);
- di.prepend_keyname(keyname)
- di.prepend_keyname(keyname)
-
-Do a sub-search in the array stored in keyname.
-
- void skip_solvable();
- $di->kip_solvable();
- di.skip_solvable()
- di.skip_solvable()
-
-Stop matching the current solvable and advance to the next
-one.
-
- <iteration>
- for my $d (@$di)
- for d in di:
- for d in di
-
-Iterate through the matches. If there is a match, the object
-in d will be of type Datamatch.
-
-The Datamatch Class
--------------------
-Objects of this type will be created for every value matched
-by a dataiterator.
-
-=== ATTRIBUTES ===
-
- Pool *pool; /* read only */
- $d->{pool}
- d.pool
- d.pool
-
-Back pointer to pool.
-
- Repo *repo; /* read only */
- $d->{repo}
- d.repo
- d.repo
-
-The repository containing the matched object.
-
- Solvable *solvable; /* read only */
- $d->{solvable}
- d.solvable
- d.solvable
-
-The solvable containing the value that was matched.
-
- Id solvid; /* read only */
- $d->{solvid}
- d.solvid
- d.solvid
-
-The id of the solvable that matched.
-
- Id key_id;
- $d->{key_id}
- d.key_id
- d.key_id
-
- const char *key_idstr;
- $d->{key_idstr}
- d.key_idstr
- d.key_idstr
-
-The keyname that matched, either as id or string.
-
- Id type_id;
- $d->{type_id}
- d.type_id
- d.type_id
-
- const char *type_idstr;
- $d->{type_idstr};
- d.type_idstr
- d.type_idstr
-
-The key type of the value that was matched, either as id or string.
-
- Id id;
- $d->{id}
- d.id
- d.id
-
- Id idstr;
- $d->{idstr}
- d.idstr
- d.idstr
-
-The Id of the value that was matched (only valid for id types),
-either as id or string.
-
- const char *str;
- $d->{str}
- d.str
- d.str
-
-The string value that was matched (only valid for string types).
-
- unsigned long long num;
- $d->{num}
- d.num
- d.num
-
-The numeric value that was matched (only valid for numeric types).
-
- unsigned int num2;
- $d->{num2}
- d.num2
- d.num2
-
-The secondary numeric value that was matched (only valid for types
-containing two values).
-
- unsigned int binary;
- $d->{binary}
- d.binary
- d.binary
-
-The value in binary form, useful for checksums and other data
-that cannot be represented as a string.
-
-=== METHODS ===
-
- Datapos pos();
- my $pos = $d->pos();
- pos = d.pos()
- pos = d.pos()
-
-The position object of the current match. It can be used to do
-sub-searches starting at the match (if it is of an array type).
-See the Datapos class for more information.
-
- Datapos parentpos();
- my $pos = $d->parentpos();
- pos = d.parentpos()
- pos = d.parentpos()
-
-The position object of the array containing the current match.
-It can be used to do sub-searches, see the Datapos class for more
-information.
-
- <stringification>
- my $str = $d->str;
- str = str(d)
- str = d.to_s
-
-Return the stringification of the matched value. Stringification
-depends on the search flags, for file list entries it will return
-just the base name unless SEARCH_FILES is used, for checksums
-it will return an empty string unless SEARCH_CHECKSUMS is used.
-Numeric values are currently stringified to an empty string.
-
-
-The Selection Class
--------------------
-Selections are a way to easily deal with sets of packages.
-There are multiple constructors to create them, the most useful
-is probably the select() method in the Pool class.
-
-=== CONSTANTS ===
-
-*SELECTION_NAME*::
-Create the selection by matching package names.
-
-*SELECTION_PROVIDES*::
-Create the selection by matching package provides.
-
-*SELECTION_FILELIST*::
-Create the selection by matching package files.
-
-*SELECTION_CANON*::
-Create the selection by matching the canonical representation
-of the package. This is normally a combination of the name,
-the version, and the architecture of a package.
-
-*SELECTION_DOTARCH*::
-Allow an ``.<architecture>'' suffix when matching names or
-provides.
-
-*SELECTION_REL*::
-Allow the specification of a relation when matching names
-or provides, e.g. "name >= 1.2".
-
-*SELECTION_INSTALLED_ONLY*::
-Limit the package search to installed packages.
-
-*SELECTION_SOURCE_ONLY*::
-Limit the package search to source packages only.
-
-*SELECTION_WITH_SOURCE*::
-Extend the package search to also match source packages. The default is
-only to match binary packages.
-
-*SELECTION_GLOB*::
-Allow glob matching for package names, package provides, and file names.
-
-*SELECTION_NOCASE*::
-Ignore case when matching package names, package provides, and file names.
-
-*SELECTION_FLAT*::
-Return only one selection element describing the selected packages.
-The default is to create multiple elements for all globbed packages.
-Multiple elements are useful if you want to turn the selection into
-an install job, in that case you want an install job for every
-globbed package.
-
-=== ATTRIBUTES ===
-
- Pool *pool; /* read only */
- $d->{pool}
- d.pool
- d.pool
-
-Back pointer to pool.
-
-=== METHODS ===
-
- int flags();
- my $flags = $sel->flags();
- flags = sel.flags()
- flags = sel.flags()
-
-Return the result flags of the selection. The flags are a subset
-of the ones used when creating the selection, they describe which
-method was used to get the result. For example, if you create the
-selection with ``SELECTION_NAME | SELECTION_PROVIDES'', the resulting
-flags will either be SELECTION_NAME or SELECTION_PROVIDES depending
-if there was a package that matched the name or not. If there was
-no match at all, the flags will be zero.
-
- bool isempty();
- $sel->isempty()
- sel.isempty()
- sel.isempty?
-
-Return true if the selection is empty, i.e. no package could be matched.
-
- void filter(Selection *other)
- $sel->filter($other);
- sel.filter(other)
- sel.filter(other)
-
-Intersect two selections. Packages will only stay in the selection if there
-are also included in the other selecting. Does an in-place modification.
-
- void add(Selection *other)
- $sel->add($other);
- sel.add(other)
- sel.add(other)
-
-Build the union of two selections. All packages of the other selection will
-be added to the set of packages of the selection object. Does an in-place
-modification. Note that the selection flags are no longer meaningful after the
-add operation.
-
- void add_raw(Id how, Id what)
- $sel->add_raw($how, $what);
- sel.add_raw(how, what)
- sel.add_raw(how, what)
-
-Add a raw element to the selection. Check the Job class for information about
-the how and what parameters.
-
- Job *jobs(int action)
- my @jobs = $sel->jobs($action);
- jobs = sel.jobs(action)
- jobs = sel.jobs(action)
-
-Convert a selection into an array of Job objects. The action parameter is or-ed
-to the ``how'' part of the job, it describes the type of job (e.g. install,
-erase). See the Job class for the action and action modifier constants.
-
- Solvable *solvables()
- my @solvables = $sel->solvables();
- solvables = sel.solvables()
- solvables = sel.solvables()
-
-Convert a selection into an array of Solvable objects.
-
- <stringification>
- my $str = $sel->str;
- str = str(sel)
- str = sel.to_s
-
-Return a string describing the selection.
-
-The Job Class
--------------
-Jobs are the way to specify to the dependency solver what to do.
-Most of the times jobs will get created by calling the jobs() method
-on a Selection object, but there is also a Job() constructor in the
-Pool class.
-
-=== CONSTANTS ===
-
-Selection constants:
-
-*SOLVER_SOLVABLE*::
-The ``what'' part is the id of a solvable.
-
-*SOLVER_SOLVABLE_NAME*::
-The ``what'' part is the id of a package name.
-
-*SOLVER_SOLVABLE_PROVIDES*::
-The ``what'' part is the id of a package provides.
-
-*SOLVER_SOLVABLE_ONE_OF*::
-The ``what'' part is an offset into the ``whatprovides'' data, created
-by calling the towhatprovides() pool method.
-
-*SOLVER_SOLVABLE_REPO*::
-The ``what'' part is the id of a repository.
-
-*SOLVER_SOLVABLE_ALL*::
-The ``what'' part is ignored, all packages are selected.
-
-*SOLVER_SOLVABLE_SELECTMASK*::
-A mask containing all the above selection bits.
-
-Action constants:
-
-*SOLVER_NOOP*::
-Do nothing.
-
-*SOLVER_INSTALL*::
-Install a package of the specified set of packages. It tries to install
-the best matching package (i.e. the highest version of the packages from
-the repositories with the highest priority).
-
-*SOLVER_ERASE*::
-Erase all of the packages from the specified set. If a package is not
-installed, erasing it will keep it from getting installed.
-
-*SOLVER_UPDATE*::
-Update the matching installed packages to their best version. If none
-of the specified packages are installed, try to update the installed
-packages to the specified versions. See the section about targeted
-updates about more information.
-
-*SOLVER_WEAKENDEPS*::
-Allow to break the dependencies of the matching packages. Handle with care.
-
-*SOLVER_MULTIVERSION*::
-Mark the matched packages for multiversion install. If they get to be
-installed because of some other job, the installation will keep the old
-version of the package installed (for rpm this is done by using ``-i''
-instead of ``-U'').
-
-*SOLVER_LOCK*::
-Do not change the state of the matched packages, i.e. when they are
-installed they stay installed, if not they are not selected for
-installation.
-
-*SOLVER_DISTUPGRADE*::
-Update the matching installed packages to the best version included in one
-of the repositories. After this operation, all come from one of the available
-repositories except orphaned packages. Orphaned packages are packages that
-have no relation to the packages in the repositories, i.e. no package in the
-repositories have the same name or obsolete the orphaned package.
-This action brings the installed packages in sync with the ones in the
-repository. By default it also turns of arch/vendor/version locking for the
-affected packages to simulate a fresh installation. This means that distupgrade can
-actually downgrade packages if only lower versions of a package are available
-in the repositories. You can tweak this behavior with the SOLVER_FLAG_DUP_
-solver flags.
-
-*SOLVER_DROP_ORPHANED*::
-Erase all the matching installed packages if they are orphaned. This only makes
-sense if there is a ``distupgrade all packages'' job. The default is to erase
-orphaned packages only if they block the installation of other packages.
-
-*SOLVER_VERIFY*::
-Fix dependency problems of matching installed packages. The default is to ignore
-dependency problems for installed packages.
-
-*SOLVER_USERINSTALLED*::
-The matching installed packages are considered to be installed by a user,
-thus not installed to fulfill some dependency. This is needed input for
-the calculation of unneeded packages for jobs that have the
-SOLVER_CLEANDEPS flag set.
-
-*SOLVER_ALLOWUNINSTALL*::
-Allow the solver to deinstall the matching installed packages if they get
-into the way of resolving a dependency. This is like the
-SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages.
-
-*SOLVER_JOBMASK*::
-A mask containing all the above action bits.
-
-Action modifier constants:
-
-*SOLVER_WEAK*::
-Makes the job a weak job. The solver tries to fulfill weak jobs, but does
-not report a problem if it is not possible to do so.
-
-*SOLVER_ESSENTIAL*::
-Makes the job an essential job. If there is a problem with the job, the
-solver will not propose to remove the job as one solution (unless all
-other solutions are also to remove essential jobs).
-
-*SOLVER_CLEANDEPS*::
-The solver will try to also erase all packages dragged in through
-dependencies when erasing the package. This needs SOLVER_USERINSTALLED
-jobs to maximize user satisfaction.
-
-*SOLVER_FORCEBEST*::
-Insist on the best package for install, update, and distupgrade jobs. If
-this flag is not used, the solver will use the second-best package if the
-best package cannot be installed for some reason. When this flag is used,
-the solver will generate a problem instead.
-
-*SOLVER_TARGETED*::
-Forces targeted operation update and distupgrade jobs. See the section
-about targeted updates about more information.
-
-Set constants.
-
-*SOLVER_SETEV*::
-The job specified the exact epoch and version of the package set.
-
-*SOLVER_SETEVR*::
-The job specified the exact epoch, version, and release of the package set.
-
-*SOLVER_SETARCH*::
-The job specified the exact architecture of the packages from the set.
-
-*SOLVER_SETVENDOR*::
-The job specified the exact vendor of the packages from the set.
-
-*SOLVER_SETREPO*::
-The job specified the exact repository of the packages from the set.
-
-*SOLVER_SETNAME*::
-The job specified the exact name of the packages from the set.
-
-*SOLVER_NOAUTOSET*::
-Turn of automatic set flag generation for SOLVER_SOLVABLE jobs.
-
-*SOLVER_SETMASK*::
-A mask containing all the above set bits.
-
-See the section about set bits for more information.
-
-=== ATTRIBUTES ===
-
- Pool *pool; /* read only */
- $job->{pool}
- d.pool
- d.pool
-
-Back pointer to pool.
-
- Id how; /* read/write */
- $job->{how}
- d.how
- d.how
-
-Union of the selection, action, action modifier, and set flags.
-The selection part describes the semantics of the ``what'' Id.
-
- Id what; /* read/write */
- $job->{what}
- d.what
- d.what
-
-Id describing the set of packages, the meaning depends on the
-selection part of the ``how'' attribute.
-
-=== METHODS ===
-
- Solvable *solvables()
- my @solvables = $job->solvables();
- solvables = job.solvables()
- solvables = job.solvables()
-
-Return the set of solvables of the job as an array of Solvable
-objects.
-
- bool isemptyupdate();
- $job->isemptyupdate()
- job.isemptyupdate()
- job.isemptyupdate?
-
-Convenience function to find out if the job describes an update
-job with no matching packages, i.e. a job that does nothing.
-Some package managers like ``zypper'' like to turn those jobs
-into install jobs, i.e. an update of a not-installed package
-will result into the installation of the package.
-
- <stringification>
- my $str = $job->str;
- str = str(job)
- str = job.to_s
-
-Return a string describing the job.
-
- <equality>
- if ($job1 == $job2)
- if job1 == job2:
- if job1 == job2
-
-Two jobs are equal if they belong to the same pool and both the
-``how'' and the ``what'' attributes are the same.
-
-=== TARGETED UPDATES ===
-Libsolv has two modes for upgrades and distupgrade: targeted and
-untargeted. Untargeted mode means that the installed packages from
-the specified set will be updated to the best version. Targeted means
-that packages that can be updated to a package in the specified set
-will be updated to the best package of the set.
-
-Here's an example to explain the subtle difference. Suppose that
-you have package A installed in version "1.1", "A-1.2" is available
-in one of the repositories and there is also package "B" that
-obsoletes package A.
-
-An untargeted update of "A" will update the installed "A-1.1" to
-package "B", because that is the newest version (B obsoletes A and
-is thus newer).
-
-A targeted update of "A" will update "A-1.1" to "A-1.2", as the
-set of packages contains both "A-1.1" and "A-1.2", and "A-1.2" is
-the newer one.
-
-An untargeted update of "B" will do nothing, as "B" is not installed.
-
-An targeted update of "B" will update "A-1.1" to "B".
-
-Note that the default is to do "auto-targeting", thus if the specified
-set of packages does not include an installed package, the solver
-will assume targeted operation even if SOLVER_TARGETED is not used.
-
-This mostly matches the intent of the user, with one exception: In
-the example above, an update of "A-1.2" will update "A-1.1" to
-"A-1.2" (targeted mode), but a second update of "A-1.2" will suddenly
-update to "B", as untargeted mode is chosen because "A-1.2" is now
-installed.
-
-If you want to have full control over when targeting mode is chosen,
-turn off auto-targeting with the SOLVER_FLAG_NO_AUTOTARGET solver option.
-In that case, all updates are considered to be untargeted unless they
-include the SOLVER_TARGETED flag.
-
-=== SET BITS ===
-Set bits specify which parts of the specified packages where specified
-by the user. It is used by the solver when checking if an operation is
-allowed or not. For example, the solver will normally not allow the
-downgrade of an installed package. But it will not report a problem if
-the SOLVER_SETEVR flag is used, as it then assumes that the user specified
-the exact version and thus knows what he is doing.
-
-So if a package "screen-1-1" is installed for the x86_64 architecture and
-version "2-1" is only available for the i586 architecture, installing
-package "screen-2.1" will ask the user for confirmation because of the
-different architecture. When using the Selection class to create jobs
-the set bits are automatically added, e.g. selecting ``screen.i586'' will
-automatically add SOLVER_SETARCH, and thus no problem will be reported.
-
-The Solver Class
-----------------
-Dependency solving is what this library is about. A solver object is needed
-for solving to store the result of the solver run. The solver object can be
-used multiple times for different jobs, reusing it allows the solver to
-re-use the dependency rules it already computed.
-
-=== CONSTANTS ===
-
-Flags to modify some of the solver's behavior:
-
-*SOLVER_FLAG_ALLOW_DOWNGRADE*::
-Allow the solver to downgrade packages without asking for confirmation
-(i.e. reporting a problem).
-
-*SOLVER_FLAG_ALLOW_ARCHCHANGE*::
-Allow the solver to change the architecture of an installed package
-without asking for confirmation. Note that changes to/from noarch
-are always considered to be allowed.
-
-*SOLVER_FLAG_ALLOW_VENDORCHANGE*::
-Allow the solver to change the vendor of an installed package
-without asking for confirmation. Each vendor is part of one or more
-vendor equivalence classes, normally installed packages may only
-change their vendor if the new vendor shares at least one equivalence
-class.
-
-*SOLVER_FLAG_ALLOW_NAMECHANGE*::
-Allow the solver to change the name of an installed package, i.e.
-install a package with a different name that obsoletes the installed
-package. This option is on by default.
-
-*SOLVER_FLAG_ALLOW_UNINSTALL*::
-Allow the solver to erase installed packages to fulfill the jobs.
-This flag also includes the above flags. You may want to set this
-flag if you only have SOLVER_ERASE jobs, as in that case it's
-better for the user to check the transaction overview instead of
-approving every single package that needs to be erased.
-
-*SOLVER_FLAG_DUP_ALLOW_DOWNGRADE*::
-Like SOLVER_FLAG_ALLOW_DOWNGRADE, but used in distupgrade mode.
-
-*SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE*::
-Like SOLVER_FLAG_ALLOW_ARCHCHANGE, but used in distupgrade mode.
-
-*SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE*::
-Like SOLVER_FLAG_ALLOW_VENDORCHANGE, but used in distupgrade mode.
-
-*SOLVER_FLAG_DUP_ALLOW_NAMECHANGE*::
-Like SOLVER_FLAG_ALLOW_NAMECHANGE, but used in distupgrade mode.
-
-*SOLVER_FLAG_NO_UPDATEPROVIDE*::
-If multiple packages obsolete an installed package, the solver checks
-the provides of every such package and ignores all packages that
-do not provide the installed package name. Thus, you can have an
-official update candidate that provides the old name, and other
-packages that also obsolete the package but are not considered for
-updating. If you cannot use this feature, you can turn it off
-by setting this flag.
-
-*SOLVER_FLAG_SPLITPROVIDES*::
-Make the solver aware of special provides of the form
-``<packagename>:<path>'' used in SUSE systems to support package
-splits.
-
-*SOLVER_FLAG_IGNORE_RECOMMENDED*::
-Do not process optional (aka weak) dependencies.
-
-*SOLVER_FLAG_ADD_ALREADY_RECOMMENDED*::
-Install recommended or supplemented packages even if they have no
-connection to the current transaction. You can use this feature
-to implement a simple way for the user to install new recommended
-packages that were not available in the past.
-
-*SOLVER_FLAG_NO_INFARCHCHECK*::
-Turn off the inferior architecture checking that is normally done
-by the solver. Normally, the solver allows only the installation
-of packages from the "best" architecture if a package is available
-for multiple architectures.
-
-*SOLVER_FLAG_BEST_OBEY_POLICY*::
-Make the SOLVER_FORCEBEST job option consider only packages that
-meet the policies for installed packages, i.e. no downgrades,
-no architecture change, no vendor change (see the first flags
-of this section). If the flag is not specified, the solver will
-enforce the installation of the best package ignoring the
-installed packages, which may conflict with the set policy.
-
-*SOLVER_FLAG_NO_AUTOTARGET*::
-Do not enable auto-targeting up update and distupgrade jobs. See
-the section on targeted updates for more information.
-
-*SOLVER_FLAG_KEEP_ORPHANS*::
-Do not allow orphaned packages to be deinstalled if they get
-in the way of resolving other packages.
-
-*SOLVER_FLAG_BREAK_ORPHANS*::
-Ignore dependencies of orphaned packages that get in the way
-of resolving non-orphaned ones. Setting the flag might result
-in no longer working packages in case they are orphaned.
-
-*SOLVER_FLAG_FOCUS_INSTALLED*::
-Resolve installed packages before resolving the given job.
-Setting this flag means that the solver will prefer picking
-a package version that fits the other installed packages
-over updating installed packages.
-
-Basic rule types:
-
-*SOLVER_RULE_UNKNOWN*::
-A rule of an unknown class. You should never encounter those.
-
-*SOLVER_RULE_PKG*::
-A package dependency rule.
-
-*SOLVER_RULE_UPDATE*::
-A rule to implement the update policy of installed packages. Every
-installed package has an update rule that consists of the packages
-that may replace the installed package.
-
-*SOLVER_RULE_FEATURE*::
-Feature rules are fallback rules used when an update rule is disabled. They
-include all packages that may replace the installed package ignoring the
-update policy, i.e. they contain downgrades, arch changes and so on.
-Without them, the solver would simply erase installed packages if their
-update rule gets disabled.
-
-*SOLVER_RULE_JOB*::
-Job rules implement the job given to the solver.
-
-*SOLVER_RULE_DISTUPGRADE*::
-These are simple negative assertions that make sure that only packages
-are kept that are also available in one of the repositories.
-
-*SOLVER_RULE_INFARCH*::
-Infarch rules are also negative assertions, they disallow the installation
-of packages when there are packages of the same name but with a better
-architecture.
-
-*SOLVER_RULE_CHOICE*::
-Choice rules are used to make sure that the solver prefers updating to
-installing different packages when some dependency is provided by
-multiple packages with different names. The solver may always break
-choice rules, so you will not see them when a problem is found.
-
-*SOLVER_RULE_LEARNT*::
-These rules are generated by the solver to keep it from running into
-the same problem multiple times when it has to backtrack. They are
-the main reason why a sat solver is faster than other dependency solver
-implementations.
-
-Special dependency rule types:
-
-*SOLVER_RULE_PKG_NOT_INSTALLABLE*::
-This rule was added to prevent the installation of a package of an
-architecture that does not work on the system.
-
-*SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP*::
-The package contains a required dependency which was not provided by
-any package.
-
-*SOLVER_RULE_PKG_REQUIRES*::
-Similar to SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP, but in this case
-some packages provided the dependency but none of them could be
-installed due to other dependency issues.
-
-*SOLVER_RULE_PKG_SELF_CONFLICT*::
-The package conflicts with itself. This is not allowed by older rpm
-versions.
-
-*SOLVER_RULE_PKG_CONFLICTS*::
-To fulfill the dependencies two packages need to be installed, but
-one of the packages contains a conflict with the other one.
-
-*SOLVER_RULE_PKG_SAME_NAME*::
-The dependencies can only be fulfilled by multiple versions of
-a package, but installing multiple versions of the same package
-is not allowed.
-
-*SOLVER_RULE_PKG_OBSOLETES*::
-To fulfill the dependencies two packages need to be installed, but
-one of the packages obsoletes the other one.
-
-*SOLVER_RULE_PKG_IMPLICIT_OBSOLETES*::
-To fulfill the dependencies two packages need to be installed, but
-one of the packages has provides a dependency that is obsoleted
-by the other one. See the POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES
-flag.
-
-*SOLVER_RULE_PKG_INSTALLED_OBSOLETES*::
-To fulfill the dependencies a package needs to be installed that is
-obsoleted by an installed package. See the POOL_FLAG_NOINSTALLEDOBSOLETES
-flag.
-
-*SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP*::
-The user asked for installation of a package providing a specific
-dependency, but no available package provides it.
-
-*SOLVER_RULE_JOB_UNKNOWN_PACKAGE*::
-The user asked for installation of a package with a specific name,
-but no available package has that name.
-
-*SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM*::
-The user asked for the erasure of a dependency that is provided by the
-system (i.e. for special hardware or language dependencies), this
-cannot be done with a job.
-
-*SOLVER_RULE_JOB_UNSUPPORTED*::
-The user asked for something that is not yet implemented, e.g. the
-installation of all packages at once.
-
-Policy error constants
-
-*POLICY_ILLEGAL_DOWNGRADE*::
-The solver ask for permission before downgrading packages.
-
-*POLICY_ILLEGAL_ARCHCHANGE*::
-The solver ask for permission before changing the architecture of installed
-packages.
-
-*POLICY_ILLEGAL_VENDORCHANGE*::
-The solver ask for permission before changing the vendor of installed
-packages.
-
-*POLICY_ILLEGAL_NAMECHANGE*::
-The solver ask for permission before replacing an installed packages with
-a package that has a different name.
-
-Solution element type constants
-
-*SOLVER_SOLUTION_JOB*::
-The problem can be solved by removing the specified job.
-
-*SOLVER_SOLUTION_POOLJOB*::
-The problem can be solved by removing the specified job that is defined
-in the pool.
-
-*SOLVER_SOLUTION_INFARCH*::
-The problem can be solved by allowing the installation of the specified
-package with an inferior architecture.
-
-*SOLVER_SOLUTION_DISTUPGRADE*::
-The problem can be solved by allowing to keep the specified package
-installed.
-
-*SOLVER_SOLUTION_BEST*::
-The problem can be solved by allowing to install the specified package
-that is not the best available package.
-
-*SOLVER_SOLUTION_ERASE*::
-The problem can be solved by allowing to erase the specified package.
-
-*SOLVER_SOLUTION_REPLACE*::
-The problem can be solved by allowing to replace the package with some
-other package.
-
-*SOLVER_SOLUTION_REPLACE_DOWNGRADE*::
-The problem can be solved by allowing to replace the package with some
-other package that has a lower version.
-
-*SOLVER_SOLUTION_REPLACE_ARCHCHANGE*::
-The problem can be solved by allowing to replace the package with some
-other package that has a different architecture.
-
-*SOLVER_SOLUTION_REPLACE_VENDORCHANGE*::
-The problem can be solved by allowing to replace the package with some
-other package that has a different vendor.
-
-*SOLVER_SOLUTION_REPLACE_NAMECHANGE*::
-The problem can be solved by allowing to replace the package with some
-other package that has a different name.
-
-
-Reason constants
-
-*SOLVER_REASON_UNRELATED*::
-The package status did not change as it was not related to any job.
-
-*SOLVER_REASON_UNIT_RULE*::
-The package was installed/erased/kept because of a unit rule, i.e. a rule
-where all literals but one were false.
-
-*SOLVER_REASON_KEEP_INSTALLED*::
-The package was chosen when trying to keep as many packages installed as
-possible.
-
-*SOLVER_REASON_RESOLVE_JOB*::
-The decision happened to fulfill a job rule.
-
-*SOLVER_REASON_UPDATE_INSTALLED*::
-The decision happened to fulfill a package update request.
-
-*SOLVER_REASON_CLEANDEPS_ERASE*::
-The package was erased when cleaning up dependencies from other erased
-packages.
-
-*SOLVER_REASON_RESOLVE*::
-The package was installed to fulfill package dependencies.
-
-*SOLVER_REASON_WEAKDEP*::
-The package was installed because of a weak dependency (Recommends or
-Supplements).
-
-*SOLVER_REASON_RESOLVE_ORPHAN*::
-The decision about the package was made when deciding the fate of orphaned
-packages.
-
-*SOLVER_REASON_RECOMMENDED*::
-This is a special case of SOLVER_REASON_WEAKDEP.
-
-*SOLVER_REASON_SUPPLEMENTED*::
-This is a special case of SOLVER_REASON_WEAKDEP.
-
-
-=== ATTRIBUTES ===
-
- Pool *pool; /* read only */
- $job->{pool}
- d.pool
- d.pool
-
-Back pointer to pool.
-
-=== METHODS ===
-
- int set_flag(int flag, int value)
- my $oldvalue = $solver->set_flag($flag, $value);
- oldvalue = solver.set_flag(flag, value)
- oldvalue = solver.set_flag(flag, value)
-
- int get_flag(int flag)
- my $value = $solver->get_flag($flag);
- value = solver.get_flag(flag)
- value = solver.get_flag(flag)
-
-Set/get a solver specific flag. The flags define the policies the solver has
-to obey. The flags are explained in the CONSTANTS section of this class.
-
- Problem *solve(Job *jobs)
- my @problems = $solver->solve(\@jobs);
- problems = solver.solve(jobs)
- problems = solver.solve(jobs)
-
-Solve a problem specified in the job list (plus the jobs defined in the pool).
-Returns an array of problems that need user interaction, or an empty array
-if no problems were encountered. See the Problem class on how to deal with
-problems.
-
- Transaction transaction()
- my $trans = $solver->transaction();
- trans = solver.transaction()
- trans = solver.transaction()
-
-Return the transaction to implement the calculated package changes. A transaction
-is available even if problems were found, this is useful for interactive user
-interfaces that show both the job result and the problems.
-
- int reason = describe_decision(Solvable *s, Rule *OUTPUT)
- my ($reason, $rule) = $solver->describe_decision($solvable);
- (reason, rule) = solver.describe_decision(solvable)
- (reason, rule) = solver.describe_decision(solvable)
-
-Return the reason why a specific solvable was installed or erased. For most of
-the reasons the rule that triggered the decision is also returned.
-
-The Problem Class
------------------
-Problems are the way of the solver to interact with the user. You can simply list
-all problems and terminate your program, but a better way is to present solutions to
-the user and let him pick the ones he likes.
-
-=== ATTRIBUTES ===
-
- Solver *solv; /* read only */
- $problem->{solv}
- problem.solv
- problem.solv
-
-Back pointer to solver object.
-
- Id id; /* read only */
- $problem->{id}
- problem.id
- problem.id
-
-Id of the problem. The first problem has Id 1, they are numbered consecutively.
-
-=== METHODS ===
-
- Rule findproblemrule()
- my $probrule = $problem->findproblemrule();
- probrule = problem.findproblemrule()
- probrule = problem.findproblemrule()
-
-Return the rule that caused the problem. Of course in most situations there is no
-single responsible rule, but many rules that interconnect with each created the
-problem. Nevertheless, the solver uses some heuristic approach to find a rule
-that somewhat describes the problem best to the user.
-
- Rule *findallproblemrules(bool unfiltered = 0)
- my @probrules = $problem->findallproblemrules();
- probrules = problem.findallproblemrule()
- probrules = problem.findallproblemrule()
-
-Return all rules responsible for the problem. The returned set of rules contains
-all the needed information why there was a problem, but it's hard to present
-them to the user in a sensible way. The default is to filter out all update and
-job rules (unless the returned rules only consist of those types).
-
- Solution *solutions()
- my @solutions = $problem->solutions();
- solutions = problem.solutions()
- solutions = problem.solutions()
-
-Return an array containing multiple possible solutions to fix the problem. See
-the solution class for more information.
-
- int solution_count()
- my $cnt = $problem->solution_count();
- cnt = problem.solution_count()
- cnt = problem.solution_count()
-
-Return the number of solutions without creating solution objects.
-
- <stringification>
- my $str = $problem->str;
- str = str(problem)
- str = problem.to_s
-
-Return a string describing the problem. This is a convenience function, it is
-a shorthand for calling findproblemrule(), then ruleinfo() on the problem
-rule and problemstr() on the ruleinfo object.
-
-The Rule Class
---------------
-Rules are the basic block of sat solving. Each package dependency gets translated
-into one or multiple rules.
-
-=== ATTRIBUTES ===
-
- Solver *solv; /* read only */
- $rule->{solv}
- rule.solv
- rule.solv
-
-Back pointer to solver object.
-
- Id id; /* read only */
- $rule->{id}
- rule.id
- rule.id
-
-The id of the rule.
-
- int type; /* read only */
- $rule->{type}
- rule.type
- rule.type
-
-The basic type of the rule. See the constant section of the solver class for the type list.
-
-=== METHODS ===
-
- Ruleinfo info()
- my $ruleinfo = $rule->info();
- ruleinfo = rule.info()
- ruleinfo = rule.info()
-
-Return a Ruleinfo object that contains information about why the rule was created. But
-see the allinfos() method below.
-
- Ruleinfo *allinfos()
- my @ruleinfos = $rule->allinfos();
- ruleinfos = rule.allinfos()
- ruleinfos = rule.allinfos()
-
-As the same dependency rule can get created because of multiple dependencies, one
-Ruleinfo is not enough to describe the reason. Thus the allinfos() method returns
-an array of all infos about a rule.
-
- <equality>
- if ($rule1 == $rule2)
- if rule1 == rule2:
- if rule1 == rule2
-
-Two rules are equal if they belong to the same solver and have the same id.
-
-The Ruleinfo Class
-------------------
-A Ruleinfo describes one reason why a rule was created.
-
-=== ATTRIBUTES ===
-
- Solver *solv; /* read only */
- $ruleinfo->{solv}
- ruleinfo.solv
- ruleinfo.solv
-
-Back pointer to solver object.
-
- int type; /* read only */
- $ruleinfo->{type}
- ruleinfo.type
- ruleinfo.type
-
-The type of the ruleinfo. See the constant section of the solver class for the
-rule type list and the special type list.
-
- Dep *dep; /* read only */
- $ruleinfo->{dep}
- ruleinfo.dep
- ruleinfo.dep
-
-The dependency leading to the creation of the rule.
-
- Dep *dep_id; /* read only */
- $ruleinfo->{'dep_id'}
- ruleinfo.dep_id
- ruleinfo.dep_id
-
-The Id of the dependency leading to the creation of the rule, or zero.
-
- Solvable *solvable; /* read only */
- $ruleinfo->{solvable}
- ruleinfo.solvable
- ruleinfo.solvable
-
-The involved Solvable, e.g. the one containing the dependency.
-
- Solvable *othersolvable; /* read only */
- $ruleinfo->{othersolvable}
- ruleinfo.othersolvable
- ruleinfo.othersolvable
-
-The other involved Solvable (if any), e.g. the one containing providing
-the dependency for conflicts.
-
- const char *problemstr();
- my $str = $ruleinfo->problemstr();
- str = ruleinfo.problemstr()
- str = ruleinfo.problemstr()
-
-A string describing the ruleinfo from a problem perspective. This probably
-only makes sense if the rule is part of a problem.
-
-The Solution Class
-------------------
-A solution solves one specific problem. It consists of multiple solution elements
-that all need to be executed.
-
-=== ATTRIBUTES ===
-
- Solver *solv; /* read only */
- $solution->{solv}
- solution.solv
- solution.solv
-
-Back pointer to solver object.
-
- Id problemid; /* read only */
- $solution->{problemid}
- solution.problemid
- solution.problemid
-
-Id of the problem the solution solves.
-
- Id id; /* read only */
- $solution->{id}
- solution.id
- solution.id
-
-Id of the solution. The first solution has Id 1, they are numbered consecutively.
-
-=== METHODS ===
-
- Solutionelement *elements(bool expandreplaces = 0)
- my @solutionelements = $solution->elements();
- solutionelements = solution.elements()
- solutionelements = solution.elements()
-
-Return an array containing the elements describing what needs to be done to
-implement the specific solution. If expandreplaces is true, elements of type
-SOLVER_SOLUTION_REPLACE will be replaced by one or more elements replace
-elements describing the policy mismatches.
-
- int element_count()
- my $cnt = $solution->solution_count();
- cnt = solution.element_count()
- cnt = solution.element_count()
-
-Return the number of solution elements without creating objects. Note that the
-count does not match the number of objects returned by the elements() method
-of expandreplaces is set to true.
-
-
-The Solutionelement Class
--------------------------
-A solution element describes a single action of a solution. The action is always
-either to remove one specific job or to add a new job that installs or erases
-a single specific package.
-
-=== ATTRIBUTES ===
-
- Solver *solv; /* read only */
- $solutionelement->{solv}
- solutionelement.solv
- solutionelement.solv
-
-Back pointer to solver object.
-
- Id problemid; /* read only */
- $solutionelement->{problemid}
- solutionelement.problemid
- solutionelement.problemid
-
-Id of the problem the element (partly) solves.
-
- Id solutionid; /* read only */
- $solutionelement->{solutionid}
- solutionelement.solutionid
- solutionelement.solutionid
-
-Id of the solution the element is a part of.
-
- Id id; /* read only */
- $solutionelement->{id}
- solutionelement.id
- solutionelement.id
-
-Id of the solution element. The first element has Id 1, they are numbered consecutively.
-
- Id type; /* read only */
- $solutionelement->{type}
- solutionelement.type
- solutionelement.type
-
-Type of the solution element. See the constant section of the solver class for the
-existing types.
-
- Solvable *solvable; /* read only */
- $solutionelement->{solvable}
- solutionelement.solvable
- solutionelement.solvable
-
-The installed solvable that needs to be replaced for replacement elements.
-
- Solvable *replacement; /* read only */
- $solutionelement->{replacement}
- solutionelement.replacement
- solutionelement.replacement
-
-The solvable that needs to be installed to fix the problem.
-
- int jobidx; /* read only */
- $solutionelement->{jobidx}
- solutionelement.jobidx
- solutionelement.jobidx
-
-The index of the job that needs to be removed to fix the problem, or -1 if the
-element is of another type. Note that it's better to change the job to SOLVER_NOOP
-type so that the numbering of other elements does not get disturbed. This
-method works both for types SOLVER_SOLUTION_JOB and SOLVER_SOLUTION_POOLJOB.
-
-=== METHODS ===
-
- Solutionelement *replaceelements()
- my @solutionelements = $solutionelement->replaceelements();
- solutionelements = solutionelement.replaceelements()
- solutionelements = solutionelement.replaceelements()
-
-If the solution element is of type SOLVER_SOLUTION_REPLACE, return an array of
-elements describing the policy mismatches, otherwise return a copy of the
-element. See also the ``expandreplaces'' option in the solution's elements()
-method.
-
- int illegalreplace()
- my $illegal = $solutionelement->illegalreplace();
- illegal = solutionelement.illegalreplace()
- illegal = solutionelement.illegalreplace()
-
-Return an integer that contains the policy mismatch bits or-ed together, or
-zero if there was no policy mismatch. See the policy error constants in
-the solver class.
-
- Job Job()
- my $job = $solutionelement->Job();
- illegal = solutionelement.Job()
- illegal = solutionelement.Job()
-
-Create a job that implements the solution element. Add this job to the array
-of jobs for all elements of type different to SOLVER_SOLUTION_JOB and
-SOLVER_SOLUTION_POOLJOB. For the later two, a SOLVER_NOOB Job is created,
-you should replace the old job with the new one.
-
- const char *str()
- my $str = $solutionelement->str();
- str = solutionelement.str()
- str = solutionelement.str()
-
-A string describing the change the solution element consists of.
-
-The Transaction Class
----------------------
-Transactions describe the output of a solver run. A transaction contains
-a number of transaction elements, each either the installation of a new
-package or the removal of an already installed package. The Transaction
-class supports a classify() method that puts the elements into different
-groups so that a transaction can be presented to the user in a meaningful
-way.
-
-=== CONSTANTS ===
-
-Transaction element types, both active and passive
-
-*SOLVER_TRANSACTION_IGNORE*::
-This element does nothing. Used to map element types that do not match
-the view mode.
-
-*SOLVER_TRANSACTION_INSTALL*::
-This element installs a package.
-
-*SOLVER_TRANSACTION_ERASE*::
-This element erases a package.
-
-*SOLVER_TRANSACTION_MULTIINSTALL*::
-This element installs a package with a different version keeping the other
-versions installed.
-
-*SOLVER_TRANSACTION_MULTIREINSTALL*::
-This element reinstalls an installed package keeping the other versions
-installed.
-
-Transaction element types, active view
-
-*SOLVER_TRANSACTION_REINSTALL*::
-This element re-installs a package, i.e. installs the same package again.
-
-*SOLVER_TRANSACTION_CHANGE*::
-This element installs a package with same name, version, architecture but
-different content.
-
-*SOLVER_TRANSACTION_UPGRADE*::
-This element installs a newer version of an installed package.
-
-*SOLVER_TRANSACTION_DOWNGRADE*::
-This element installs an older version of an installed package.
-
-*SOLVER_TRANSACTION_OBSOLETES*::
-This element installs a package that obsoletes an installed package.
-
-Transaction element types, passive view
-
-*SOLVER_TRANSACTION_REINSTALLED*::
-This element re-installs a package, i.e. installs the same package again.
-
-*SOLVER_TRANSACTION_CHANGED*::
-This element replaces an installed package with one of the same name,
-version, architecture but different content.
-
-*SOLVER_TRANSACTION_UPGRADED*::
-This element replaces an installed package with a new version.
-
-*SOLVER_TRANSACTION_DOWNGRADED*::
-This element replaces an installed package with an old version.
-
-*SOLVER_TRANSACTION_OBSOLETED*::
-This element replaces an installed package with a package that obsoletes
-it.
-
-Pseudo element types for showing extra information used by classify()
-
-*SOLVER_TRANSACTION_ARCHCHANGE*::
-This element replaces an installed package with a package of a different
-architecture.
-
-*SOLVER_TRANSACTION_VENDORCHANGE*::
-This element replaces an installed package with a package of a different
-vendor.
-
-Transaction mode flags
-
-*SOLVER_TRANSACTION_SHOW_ACTIVE*::
-Filter for active view types. The default is to return passive view type,
-i.e. to show how the installed packages get changed.
-
-*SOLVER_TRANSACTION_SHOW_OBSOLETES*::
-Do not map the obsolete view type into INSTALL/ERASE elements.
-
-*SOLVER_TRANSACTION_SHOW_ALL*::
-If multiple packages replace an installed package, only the best of them
-is kept as OBSOLETE element, the other ones are mapped to INSTALL/ERASE
-elements. This is because most applications want to show just one package
-replacing the installed one. The SOLVER_TRANSACTION_SHOW_ALL makes the
-library keep all OBSOLETE elements.
-
-*SOLVER_TRANSACTION_SHOW_MULTIINSTALL*::
-The library maps MULTIINSTALL elements to simple INSTALL elements. This
-flag can be used to disable the mapping.
-
-*SOLVER_TRANSACTION_CHANGE_IS_REINSTALL*::
-Use this flag if you want to map CHANGE elements to the REINSTALL type.
-
-*SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE*::
-Use this flag if you want to map OBSOLETE elements to the UPGRADE type.
-
-*SOLVER_TRANSACTION_MERGE_ARCHCHANGES*::
-Do not add extra categories for every architecture change, instead cumulate
-them in one category.
-
-*SOLVER_TRANSACTION_MERGE_VENDORCHANGES*::
-Do not add extra categories for every vendor change, instead cumulate
-them in one category.
-
-*SOLVER_TRANSACTION_RPM_ONLY*::
-Special view mode that just returns IGNORE, ERASE, INSTALL, MULTIINSTALL
-elements. Useful if you want to find out what to feed to the underlying
-package manager.
-
-Transaction order flags
-
-*SOLVER_TRANSACTION_KEEP_ORDERDATA*::
-Do not throw away the dependency graph used for ordering the transaction.
-This flag is needed if you want to do manual ordering.
-
-=== ATTRIBUTES ===
-
- Pool *pool; /* read only */
- $trans->{pool}
- trans.pool
- trans.pool
-
-Back pointer to pool.
-
-=== METHODS ===
-
- bool isempty();
- $trans->isempty()
- trans.isempty()
- trans.isempty?
-
-Returns true if the transaction does not do anything, i.e. has no elements.
-
- Solvable *newsolvables();
- my @newsolvables = $trans->newsolvables();
- newsolvables = trans.newsolvables()
- newsolvables = trans.newsolvables()
-
-Return all packages that are to be installed by the transaction. These are
-the packages that need to be downloaded from the repositories.
-
- Solvable *keptsolvables();
- my @keptsolvables = $trans->keptsolvables();
- keptsolvables = trans.keptsolvables()
- keptsolvables = trans.keptsolvables()
-
-Return all installed packages that the transaction will keep installed.
-
- Solvable *steps();
- my @steps = $trans->steps();
- steps = trans.steps()
- steps = trans.steps()
-
-Return all solvables that need to be installed (if the returned solvable
-is not already installed) or erased (if the returned solvable is installed).
-A step is also called a transaction element.
-
- int steptype(Solvable *solvable, int mode)
- my $type = $trans->steptype($solvable, $mode);
- type = trans.steptype(solvable, mode)
- type = trans.steptype(solvable, mode)
-
-Return the transaction type of the specified solvable. See the CONSTANTS
-sections for the mode argument flags and the list of returned types.
-
- TransactionClass *classify(int mode = 0)
- my @classes = $trans->classify();
- classes = trans.classify()
- classes = trans.classify()
-
-Group the transaction elements into classes so that they can be displayed
-in a structured way. You can use various mapping mode flags to tweak
-the result to match your preferences, see the mode argument flag in
-the CONSTANTS section. See the TransactionClass class for how to deal
-with the returned objects.
-
- Solvable othersolvable(Solvable *solvable);
- my $other = $trans->othersolvable($solvable);
- other = trans.othersolvable(solvable)
- other = trans.othersolvable(solvable)
-
-Return the ``other'' solvable for a given solvable. For installed packages
-the other solvable is the best package with the same name that replaces
-the installed package, or the best package of the obsoleting packages if
-the package does not get replaced by one with the same name.
-
-For to be installed packages, the ``other'' solvable is the best installed
-package with the same name that will be replaced, or the best packages
-of all the packages that are obsoleted if the new package does not replace
-a package with the same name.
-
-Thus, the ``other'' solvable is normally the package that is also shown
-for a given package.
-
- Solvable *allothersolvables(Solvable *solvable);
- my @others = $trans->allothersolvables($solvable);
- others = trans.allothersolvables(solvable)
- others = trans.allothersolvables(solvable)
-
-For installed packages, returns all of the packages that replace us. For to
-be installed packages, returns all of the packages that the new package
-replaces. The special ``other'' solvable is always the first entry of the
-returned array.
-
- int calc_installsizechange();
- my $change = $trans->calc_installsizechange();
- change = trans.calc_installsizechange()
- change = trans.calc_installsizechange()
-
-Return the size change of the installed system in kilobytes (kibibytes).
-
- void order(int flags = 0);
- $trans->order();
- trans.order()
- trans.order()
-
-Order the steps in the transactions so that dependent packages are updated
-before packages that depend on them. For rpm, you can also use rpmlib's
-ordering functionality, debian's dpkg does not provide a way to order a
-transaction.
-
-=== ACTIVE/PASSIVE VIEW ===
-
-Active view lists what new packages get installed, while passive view shows
-what happens to the installed packages. Most often there's not much
-difference between the two modes, but things get interesting if multiple
-packages get replaced by one new package. Say you have installed packages
-A-1-1 and B-1-1, and now install A-2-1 which has a new dependency that
-obsoletes B. The transaction elements will be
-
- updated A-1-1 (other: A-2-1)
- obsoleted B-1-1 (other: A-2-1)
-
-in passive mode, but
-
- update A-2-1 (other: A-1-1)
- erase B
-
-in active mode. If the mode contains SOLVER_TRANSACTION_SHOW_ALL, the
-passive mode list will be unchanged but the active mode list will just
-contain A-2-1.
-
-The Transactionclass Class
---------------------------
-Objects of this type are returned by the classify() Transaction method.
-
-=== ATTRIBUTES ===
-
- Transaction *transaction; /* read only */
- $class->{transaction}
- class.transaction
- class.transaction
-
-Back pointer to transaction object.
-
- int type; /* read only */
- $class->{type}
- class.type
- class.type
-
-The type of the transaction elements in the class.
-
- int count; /* read only */
- $class->{count}
- class.count
- class.count
-
-The number of elements in the class.
-
- const char *fromstr;
- $class->{fromstr}
- class.fromstr
- class.fromstr
-
-The old vendor or architecture.
-
- const char *tostr;
- $class->{tostr}
- class.tostr
- class.tostr
-
-The new vendor or architecture.
-
- Id fromid;
- $class->{fromid}
- class.fromid
- class.fromid
-
-The id of the old vendor or architecture.
-
- Id toid;
- $class->{toid}
- class.toid
- class.toid
-
-The id of the new vendor or architecture.
-
-=== METHODS ===
-
- void solvables();
- my @solvables = $class->solvables();
- solvables = class.solvables()
- solvables = class.solvables()
-
-Return the solvables for all transaction elements in the class.
-
-Checksums
----------
-Checksums (also called hashes) are used to make sure that downloaded data is
-not corrupt and also as a fingerprint mechanism to check if data has changed.
-
-=== CLASS METHODS ===
-
- Chksum Chksum(Id type)
- my $chksum = solv::Chksum->new($type);
- chksum = solv.Chksum(type)
- chksum = Solv::Chksum.new(type)
-
-Create a checksum object. Currently the following types are supported:
-
- REPOKEY_TYPE_MD5
- REPOKEY_TYPE_SHA1
- REPOKEY_TYPE_SHA256
-
-These keys are constants in the *solv* class.
-
- Chksum Chksum(Id type, const char *hex)
- my $chksum = solv::Chksum->new($type, $hex);
- chksum = solv.Chksum(type, hex)
- chksum = Solv::Chksum.new(type, hex)
-
-Create an already finalized checksum object from a hex string.
-
- Chksum Chksum_from_bin(Id type, char *bin)
- my $chksum = solv::Chksum->from_bin($type, $bin);
- chksum = solv.Chksum.from_bin(type, bin)
- chksum = Solv::Chksum.from_bin(type, bin)
-
-Create an already finalized checksum object from a binary checksum.
-
-=== ATTRIBUTES ===
-
- Id type; /* read only */
- $chksum->{type}
- chksum.type
- chksum.type
-
-Return the type of the checksum object.
-
-=== METHODS ===
-
- void add(const char *str)
- $chksum->add($str);
- chksum.add(str)
- chksum.add(str)
-
-Add a (binary) string to the checksum.
-
- void add_fp(FILE *fp)
- $chksum->add_fp($file);
- chksum.add_fp(file)
- chksum.add_fp(file)
-
-Add the contents of a file to the checksum.
-
- void add_stat(const char *filename)
- $chksum->add_stat($filename);
- chksum.add_stat(filename)
- chksum.add_stat(filename)
-
-Stat the file and add the dev/ino/size/mtime member to the checksum. If the
-stat fails, the members are zeroed.
-
- void add_fstat(int fd)
- $chksum->add_fstat($fd);
- chksum.add_fstat(fd)
- chksum.add_fstat(fd)
-
-Same as add_stat, but instead of the filename a file descriptor is used.
-
- unsigned char *raw()
- my $raw = $chksum->raw();
- raw = chksum.raw()
- raw = chksum.raw()
-
-Finalize the checksum and return the result as raw bytes. This means that the
-result can contain NUL bytes or unprintable characters.
-
- const char *hex()
- my $raw = $chksum->hex();
- raw = chksum.hex()
- raw = chksum.hex()
-
-Finalize the checksum and return the result as hex string.
-
- const char *typestr()
- my $typestr = $chksum->typestr();
- typestr = chksum.typestr
- typestr = chksum.typestr
-
-Return the type of the checksum as a string, e.g. "sha256".
-
- <equality>
- if ($chksum1 == $chksum2)
- if chksum1 == chksum2:
- if chksum1 == chksum2
-
-Checksums are equal if they are of the same type and the finalized results are
-the same.
-
- <stringification>
- my $str = $chksum->str;
- str = str(chksum)
- str = chksum.to_s
-
-If the checksum is finished, the checksum is returned as "<type>:<hex>" string.
-Otherwise "<type>:unfinished" is returned.
-
-
-File Management
----------------
-This functions were added because libsolv uses standard *FILE* pointers to
-read/write files, but languages like perl have their own implementation of
-files. The libsolv functions also support decompression and compression, the
-algorithm is selected by looking at the file name extension.
-
- FILE *xfopen(char *fn, char *mode = "r")
- my $file = solv::xfopen($path);
- file = solv.xfopen(path)
- file = Solv::xfopen(path)
-
-Open a file at the specified path. The `mode` argument is passed on to the
-stdio library.
-
- FILE *xfopen_fd(char *fn, int fileno)
- my $file = solv::xfopen_fd($path, $fileno);
- file = solv.xfopen_fd(path, fileno)
- file = Solv::xfopen_fd(path, fileno)
-
-Create a file handle from the specified file descriptor. The path argument is
-only used to select the correct (de-)compression algorithm, use an empty path
-if you want to make sure to read/write raw data. The file descriptor is dup()ed
-before the file handle is created.
-
-=== METHODS ===
-
- int fileno()
- my $fileno = $file->fileno();
- fileno = file.fileno()
- fileno = file.fileno()
-
-Return file file descriptor of the file. If the file is not open, `-1` is
-returned.
-
- void cloexec(bool state)
- $file->cloexec($state)
- file.cloexec(state)
- file.cloexec(state)
-
-Set the close-on-exec flag of the file descriptor. The xfopen function
-returns files with close-on-exec turned on, so if you want to pass
-a file to some other process you need to call cloexec(0) before calling
-exec.
-
- int dup()
- my $fileno = $file->dup();
- fileno = file.dup()
- fileno = file.dup()
-
-Return a copy of the descriptor of the file. If the file is not open, `-1` is
-returned.
-
- bool flush()
- $file->flush();
- file.flush()
- file.flush()
-
-Flush the file. Returns false if there was an error. Flushing a closed file
-always returns true.
-
- bool close()
- $file->close();
- file.close()
- file.close()
-
-Close the file. This is needed for languages like Ruby that do not destruct
-objects right after they are no longer referenced. In that case, it is good
-style to close open files so that the file descriptors are freed right away.
-Returns false if there was an error.
-
-
-The Repodata Class
-------------------
-The Repodata stores attributes for packages and the repository itself, each
-repository can have multiple repodata areas. You normally only need to
-directly access them if you implement lazy downloading of repository data.
-Repodata areas are created by calling the repository's add_repodata() method
-or by using repo_add methods without the REPO_REUSE_REPODATA or REPO_USE_LOADING
-flag.
-
-=== ATTRIBUTES ===
-
- Repo *repo; /* read only */
- $data->{repo}
- data.repo
- data.repo
-
-Back pointer to repository object.
-
- Id id; /* read only */
- $data->{id}
- data.id
- data.id
-
-The id of the repodata area. Repodata ids of different repositories overlap.
-
-=== METHODS ===
-
- internalize();
- $data->internalize();
- data.internalize()
- data.internalize()
-
-Internalize newly added data. The lookup functions will only see the new data
-after it has been internalized.
-
- bool write(FILE *fp);
- $data->write($fp);
- data.write(fp)
- data.write(fp)
-
-Write the contents of the repodata area as solv file.
-
- bool add_solv(FILE *fp, int flags = 0);
- $data->add_solv($fp);
- data.add_solv(fp)
- data.add_solv(fp)
-
-Replace a stub repodata object with the data from a solv file. This method
-automatically adds the REPO_USE_LOADING flag. It should only be used from
-a load callback.
-
- void create_stubs();
- $data->create_stubs()
- data.create_stubs()
- data.create_stubs()
-
-Create stub repodatas from the information stored in the repodata meta
-area.
-
- void extend_to_repo();
- $data->extend_to_repo();
- data.extend_to_repo()
- data.extend_to_repo()
-
-Extend the repodata so that it has the same size as the repo it belongs to.
-This method is only needed when switching to a just written repodata extension
-to make the repodata match the written extension (which is always of the
-size of the repo).
-
- <equality>
- if ($data1 == $data2)
- if data1 == data2:
- if data1 == data2
-
-Two repodata objects are equal if they belong to the same repository and have
-the same id.
-
-=== DATA RETRIEVAL METHODS ===
-
- const char *lookup_str(Id solvid, Id keyname)
- my $string = $data->lookup_str($solvid, $keyname);
- string = data.lookup_str(solvid, keyname)
- string = data.lookup_str(solvid, keyname)
-
- Id *lookup_idarray(Id solvid, Id keyname)
- my @ids = $data->lookup_idarray($solvid, $keyname);
- ids = data.lookup_idarray(solvid, keyname)
- ids = data.lookup_idarray(solvid, keyname)
-
- Chksum lookup_checksum(Id solvid, Id keyname)
- my $chksum = $data->lookup_checksum($solvid, $keyname);
- chksum = data.lookup_checksum(solvid, keyname)
- chksum = data.lookup_checksum(solvid, keyname)
-
-Lookup functions. Return the data element stored in the specified solvable.
-The methods probably only make sense to retrieve data from the special
-SOLVID_META solvid that stores repodata meta information.
-
-=== DATA STORAGE METHODS ===
-
- void set_id(Id solvid, Id keyname, DepId id);
- $data->set_id($solvid, $keyname, $id);
- data.set_id(solvid, keyname, id)
- data.set_id(solvid, keyname, id)
-
- void set_str(Id solvid, Id keyname, const char *str);
- $data->set_str($solvid, $keyname, $str);
- data.set_str(solvid, keyname, str)
- data.set_str(solvid, keyname, str)
-
- void set_poolstr(Id solvid, Id keyname, const char *str);
- $data->set_poolstr($solvid, $keyname, $str);
- data.set_poolstr(solvid, keyname, str)
- data.set_poolstr(solvid, keyname, str)
-
- void set_checksum(Id solvid, Id keyname, Chksum *chksum);
- $data->set_checksum($solvid, $keyname, $chksum);
- data.set_checksum(solvid, keyname, chksum)
- data.set_checksum(solvid, keyname, chksum)
-
- void add_idarray(Id solvid, Id keyname, DepId id);
- $data->add_idarray($solvid, $keyname, $id);
- data.add_idarray(solvid, keyname, id)
- data.add_idarray(solvid, keyname, id)
-
- Id new_handle();
- my $handle = $data->new_handle();
- handle = data.new_handle()
- handle = data.new_handle()
-
- void add_flexarray(Id solvid, Id keyname, Id handle);
- $data->add_flexarray($solvid, $keyname, $handle);
- data.add_flexarray(solvid, keyname, handle)
- data.add_flexarray(solvid, keyname, handle)
-
-Data storage methods. Probably only useful to store data in the special
-SOLVID_META solvid that stores repodata meta information. Note that
-repodata areas can have their own Id pool (see the REPO_LOCALPOOL flag),
-so be careful if you need to store ids. Arrays are created by calling
-the add function for every element. A flexarray is an array of
-sub-structures, call new_handle to create a new structure, use the
-handle as solvid to fill the structure with data and call add_flexarray
-to put the structure in an array.
-
-
-The Datapos Class
------------------
-Datapos objects describe a specific position in the repository data area.
-Thus they are only valid until the repository is modified in some way.
-Datapos objects can be created by the pos() and parentpos() methods of
-a Datamatch object or by accessing the ``meta'' attribute of a repository.
-
-=== ATTRIBUTES ===
-
- Repo *repo; /* read only */
- $data->{repo}
- data.repo
- data.repo
-
-Back pointer to repository object.
-
-=== METHODS ===
-
- Dataiterator(Id keyname, const char *match, int flags)
- my $di = $datapos->Dataiterator($keyname, $match, $flags);
- di = datapos.Dataiterator(keyname, match, flags)
- di = datapos.Dataiterator(keyname, match, flags)
-
-Create a Dataiterator at the position of the datapos object.
-
- const char *lookup_deltalocation(unsigned int *OUTPUT);
- my ($location, $medianr) = $datapos->lookup_deltalocation();
- location, medianr = datapos.lookup_deltalocation()
- location, medianr = datapos.lookup_deltalocation()
-
-Return a tuple containing the on-media location and an optional media number
-for a delta rpm. This obviously only works if the data position points to
-structure describing a delta rpm.
-
- const char *lookup_deltaseq();
- my $seq = $datapos->lookup_deltaseq();
- seq = datapos.lookup_deltaseq();
- seq = datapos.lookup_deltaseq();
-
-Return the delta rpm sequence from the structure describing a delta rpm.
-
-=== DATA RETRIEVAL METHODS ===
-
- const char *lookup_str(Id keyname)
- my $string = $datapos->lookup_str($keyname);
- string = datapos.lookup_str(keyname)
- string = datapos.lookup_str(keyname)
-
- Id lookup_id(Id solvid, Id keyname)
- my $id = $datapos->lookup_id($keyname);
- id = datapos.lookup_id(keyname)
- id = datapos.lookup_id(keyname)
-
- unsigned long long lookup_num(Id keyname, unsigned long long notfound = 0)
- my $num = $datapos->lookup_num($keyname);
- num = datapos.lookup_num(keyname)
- num = datapos.lookup_num(keyname)
-
- bool lookup_void(Id keyname)
- my $bool = $datapos->lookup_void($keyname);
- bool = datapos.lookup_void(keyname)
- bool = datapos.lookup_void(keyname)
-
- Id *lookup_idarray(Id keyname)
- my @ids = $datapos->lookup_idarray($keyname);
- ids = datapos.lookup_idarray(keyname)
- ids = datapos.lookup_idarray(keyname)
-
- Chksum lookup_checksum(Id keyname)
- my $chksum = $datapos->lookup_checksum($keyname);
- chksum = datapos.lookup_checksum(keyname)
- chksum = datapos.lookup_checksum(keyname)
-
-Lookup functions. Note that the returned Ids are always translated into
-the Ids of the global pool even if the repodata area contains its own pool.
-
- Dataiterator Dataiterator(Id keyname, const char *match = 0, int flags = 0)
- my $di = $datapos->Dataiterator($keyname, $match, $flags);
- di = datapos.Dataiterator(keyname, match, flags)
- di = datapos.Dataiterator(keyname, match, flags)
-
- for my $d (@$di)
- for d in di:
- for d in di
-
-Iterate over the matching data elements. See the Dataiterator class for more
-information.
-
-Author
-------
-Michael Schroeder <mls@suse.de>
-
+++ /dev/null
-'\" t
-.\" Title: Libsolv-Constantids
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 12/14/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "LIBSOLV\-CONSTANTIDS" "3" "12/14/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-libsolv-constantids \- fixed Ids for often used strings
-.SH "DESCRIPTION"
-.sp
-Constant Ids are Ids of strings that are often needed\&. They are defined to ease programming and reduce the number of pool_str2id calls\&. The constant Ids are part of the binary ABI of libsolv, a minor version update will only add new constants and not change existing Ids to maintain compatible\&. The on\-disk solv format works does not use the fixed Ids, but instead references the strings, so solv files can still be read when the ABI is broken\&.
-.SH "SPECIAL STRINGS"
-.PP
-\fBID_EMPTY ""\fR
-.RS 4
-The empty string\&. It will always have Id 1\&.
-.RE
-.PP
-\fBSYSTEM_SYSTEM "system:system"\fR
-.RS 4
-The name of the always installed "system" solvable\&.
-.RE
-.SH "SOLVABLE ATTRIBUTES"
-.sp
-These are Ids for keyname of attributes\&. They can be used in the lookup and storage functions to select the correct attribute in the solvable\&. The descriptions below describe the intended semantics of the values stored in the attribute with the keyname\&.
-.PP
-\fBSOLVABLE_NAME "solvable:name"\fR
-.RS 4
-The name of the package\&.
-.RE
-.PP
-\fBSOLVABLE_ARCH "solvable:arch"\fR
-.RS 4
-The architecture of the package\&. See the Solvable Architecture section for predefined architecture Id values\&.
-.RE
-.PP
-\fBSOLVABLE_EVR "solvable:evr"\fR
-.RS 4
-The version of the package\&. It usually consists of some combination of the Epoch, the Version, and the Release of the solvable\&.
-.RE
-.PP
-\fBSOLVABLE_VENDOR "solvable:vendor"\fR
-.RS 4
-A vendor string\&. Usually the company or group that created the binary package\&.
-.RE
-.PP
-\fBSOLVABLE_PROVIDES "solvable:provides"\fR
-.RS 4
-Stores an array of dependency Ids that describe the capabilities that the package provides\&.
-.RE
-.PP
-\fBSOLVABLE_OBSOLETES "solvable:obsoletes"\fR
-.RS 4
-Stores an array of dependency Ids that describe the packages that this package replaces\&.
-.RE
-.PP
-\fBSOLVABLE_CONFLICTS "solvable:conflicts"\fR
-.RS 4
-Stores an array of dependency Ids that describe the capabilities that this package conflicts with, i\&.e\&. that can\(cqt be installed together with this package\&.
-.RE
-.PP
-\fBSOLVABLE_REQUIRES "solvable:requires"\fR
-.RS 4
-Stores an array of dependency Ids that describe the capabilities that also must be installed when this package is installed\&.
-.RE
-.PP
-\fBSOLVABLE_RECOMMENDS "solvable:recommends"\fR
-.RS 4
-Stores an array of dependency Ids that describe the capabilities that also should be installed when this package is installed\&. It\(cqs not an error if not all capabilities can be met\&.
-.RE
-.PP
-\fBSOLVABLE_SUGGESTS "solvable:suggests"\fR
-.RS 4
-Stores an array of dependency Ids that describe the capabilities that also useful to have installed when this package is installed\&. This is intended to provide a hint to the user about other packages\&.
-.RE
-.PP
-\fBSOLVABLE_SUPPLEMENTS "solvable:supplements"\fR
-.RS 4
-Stores an array of dependency Ids that define that this package should be installed if one of the capabilities is met\&. This is like the recommends attribute, but works in the reverse way\&.
-.RE
-.PP
-\fBSOLVABLE_ENHANCES "solvable:enhances"\fR
-.RS 4
-Stores an array of dependency Ids that define that this package is useful to have installed if one of the capabilities is met\&. This is like the suggests attribute, but works in the reverse way\&.
-.RE
-.PP
-\fBSOLVABLE_SUMMARY "solvable:summary"\fR
-.RS 4
-The summary should be a short string without any newlines that describes what a package does\&.
-.RE
-.PP
-\fBSOLVABLE_DESCRIPTION "solvable:description"\fR
-.RS 4
-The description should be a more verbose description about what a package does\&. It may consist of multiple lines\&.
-.RE
-.PP
-\fBSOLVABLE_DISTRIBUTION "solvable:distribution"\fR
-.RS 4
-The distribution is a short string that describes the OS and OS version this package is built for\&.
-.RE
-.PP
-\fBSOLVABLE_AUTHORS "solvable:authors"\fR
-.RS 4
-A list of authors of this package\&. This attribute was used in SUSE packages\&.
-.RE
-.PP
-\fBSOLVABLE_PACKAGER "solvable:packager"\fR
-.RS 4
-The person who created the binary package, see also the vendor attribute\&.
-.RE
-.PP
-\fBSOLVABLE_GROUP "solvable:group"\fR
-.RS 4
-The package group that this package belongs to\&. See also the keywords attribute\&.
-.RE
-.PP
-\fBSOLVABLE_URL "solvable:url"\fR
-.RS 4
-An URL that points to more information about the package\&.
-.RE
-.PP
-\fBSOLVABLE_KEYWORDS "solvable:keywords"\fR
-.RS 4
-list of keyword string IDs used for tagging this package\&.
-.RE
-.PP
-\fBSOLVABLE_LICENSE "solvable:license"\fR
-.RS 4
-The license(s) of this package\&.
-.RE
-.PP
-\fBSOLVABLE_BUILDTIME "solvable:buildtime"\fR
-.RS 4
-The seconds since the unix epoch when the binary package was created\&.
-.RE
-.PP
-\fBSOLVABLE_BUILDHOST "solvable:buildhost"\fR
-.RS 4
-The name of the host on which the binary package was created\&.
-.RE
-.PP
-\fBSOLVABLE_EULA "solvable:eula"\fR
-.RS 4
-If this attribute is present the user should be asked to accept the end user license agreement before the package gets installed\&.
-.RE
-.PP
-\fBSOLVABLE_CPEID "solvable:cpeid"\fR
-.RS 4
-A Common Platform Enumeration string describes the platform this package is intended for\&. See also the distribution attribute\&.
-.RE
-.PP
-\fBSOLVABLE_MESSAGEINS "solvable:messageins"\fR
-.RS 4
-A message that should be displayed to the user when the package gets installed\&.
-.RE
-.PP
-\fBSOLVABLE_MESSAGEDEL "solvable:messagedel"\fR
-.RS 4
-A message that should be displayed to the user when the package gets erased\&.
-.RE
-.PP
-\fBSOLVABLE_INSTALLSIZE "solvable:installsize"\fR
-.RS 4
-The disk space in bytes needed when installing the package\&.
-.RE
-.PP
-\fBSOLVABLE_DISKUSAGE "solvable:diskusage"\fR
-.RS 4
-A SUSE extension that stores for each directory the needed amount of disk space in kilobytes and inodes\&.
-.RE
-.PP
-\fBSOLVABLE_FILELIST "solvable:filelist"\fR
-.RS 4
-A list of files that the package contains\&.
-.RE
-.PP
-\fBSOLVABLE_INSTALLTIME "solvable:installtime"\fR
-.RS 4
-The seconds since the unix epoch when the binary package was installed on the system\&.
-.RE
-.PP
-\fBSOLVABLE_MEDIADIR "solvable:mediadir"\fR
-.RS 4
-The directory on the repository that contains the package\&. If this attribute is set to void, the package architecture is used as directory\&.
-.RE
-.PP
-\fBSOLVABLE_MEDIAFILE "solvable:mediafile"\fR
-.RS 4
-The filename on the repository that contains the package\&. If this attribute is set to void, the canonical file name of the package is used (i\&.e\&. a combination of the name, version, architecture)\&.
-.RE
-.PP
-\fBSOLVABLE_MEDIANR "solvable:medianr"\fR
-.RS 4
-The media number\&. This is an integer describing on which of a multi\-part media set this package is on\&.
-.RE
-.PP
-\fBSOLVABLE_MEDIABASE "solvable:mediabase"\fR
-.RS 4
-This attribute can be used to overwrite the repositories base url\&.
-.RE
-.PP
-\fBSOLVABLE_DOWNLOADSIZE "solvable:downloadsize"\fR
-.RS 4
-The size of the binary package in bytes\&.
-.RE
-.PP
-\fBSOLVABLE_SOURCEARCH "solvable:sourcearch"\fR
-.RS 4
-The architecture of the source package that this package belongs to\&.
-.RE
-.PP
-\fBSOLVABLE_SOURCENAME "solvable:sourcename"\fR
-.RS 4
-The name of the source package that this package belongs to\&. If set to void, the package name attribute is used instead\&.
-.RE
-.PP
-\fBSOLVABLE_SOURCEEVR "solvable:sourceevr"\fR
-.RS 4
-The version of the source package that this package belongs to\&. If set to void, the package version attribute is used instead\&.
-.RE
-.PP
-\fBSOLVABLE_TRIGGERS "solvable:triggers"\fR
-.RS 4
-A list of package triggers for this package\&. Used in the transaction ordering code\&.
-.RE
-.PP
-\fBSOLVABLE_CHECKSUM "solvable:checksum"\fR
-.RS 4
-The checksum of the binary package\&. See the Data Types section for a list of supported algorithms\&.
-.RE
-.PP
-\fBSOLVABLE_PKGID "solvable:pkgid"\fR
-.RS 4
-A string identifying a package\&. For rpm packages, this is the md5sum over the package header and the payload\&.
-.RE
-.PP
-\fBSOLVABLE_HDRID "solvable:hdrid"\fR
-.RS 4
-A string identifying a package\&. For rpm packages, this is the sha1sum over just the package header\&.
-.RE
-.PP
-\fBSOLVABLE_LEADSIGID "solvable:leadsigid"\fR
-.RS 4
-A string identifying the signature part of a package\&. For rpm packages, this is the md5sum from the start of the file up to the package header (i\&.e\&. it includes the lead, the signature header, and the padding)\&.
-.RE
-.PP
-\fBSOLVABLE_HEADEREND "solvable:headerend"\fR
-.RS 4
-The offset of the payload in rpm binary packages\&. You can use this information to download just the header if you want to display information not included in the repository metadata\&.
-.RE
-.PP
-\fBSOLVABLE_CHANGELOG "solvable:changelog"\fR
-.RS 4
-The array containing all the changelog structures\&.
-.RE
-.PP
-\fBSOLVABLE_CHANGELOG_AUTHOR "solvable:changelog:author"\fR
-.RS 4
-The author of a changelog entry\&.
-.RE
-.PP
-\fBSOLVABLE_CHANGELOG_TIME "solvable:changelog:time"\fR
-.RS 4
-The seconds since the unix epoch when the changelog entry was written\&.
-.RE
-.PP
-\fBSOLVABLE_CHANGELOG_TEXT "solvable:changelog:text"\fR
-.RS 4
-The text of a changelog entry\&.
-.RE
-.SH "SPECIAL SOLVABLE ATTRIBUTES"
-.PP
-\fBRPM_RPMDBID "rpm:dbid"\fR
-.RS 4
-The rpm database id of this (installed) package\&. Usually a small integer number\&.
-.RE
-.PP
-\fBSOLVABLE_PATCHCATEGORY "solvable:patchcategory"\fR
-.RS 4
-The category field for patch solvables\&. Should be named \(lqupdate:category\(rq instead\&.
-.RE
-.PP
-\fBUPDATE_REBOOT "update:reboot"\fR
-.RS 4
-If this attribute is present the system should be rebooted after the update is installed\&.
-.RE
-.PP
-\fBUPDATE_RESTART "update:restart"\fR
-.RS 4
-If this attribute is present the software manager should be run again after the update is installed\&.
-.RE
-.PP
-\fBUPDATE_RELOGIN "update:relogin"\fR
-.RS 4
-If this attribute is present the user should log off and on again after the update is installed\&.
-.RE
-.PP
-\fBUPDATE_MESSAGE "update:message"\fR
-.RS 4
-A message that should be shown to the user to warn him about anything non\-standard\&.
-.RE
-.PP
-\fBUPDATE_SEVERITY "update:severity"\fR
-.RS 4
-The severity of the update\&.
-.RE
-.PP
-\fBUPDATE_RIGHTS "update:rights"\fR
-.RS 4
-Any legal or other rights of the update\&.
-.RE
-.PP
-\fBUPDATE_COLLECTION "update:collection"\fR
-.RS 4
-The array containing the package list of the update\&.
-.RE
-.PP
-\fBUPDATE_COLLECTION_NAME "update:collection:name"\fR
-.RS 4
-The name of the updated package\&.
-.RE
-.PP
-\fBUPDATE_COLLECTION_EVR "update:collection:evr"\fR
-.RS 4
-The version of the updated package\&.
-.RE
-.PP
-\fBUPDATE_COLLECTION_ARCH "update:collection:arch"\fR
-.RS 4
-The architecture of the updated package\&.
-.RE
-.PP
-\fBUPDATE_COLLECTION_FILENAME "update:collection:filename"\fR
-.RS 4
-The file name of the updated package\&.
-.RE
-.PP
-\fBUPDATE_REFERENCE "update:reference"\fR
-.RS 4
-The array containing the reference list of the update\&.
-.RE
-.PP
-\fBUPDATE_REFERENCE_TYPE "update:reference:type"\fR
-.RS 4
-The type of the reference, e\&.g\&. bugzilla\&.
-.RE
-.PP
-\fBUPDATE_REFERENCE_HREF "update:reference:href"\fR
-.RS 4
-The URL of the reference\&.
-.RE
-.PP
-\fBUPDATE_REFERENCE_ID "update:reference:id"\fR
-.RS 4
-The identification string of the reference, e\&.g\&. the bug number\&.
-.RE
-.PP
-\fBUPDATE_REFERENCE_TITLE "update:reference:title"\fR
-.RS 4
-The title of the reference, e\&.g\&. the bug summary\&.
-.RE
-.PP
-\fBPRODUCT_REFERENCEFILE "product:referencefile"\fR
-.RS 4
-The basename of the product file in the package\&.
-.RE
-.PP
-\fBPRODUCT_SHORTLABEL "product:shortlabel"\fR
-.RS 4
-An identification string of the product\&.
-.RE
-.PP
-\fBPRODUCT_DISTPRODUCT "product:distproduct"\fR
-.RS 4
-Obsolete, do not use\&. Was a SUSE Code\-10 product name\&.
-.RE
-.PP
-\fBPRODUCT_DISTVERSION "product:distversion"\fR
-.RS 4
-Obsolete, do not use\&. Was a SUSE Code\-10 product version\&.
-.RE
-.PP
-\fBPRODUCT_TYPE "product:type"\fR
-.RS 4
-The type of the product, e\&.g\&. \(lqbase\(rq\&.
-.RE
-.PP
-\fBPRODUCT_URL "product:url"\fR
-.RS 4
-An array of product URLs\&.
-.RE
-.PP
-\fBPRODUCT_URL_TYPE "product:url:type"\fR
-.RS 4
-An array of product URL types\&.
-.RE
-.PP
-\fBPRODUCT_FLAGS "product:flags"\fR
-.RS 4
-An array of product flags\&.
-.RE
-.PP
-\fBPRODUCT_PRODUCTLINE "product:productline"\fR
-.RS 4
-A product line string used for product registering\&.
-.RE
-.PP
-\fBPRODUCT_REGISTER_TARGET "product:regtarget"\fR
-.RS 4
-A target for product registering\&.
-.RE
-.PP
-\fBPRODUCT_REGISTER_RELEASE "product:regrelease"\fR
-.RS 4
-A release string for product registering\&.
-.RE
-.PP
-\fBPUBKEY_KEYID "pubkey:keyid"\fR
-.RS 4
-The keyid of a pubkey, consisting of 8 bytes in hex\&.
-.RE
-.PP
-\fBPUBKEY_FINGERPRINT "pubkey:fingerprint"\fR
-.RS 4
-The fingerprint of a pubkey, usually a sha1sum in hex\&. Old V3 RSA keys use a md5sum instead\&.
-.RE
-.PP
-\fBPUBKEY_EXPIRES "pubkey:expires"\fR
-.RS 4
-The seconds since the unix epoch when the pubkey expires\&.
-.RE
-.PP
-\fBPUBKEY_SUBKEYOF "pubkey:subkeyof"\fR
-.RS 4
-The keyid of the master pubkey for subkeys\&.
-.RE
-.PP
-\fBPUBKEY_DATA "pubkey:data"\fR
-.RS 4
-The MPI data of the pubkey\&.
-.RE
-.PP
-\fBSOLVABLE_ISVISIBLE "solvable:isvisible"\fR
-.RS 4
-An attribute describing if the package should be listed to the user or not\&. Used for SUSE patterns\&.
-.RE
-.PP
-\fBSOLVABLE_CATEGORY "solvable:category"\fR
-.RS 4
-The category of a pattern\&.
-.RE
-.PP
-\fBSOLVABLE_INCLUDES "solvable:includes"\fR
-.RS 4
-A list of other patterns that this pattern includes\&.
-.RE
-.PP
-\fBSOLVABLE_EXTENDS "solvable:extends"\fR
-.RS 4
-A list of other patterns that this pattern extends\&.
-.RE
-.PP
-\fBSOLVABLE_ICON "solvable:icon"\fR
-.RS 4
-The icon name of a pattern\&.
-.RE
-.PP
-\fBSOLVABLE_ORDER "solvable:order"\fR
-.RS 4
-An ordering clue of a pattern\&.
-.RE
-.PP
-\fBSUSETAGS_SHARE_NAME "susetags:share:name"\fR
-.RS 4
-Internal attribute to implement susetags shared records\&. Holds the name of the solvable used for sharing attributes\&.
-.RE
-.PP
-\fBSUSETAGS_SHARE_EVR "susetags:share:evr"\fR
-.RS 4
-Internal attribute to implement susetags shared records\&. Holds the version of the solvable used for sharing attributes\&.
-.RE
-.PP
-\fBSUSETAGS_SHARE_ARCH "susetags:share:arch"\fR
-.RS 4
-Internal attribute to implement susetags shared records\&. Holds the architecture of the solvable used for sharing attributes\&.
-.RE
-.SH "SOLVABLE ARCHITECTURES"
-.sp
-Predefined architecture values for commonly used architectures\&.
-.PP
-\fBARCH_SRC "src"\fR
-.RS 4
-Used for binary packages that contain the package sources\&.
-.RE
-.PP
-\fBARCH_NOSRC "nosrc"\fR
-.RS 4
-Used for binary packages that contain some of the package sources, but not all files (because of restrictions)\&.
-.RE
-.PP
-\fBARCH_NOARCH "noarch"\fR
-.RS 4
-This package can be installed on any architecture\&. Used for rpm\&.
-.RE
-.PP
-\fBARCH_ALL "all"\fR
-.RS 4
-This package can be installed on any architecture\&. Used for Debian\&.
-.RE
-.PP
-\fBARCH_ANY "any"\fR
-.RS 4
-This package can be installed on any architecture\&. Used for Archlinux and Haiku\&.
-.RE
-.SH "DEPENDENCY IDS"
-.sp
-Namespaces are special modifiers that change the meaning of a dependency\&. Namespace dependencies are created with the REL_NAMESPACE flag\&. To make custom namespaces work you have to implement a namespace callback function\&.
-.sp
-The dependency markers partition the dependency array in two parts with different semantics\&.
-.PP
-\fBNAMESPACE_MODALIAS "namespace:modalias"\fR
-.RS 4
-The dependency is a special modalias dependency that matches installed hardware\&.
-.RE
-.PP
-\fBNAMESPACE_SPLITPROVIDES "namespace:splitprovides"\fR
-.RS 4
-The dependency is a special splitprovides dependency used to implement updates that include a package split\&. A splitprovides dependency contains a filename and a package name, it is matched if a package with the provided package name is installed that contains the filename\&. This namespace is implemented in libsolv, so you do not need a callback\&.
-.RE
-.PP
-\fBNAMESPACE_LANGUAGE "namespace:language"\fR
-.RS 4
-The dependency describes a language\&. The callback should return true if the language was selected by the user\&.
-.RE
-.PP
-\fBNAMESPACE_FILESYSTEM "namespace:filesystem"\fR
-.RS 4
-The dependency describes a filesystem\&. The callback should return true if the filesystem is needed\&.
-.RE
-.PP
-\fBNAMESPACE_OTHERPROVIDERS "namespace:otherproviders"\fR
-.RS 4
-This is a hack to allow self\-conflicting packages\&. It is not needed with current rpm version, so do not use this namespace\&.
-.RE
-.PP
-\fBSOLVABLE_PREREQMARKER "solvable:prereqmarker"\fR
-.RS 4
-This marker partitions the normal require dependencies from the prerequires\&. It is not needed for dependency solving, but it is used by the transaction ordering algorithm when a dependency cycle needs to be broken (non\-prereq deps get broken first)\&.
-.RE
-.PP
-\fBSOLVABLE_FILEMARKER "solvable:filemarker"\fR
-.RS 4
-This marker partitions the package provides dependencies from the synthetic file provides dependencies added by pool_addfileprovides()\&.
-.RE
-.SH "DATA TYPES"
-.sp
-Each attribute data is stored with a type, so that the lookup functions know how to interpret the data\&. The following types are available:
-.PP
-\fBREPOKEY_TYPE_VOID "repokey:type:void"\fR
-.RS 4
-No data is stored with this attribute\&. Thus you can only test if the attribute exists or not\&. Useful to store boolean values\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_CONSTANT "repokey:type:constant"\fR
-.RS 4
-The data is a constant 32bit number\&. The number is stored in the key area, so using it does not cost extra storage space (but you need the extra key space)\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_CONSTANTID "repokey:type:constantid"\fR
-.RS 4
-The data is a constant Id\&. The Id is stored in the key area, so using it does not cost extra storage space (but you need the extra key space)\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_ID "repokey:type:id"\fR
-.RS 4
-The data is an Id\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_NUM "repokey:type:num"\fR
-.RS 4
-The data is an unsigned 64bit number\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_U32 "repokey:type:num32"\fR
-.RS 4
-The data is an unsigned 32bit number\&. Obsolete, do not use\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_DIR "repokey:type:dir"\fR
-.RS 4
-The data is an Id of a directory\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_STR "repokey:type:str"\fR
-.RS 4
-The data is a regular string\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_BINARY "repokey:type:binary"\fR
-.RS 4
-The data is a binary blob\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_IDARRAY "repokey:type:idarray"\fR
-.RS 4
-The data is an array of non\-zero Ids\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_REL_IDARRAY "repokey:type:relidarray"\fR
-.RS 4
-The data is an array of non\-zero Ids ordered so that it needs less space\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_DIRSTRARRAY "repokey:type:dirstrarray"\fR
-.RS 4
-The data is a tuple consisting of a directory Id and a basename\&. Used to store file names\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_DIRNUMNUMARRAY "repokey:type:dirnumnumarray"\fR
-.RS 4
-The data is a triple consisting of a directory Id and two 32bit unsigned integers\&. Used to store disk usage information\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_MD5 "repokey:type:md5"\fR
-.RS 4
-The data is a binary md5sum\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_SHA1 "repokey:type:sha1"\fR
-.RS 4
-The data is a binary sha1sum\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_SHA256 "repokey:type:sha256"\fR
-.RS 4
-The data is a binary sha256sum\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_FIXARRAY "repokey:type:fixarray"\fR
-.RS 4
-The data is an array of structures that have all the same layout (i\&.e\&. the same keynames and keytypes in the same order)\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_FLEXARRAY "repokey:type:flexarray"\fR
-.RS 4
-The data is an array of structures that have a different layout\&.
-.RE
-.PP
-\fBREPOKEY_TYPE_DELETED "repokey:type:deleted"\fR
-.RS 4
-The data does not exist\&. Used to mark an attribute that was deleted\&.
-.RE
-.SH "REPOSITORY METADATA"
-.sp
-This attributes contain meta information about the repository\&.
-.PP
-\fBREPOSITORY_SOLVABLES "repository:solvables"\fR
-.RS 4
-This attribute holds the array including all of the solvables\&. It is only used in the on\-disk solv files, internally the solvables are stored in the pool\(cqs solvable array for fast access\&.
-.RE
-.PP
-\fBREPOSITORY_DELTAINFO "repository:deltainfo"\fR
-.RS 4
-This attribute holds the array including all of the delta packages\&.
-.RE
-.PP
-\fBREPOSITORY_EXTERNAL "repository:external"\fR
-.RS 4
-This attribute holds the array including all of the data to construct stub repodata areas to support on\-demand loading of metadata\&.
-.RE
-.PP
-\fBREPOSITORY_KEYS "repository:keys"\fR
-.RS 4
-This should really be named "repository:external:keys", it contains an array if Ids that consists of (keyname, keytype) pairs that describe the keys of the stub\&.
-.RE
-.PP
-\fBREPOSITORY_LOCATION "repository:location"\fR
-.RS 4
-This is used to provide a file name in the stub\&.
-.RE
-.PP
-\fBREPOSITORY_ADDEDFILEPROVIDES "repository:addedfileprovides"\fR
-.RS 4
-This attribute holds an array of filename Ids, that tell the library, that all of the Ids were already added to the solvable provides\&.
-.RE
-.PP
-\fBREPOSITORY_RPMDBCOOKIE "repository:rpmdbcookie"\fR
-.RS 4
-An attribute that stores a sha256sum over the file stats of the Packages database\&. It\(cqs used to detect rebuilds of the database, as in that case the database Ids of every package are newly distributed\&.
-.RE
-.PP
-\fBREPOSITORY_TIMESTAMP "repository:timestamp"\fR
-.RS 4
-The seconds since the unix epoch when the repository was created\&.
-.RE
-.PP
-\fBREPOSITORY_EXPIRE "repository:expire"\fR
-.RS 4
-The seconds after the timestamp when the repository will expire\&.
-.RE
-.PP
-\fBREPOSITORY_UPDATES "repository:updates"\fR
-.RS 4
-An array of structures describing what this repository updates\&.
-.RE
-.PP
-\fBREPOSITORY_DISTROS "repository:distros"\fR
-.RS 4
-Also an array of structures describing what this repository updates\&. Seems to be the newer name of REPOSITORY_UPDATES\&.
-.RE
-.PP
-\fBREPOSITORY_PRODUCT_LABEL "repository:product:label"\fR
-.RS 4
-Should really be called "repository:updates:label"\&. What distribution is updated with this repository\&.
-.RE
-.PP
-\fBREPOSITORY_PRODUCT_CPEID "repository:product:cpeid"\fR
-.RS 4
-The cpeid of the platform updated by this repository\&. Is both used in REPOSITORY_UPDATES and REPOSITORY_DISTROS to maximize confusion\&.
-.RE
-.PP
-\fBREPOSITORY_REPOID "repository:repoid"\fR
-.RS 4
-An array of Id strings describing keywords/tags about the repository itself\&.
-.RE
-.PP
-\fBREPOSITORY_KEYWORDS "repository:keywords"\fR
-.RS 4
-An array of Id strings describing keywords/tags about the content of the repository\&.
-.RE
-.PP
-\fBREPOSITORY_REVISION "repository:revision"\fR
-.RS 4
-An arbitrary string describing the revision of the repository\&.
-.RE
-.PP
-\fBREPOSITORY_TOOLVERSION "repository:toolversion"\fR
-.RS 4
-Some string describing somewhat the version of libsolv used to create the solv file\&.
-.RE
-.SH "REPOSITORY METADATA FOR SUSETAGS REPOS"
-.sp
-Attributes describing repository files in a susetags repository\&. \fBSUSETAGS_DATADIR "susetags:datadir"\fR:: The directory that contains the packages\&.
-.PP
-\fBSUSETAGS_DESCRDIR "susetags:descrdir"\fR
-.RS 4
-The directory that contains the repository file resources\&.
-.RE
-.PP
-\fBSUSETAGS_DEFAULTVENDOR "susetags:defaultvendor"\fR
-.RS 4
-The default vendor used when a package does not specify a vendor\&.
-.RE
-.PP
-\fBSUSETAGS_FILE "susetags:file"\fR
-.RS 4
-An array of file resources of the repository\&.
-.RE
-.PP
-\fBSUSETAGS_FILE_NAME "susetags:file:name"\fR
-.RS 4
-The filename of the resource\&.
-.RE
-.PP
-\fBSUSETAGS_FILE_TYPE "susetags:file:type"\fR
-.RS 4
-The type of the resource, e\&.g\&. \(lqMETA\(rq\&.
-.RE
-.PP
-\fBSUSETAGS_FILE_CHECKSUM "susetags:file:checksum"\fR
-.RS 4
-The file checksum of the resource\&.
-.RE
-.SH "REPOSITORY METADATA FOR RPMMD REPOS"
-.PP
-\fBREPOSITORY_REPOMD "repository:repomd"\fR
-.RS 4
-An array of file resources of the repository\&.
-.RE
-.PP
-\fBREPOSITORY_REPOMD_TYPE "repository:repomd:type"\fR
-.RS 4
-The type of the resource, e\&.g\&. \(lqprimary\(rq\&.
-.RE
-.PP
-\fBREPOSITORY_REPOMD_LOCATION "repository:repomd:location"\fR
-.RS 4
-The location (aka filename) of the resource
-.RE
-.PP
-\fBREPOSITORY_REPOMD_TIMESTAMP "repository:repomd:timestamp"\fR
-.RS 4
-The seconds since the unix epoch when the resource was created\&.
-.RE
-.PP
-\fBREPOSITORY_REPOMD_CHECKSUM "repository:repomd:checksum"\fR
-.RS 4
-The file checksum of the resource\&.
-.RE
-.PP
-\fBREPOSITORY_REPOMD_OPENCHECKSUM "repository:repomd:openchecksum"\fR
-.RS 4
-The checksum over the uncompressed contents of the resource\&.
-.RE
-.PP
-\fBREPOSITORY_REPOMD_SIZE "repository:repomd:size"\fR
-.RS 4
-The size of the resource file\&.
-.RE
-.SH "DELTA PACKAGE ATTRIBUTES"
-.PP
-\fBDELTA_PACKAGE_NAME "delta:pkgname"\fR
-.RS 4
-The target package name for the delta package\&. Applying the delta will recreate the target package\&.
-.RE
-.PP
-\fBDELTA_PACKAGE_EVR "delta:pkgevr"\fR
-.RS 4
-The version of the target package\&.
-.RE
-.PP
-\fBDELTA_PACKAGE_ARCH "delta:pkgarch"\fR
-.RS 4
-The architecture of the target package\&.
-.RE
-.PP
-\fBDELTA_LOCATION_DIR "delta:locdir"\fR
-.RS 4
-The directory in the repository that contains the delta package\&.
-.RE
-.PP
-\fBDELTA_LOCATION_NAME "delta:locname"\fR
-.RS 4
-The first part of the file name of the delta package\&.
-.RE
-.PP
-\fBDELTA_LOCATION_EVR "delta:locevr"\fR
-.RS 4
-The version part of the file name of the delta package\&.
-.RE
-.PP
-\fBDELTA_LOCATION_SUFFIX "delta:locsuffix"\fR
-.RS 4
-The suffix part of the file name of the delta package\&.
-.RE
-.PP
-\fBDELTA_LOCATION_BASE "delta:locbase"\fR
-.RS 4
-This attribute can be used to overwrite the repositories base url for the delta\&.
-.RE
-.PP
-\fBDELTA_DOWNLOADSIZE "delta:downloadsize"\fR
-.RS 4
-The size of the delta rpm file\&.
-.RE
-.PP
-\fBDELTA_CHECKSUM "delta:checksum"\fR
-.RS 4
-The checksum of the delta rpm file\&.
-.RE
-.PP
-\fBDELTA_BASE_EVR "delta:baseevr"\fR
-.RS 4
-The version of the package the delta was built against\&.
-.RE
-.PP
-\fBDELTA_SEQ_NAME "delta:seqname"\fR
-.RS 4
-The first part of the delta sequence, the base package name\&.
-.RE
-.PP
-\fBDELTA_SEQ_EVR "delta:seqevr"\fR
-.RS 4
-The evr part of the delta sequence, the base package evr\&. Identical to the DELTA_BASE_EVR attribute\&.
-.RE
-.PP
-\fBDELTA_SEQ_NUM "delta:seqnum"\fR
-.RS 4
-The last part of the delta sequence, the content selection string\&.
-.RE
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-Libsolv-Constantids(3)
-======================
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-libsolv-constantids - fixed Ids for often used strings
-
-
-Description
------------
-Constant Ids are Ids of strings that are often needed. They are defined
-to ease programming and reduce the number of pool_str2id calls. The
-constant Ids are part of the binary ABI of libsolv, a minor version
-update will only add new constants and not change existing Ids to
-maintain compatible. The on-disk solv format works does not use the
-fixed Ids, but instead references the strings, so solv files can still
-be read when the ABI is broken.
-
-
-Special Strings
----------------
-*ID_EMPTY ""*::
- The empty string. It will always have Id 1.
-
-*SYSTEM_SYSTEM "system:system"*::
- The name of the always installed "system" solvable.
-
-
-Solvable Attributes
--------------------
-These are Ids for keyname of attributes. They can be used in the
-lookup and storage functions to select the correct attribute in the
-solvable. The descriptions below describe the intended semantics
-of the values stored in the attribute with the keyname.
-
-*SOLVABLE_NAME "solvable:name"*::
- The name of the package.
-
-*SOLVABLE_ARCH "solvable:arch"*::
- The architecture of the package. See the Solvable Architecture section
- for predefined architecture Id values.
-
-*SOLVABLE_EVR "solvable:evr"*::
- The version of the package. It usually consists of some combination of
- the Epoch, the Version, and the Release of the solvable.
-
-*SOLVABLE_VENDOR "solvable:vendor"*::
- A vendor string. Usually the company or group that created the binary
- package.
-
-*SOLVABLE_PROVIDES "solvable:provides"*::
- Stores an array of dependency Ids that describe the capabilities
- that the package provides.
-
-*SOLVABLE_OBSOLETES "solvable:obsoletes"*::
- Stores an array of dependency Ids that describe the packages that this
- package replaces.
-
-*SOLVABLE_CONFLICTS "solvable:conflicts"*::
- Stores an array of dependency Ids that describe the capabilities that
- this package conflicts with, i.e. that can't be installed together with
- this package.
-
-*SOLVABLE_REQUIRES "solvable:requires"*::
- Stores an array of dependency Ids that describe the capabilities that
- also must be installed when this package is installed.
-
-*SOLVABLE_RECOMMENDS "solvable:recommends"*::
- Stores an array of dependency Ids that describe the capabilities that
- also should be installed when this package is installed. It's not an
- error if not all capabilities can be met.
-
-*SOLVABLE_SUGGESTS "solvable:suggests"*::
- Stores an array of dependency Ids that describe the capabilities that
- also useful to have installed when this package is installed. This is
- intended to provide a hint to the user about other packages.
-
-*SOLVABLE_SUPPLEMENTS "solvable:supplements"*::
- Stores an array of dependency Ids that define that this package should
- be installed if one of the capabilities is met. This is like the
- recommends attribute, but works in the reverse way.
-
-*SOLVABLE_ENHANCES "solvable:enhances"*::
- Stores an array of dependency Ids that define that this package is
- useful to have installed if one of the capabilities is met. This is like
- the suggests attribute, but works in the reverse way.
-
-*SOLVABLE_SUMMARY "solvable:summary"*::
- The summary should be a short string without any newlines that describes
- what a package does.
-
-*SOLVABLE_DESCRIPTION "solvable:description"*::
- The description should be a more verbose description about what a
- package does. It may consist of multiple lines.
-
-*SOLVABLE_DISTRIBUTION "solvable:distribution"*::
- The distribution is a short string that describes the OS and OS version
- this package is built for.
-
-*SOLVABLE_AUTHORS "solvable:authors"*::
- A list of authors of this package. This attribute was used in SUSE
- packages.
-
-*SOLVABLE_PACKAGER "solvable:packager"*::
- The person who created the binary package, see also the vendor attribute.
-
-*SOLVABLE_GROUP "solvable:group"*::
- The package group that this package belongs to. See also the keywords
- attribute.
-
-*SOLVABLE_URL "solvable:url"*::
- An URL that points to more information about the package.
-
-*SOLVABLE_KEYWORDS "solvable:keywords"*::
- list of keyword string IDs used for tagging this package.
-
-*SOLVABLE_LICENSE "solvable:license"*::
- The license(s) of this package.
-
-*SOLVABLE_BUILDTIME "solvable:buildtime"*::
- The seconds since the unix epoch when the binary package was created.
-
-*SOLVABLE_BUILDHOST "solvable:buildhost"*::
- The name of the host on which the binary package was created.
-
-*SOLVABLE_EULA "solvable:eula"*::
- If this attribute is present the user should be asked to accept the end
- user license agreement before the package gets installed.
-
-*SOLVABLE_CPEID "solvable:cpeid"*::
- A Common Platform Enumeration string describes the platform this package
- is intended for. See also the distribution attribute.
-
-*SOLVABLE_MESSAGEINS "solvable:messageins"*::
- A message that should be displayed to the user when the package gets
- installed.
-
-*SOLVABLE_MESSAGEDEL "solvable:messagedel"*::
- A message that should be displayed to the user when the package gets
- erased.
-
-*SOLVABLE_INSTALLSIZE "solvable:installsize"*::
- The disk space in bytes needed when installing the package.
-
-*SOLVABLE_DISKUSAGE "solvable:diskusage"*::
- A SUSE extension that stores for each directory the needed amount of
- disk space in kilobytes and inodes.
-
-*SOLVABLE_FILELIST "solvable:filelist"*::
- A list of files that the package contains.
-
-*SOLVABLE_INSTALLTIME "solvable:installtime"*::
- The seconds since the unix epoch when the binary package was installed
- on the system.
-
-*SOLVABLE_MEDIADIR "solvable:mediadir"*::
- The directory on the repository that contains the package. If this
- attribute is set to void, the package architecture is used as
- directory.
-
-*SOLVABLE_MEDIAFILE "solvable:mediafile"*::
- The filename on the repository that contains the package. If this
- attribute is set to void, the canonical file name of the package is
- used (i.e. a combination of the name, version, architecture).
-
-*SOLVABLE_MEDIANR "solvable:medianr"*::
- The media number. This is an integer describing on which of a multi-part
- media set this package is on.
-
-*SOLVABLE_MEDIABASE "solvable:mediabase"*::
- This attribute can be used to overwrite the repositories base url.
-
-*SOLVABLE_DOWNLOADSIZE "solvable:downloadsize"*::
- The size of the binary package in bytes.
-
-*SOLVABLE_SOURCEARCH "solvable:sourcearch"*::
- The architecture of the source package that this package belongs to.
-
-*SOLVABLE_SOURCENAME "solvable:sourcename"*::
- The name of the source package that this package belongs to. If set
- to void, the package name attribute is used instead.
-
-*SOLVABLE_SOURCEEVR "solvable:sourceevr"*::
- The version of the source package that this package belongs to. If set
- to void, the package version attribute is used instead.
-
-*SOLVABLE_TRIGGERS "solvable:triggers"*::
- A list of package triggers for this package. Used in the transaction
- ordering code.
-
-*SOLVABLE_CHECKSUM "solvable:checksum"*::
- The checksum of the binary package. See the Data Types section for
- a list of supported algorithms.
-
-*SOLVABLE_PKGID "solvable:pkgid"*::
- A string identifying a package. For rpm packages, this is the md5sum
- over the package header and the payload.
-
-*SOLVABLE_HDRID "solvable:hdrid"*::
- A string identifying a package. For rpm packages, this is the sha1sum
- over just the package header.
-
-*SOLVABLE_LEADSIGID "solvable:leadsigid"*::
- A string identifying the signature part of a package. For rpm packages,
- this is the md5sum from the start of the file up to the package header
- (i.e. it includes the lead, the signature header, and the padding).
-
-*SOLVABLE_HEADEREND "solvable:headerend"*::
- The offset of the payload in rpm binary packages. You can use this
- information to download just the header if you want to display
- information not included in the repository metadata.
-
-*SOLVABLE_CHANGELOG "solvable:changelog"*::
- The array containing all the changelog structures.
-
-*SOLVABLE_CHANGELOG_AUTHOR "solvable:changelog:author"*::
- The author of a changelog entry.
-
-*SOLVABLE_CHANGELOG_TIME "solvable:changelog:time"*::
- The seconds since the unix epoch when the changelog entry was written.
-
-*SOLVABLE_CHANGELOG_TEXT "solvable:changelog:text"*::
- The text of a changelog entry.
-
-
-Special Solvable Attributes
----------------------------
-*RPM_RPMDBID "rpm:dbid"*::
- The rpm database id of this (installed) package. Usually a small
- integer number.
-
-*SOLVABLE_PATCHCATEGORY "solvable:patchcategory"*::
- The category field for patch solvables. Should be named
- ``update:category'' instead.
-
-*UPDATE_REBOOT "update:reboot"*::
- If this attribute is present the system should be rebooted after
- the update is installed.
-
-*UPDATE_RESTART "update:restart"*::
- If this attribute is present the software manager should be run
- again after the update is installed.
-
-*UPDATE_RELOGIN "update:relogin"*::
- If this attribute is present the user should log off and on again
- after the update is installed.
-
-*UPDATE_MESSAGE "update:message"*::
- A message that should be shown to the user to warn him about anything
- non-standard.
-
-*UPDATE_SEVERITY "update:severity"*::
- The severity of the update.
-
-*UPDATE_RIGHTS "update:rights"*::
- Any legal or other rights of the update.
-
-*UPDATE_COLLECTION "update:collection"*::
- The array containing the package list of the update.
-
-*UPDATE_COLLECTION_NAME "update:collection:name"*::
- The name of the updated package.
-
-*UPDATE_COLLECTION_EVR "update:collection:evr"*::
- The version of the updated package.
-
-*UPDATE_COLLECTION_ARCH "update:collection:arch"*::
- The architecture of the updated package.
-
-*UPDATE_COLLECTION_FILENAME "update:collection:filename"*::
- The file name of the updated package.
-
-*UPDATE_REFERENCE "update:reference"*::
- The array containing the reference list of the update.
-
-*UPDATE_REFERENCE_TYPE "update:reference:type"*::
- The type of the reference, e.g. bugzilla.
-
-*UPDATE_REFERENCE_HREF "update:reference:href"*::
- The URL of the reference.
-
-*UPDATE_REFERENCE_ID "update:reference:id"*::
- The identification string of the reference, e.g. the bug number.
-
-*UPDATE_REFERENCE_TITLE "update:reference:title"*::
- The title of the reference, e.g. the bug summary.
-
-*PRODUCT_REFERENCEFILE "product:referencefile"*::
- The basename of the product file in the package.
-
-*PRODUCT_SHORTLABEL "product:shortlabel"*::
- An identification string of the product.
-
-*PRODUCT_DISTPRODUCT "product:distproduct"*::
- Obsolete, do not use. Was a SUSE Code-10 product name.
-
-*PRODUCT_DISTVERSION "product:distversion"*::
- Obsolete, do not use. Was a SUSE Code-10 product version.
-
-*PRODUCT_TYPE "product:type"*::
- The type of the product, e.g. ``base''.
-
-*PRODUCT_URL "product:url"*::
- An array of product URLs.
-
-*PRODUCT_URL_TYPE "product:url:type"*::
- An array of product URL types.
-
-*PRODUCT_FLAGS "product:flags"*::
- An array of product flags.
-
-*PRODUCT_PRODUCTLINE "product:productline"*::
- A product line string used for product registering.
-
-*PRODUCT_REGISTER_TARGET "product:regtarget"*::
- A target for product registering.
-
-*PRODUCT_REGISTER_RELEASE "product:regrelease"*::
- A release string for product registering.
-
-*PUBKEY_KEYID "pubkey:keyid"*::
- The keyid of a pubkey, consisting of 8 bytes in hex.
-
-*PUBKEY_FINGERPRINT "pubkey:fingerprint"*::
- The fingerprint of a pubkey, usually a sha1sum in hex. Old V3 RSA keys
- use a md5sum instead.
-
-*PUBKEY_EXPIRES "pubkey:expires"*::
- The seconds since the unix epoch when the pubkey expires.
-
-*PUBKEY_SUBKEYOF "pubkey:subkeyof"*::
- The keyid of the master pubkey for subkeys.
-
-*PUBKEY_DATA "pubkey:data"*::
- The MPI data of the pubkey.
-
-*SOLVABLE_ISVISIBLE "solvable:isvisible"*::
- An attribute describing if the package should be listed to the user
- or not. Used for SUSE patterns.
-
-*SOLVABLE_CATEGORY "solvable:category"*::
- The category of a pattern.
-
-*SOLVABLE_INCLUDES "solvable:includes"*::
- A list of other patterns that this pattern includes.
-
-*SOLVABLE_EXTENDS "solvable:extends"*::
- A list of other patterns that this pattern extends.
-
-*SOLVABLE_ICON "solvable:icon"*::
- The icon name of a pattern.
-
-*SOLVABLE_ORDER "solvable:order"*::
- An ordering clue of a pattern.
-
-*SUSETAGS_SHARE_NAME "susetags:share:name"*::
- Internal attribute to implement susetags shared records. Holds the
- name of the solvable used for sharing attributes.
-
-*SUSETAGS_SHARE_EVR "susetags:share:evr"*::
- Internal attribute to implement susetags shared records. Holds the
- version of the solvable used for sharing attributes.
-
-*SUSETAGS_SHARE_ARCH "susetags:share:arch"*::
- Internal attribute to implement susetags shared records. Holds the
- architecture of the solvable used for sharing attributes.
-
-
-Solvable Architectures
-----------------------
-Predefined architecture values for commonly used architectures.
-
-*ARCH_SRC "src"*::
- Used for binary packages that contain the package sources.
-
-*ARCH_NOSRC "nosrc"*::
- Used for binary packages that contain some of the package sources,
- but not all files (because of restrictions).
-
-*ARCH_NOARCH "noarch"*::
- This package can be installed on any architecture. Used for rpm.
-
-*ARCH_ALL "all"*::
- This package can be installed on any architecture. Used for Debian.
-
-*ARCH_ANY "any"*::
- This package can be installed on any architecture. Used for Archlinux
- and Haiku.
-
-
-Dependency Ids
---------------
-Namespaces are special modifiers that change the meaning of a dependency.
-Namespace dependencies are created with the REL_NAMESPACE flag. To make
-custom namespaces work you have to implement a namespace callback function.
-
-The dependency markers partition the dependency array in two parts with
-different semantics.
-
-*NAMESPACE_MODALIAS "namespace:modalias"*::
- The dependency is a special modalias dependency that matches installed
- hardware.
-
-*NAMESPACE_SPLITPROVIDES "namespace:splitprovides"*::
- The dependency is a special splitprovides dependency used to implement
- updates that include a package split. A splitprovides dependency contains
- a filename and a package name, it is matched if a package with the
- provided package name is installed that contains the filename.
- This namespace is implemented in libsolv, so you do not need a callback.
-
-*NAMESPACE_LANGUAGE "namespace:language"*::
- The dependency describes a language. The callback should return true
- if the language was selected by the user.
-
-*NAMESPACE_FILESYSTEM "namespace:filesystem"*::
- The dependency describes a filesystem. The callback should return true
- if the filesystem is needed.
-
-*NAMESPACE_OTHERPROVIDERS "namespace:otherproviders"*::
- This is a hack to allow self-conflicting packages. It is not needed
- with current rpm version, so do not use this namespace.
-
-*SOLVABLE_PREREQMARKER "solvable:prereqmarker"*::
- This marker partitions the normal require dependencies from the
- prerequires. It is not needed for dependency solving, but it is
- used by the transaction ordering algorithm when a dependency cycle
- needs to be broken (non-prereq deps get broken first).
-
-*SOLVABLE_FILEMARKER "solvable:filemarker"*::
- This marker partitions the package provides dependencies from the
- synthetic file provides dependencies added by pool_addfileprovides().
-
-
-Data Types
-----------
-Each attribute data is stored with a type, so that the lookup functions
-know how to interpret the data. The following types are available:
-
-*REPOKEY_TYPE_VOID "repokey:type:void"*::
- No data is stored with this attribute. Thus you can only test if
- the attribute exists or not. Useful to store boolean values.
-
-*REPOKEY_TYPE_CONSTANT "repokey:type:constant"*::
- The data is a constant 32bit number. The number is stored in the key
- area, so using it does not cost extra storage space (but you need the
- extra key space).
-
-*REPOKEY_TYPE_CONSTANTID "repokey:type:constantid"*::
- The data is a constant Id. The Id is stored in the key area,
- so using it does not cost extra storage space (but you need the
- extra key space).
-
-*REPOKEY_TYPE_ID "repokey:type:id"*::
- The data is an Id.
-
-*REPOKEY_TYPE_NUM "repokey:type:num"*::
- The data is an unsigned 64bit number.
-
-*REPOKEY_TYPE_U32 "repokey:type:num32"*::
- The data is an unsigned 32bit number. Obsolete, do not use.
-
-*REPOKEY_TYPE_DIR "repokey:type:dir"*::
- The data is an Id of a directory.
-
-*REPOKEY_TYPE_STR "repokey:type:str"*::
- The data is a regular string.
-
-*REPOKEY_TYPE_BINARY "repokey:type:binary"*::
- The data is a binary blob.
-
-*REPOKEY_TYPE_IDARRAY "repokey:type:idarray"*::
- The data is an array of non-zero Ids.
-
-*REPOKEY_TYPE_REL_IDARRAY "repokey:type:relidarray"*::
- The data is an array of non-zero Ids ordered so that it needs less
- space.
-
-*REPOKEY_TYPE_DIRSTRARRAY "repokey:type:dirstrarray"*::
- The data is a tuple consisting of a directory Id and a basename.
- Used to store file names.
-
-*REPOKEY_TYPE_DIRNUMNUMARRAY "repokey:type:dirnumnumarray"*::
- The data is a triple consisting of a directory Id and two 32bit
- unsigned integers. Used to store disk usage information.
-
-*REPOKEY_TYPE_MD5 "repokey:type:md5"*::
- The data is a binary md5sum.
-
-*REPOKEY_TYPE_SHA1 "repokey:type:sha1"*::
- The data is a binary sha1sum.
-
-*REPOKEY_TYPE_SHA256 "repokey:type:sha256"*::
- The data is a binary sha256sum.
-
-*REPOKEY_TYPE_FIXARRAY "repokey:type:fixarray"*::
- The data is an array of structures that have all the same layout
- (i.e. the same keynames and keytypes in the same order).
-
-*REPOKEY_TYPE_FLEXARRAY "repokey:type:flexarray"*::
- The data is an array of structures that have a different layout.
-
-*REPOKEY_TYPE_DELETED "repokey:type:deleted"*::
- The data does not exist. Used to mark an attribute that was deleted.
-
-
-Repository Metadata
--------------------
-This attributes contain meta information about the repository.
-
-*REPOSITORY_SOLVABLES "repository:solvables"*::
- This attribute holds the array including all of the solvables. It is
- only used in the on-disk solv files, internally the solvables are
- stored in the pool's solvable array for fast access.
-
-*REPOSITORY_DELTAINFO "repository:deltainfo"*::
- This attribute holds the array including all of the delta packages.
-
-*REPOSITORY_EXTERNAL "repository:external"*::
- This attribute holds the array including all of the data to construct
- stub repodata areas to support on-demand loading of metadata.
-
-*REPOSITORY_KEYS "repository:keys"*::
- This should really be named "repository:external:keys", it contains an
- array if Ids that consists of (keyname, keytype) pairs that describe the
- keys of the stub.
-
-*REPOSITORY_LOCATION "repository:location"*::
- This is used to provide a file name in the stub.
-
-*REPOSITORY_ADDEDFILEPROVIDES "repository:addedfileprovides"*::
- This attribute holds an array of filename Ids, that tell the library,
- that all of the Ids were already added to the solvable provides.
-
-*REPOSITORY_RPMDBCOOKIE "repository:rpmdbcookie"*::
- An attribute that stores a sha256sum over the file stats of the
- Packages database. It's used to detect rebuilds of the database,
- as in that case the database Ids of every package are newly
- distributed.
-
-*REPOSITORY_TIMESTAMP "repository:timestamp"*::
- The seconds since the unix epoch when the repository was created.
-
-*REPOSITORY_EXPIRE "repository:expire"*::
- The seconds after the timestamp when the repository will expire.
-
-*REPOSITORY_UPDATES "repository:updates"*::
- An array of structures describing what this repository updates.
-
-*REPOSITORY_DISTROS "repository:distros"*::
- Also an array of structures describing what this repository updates.
- Seems to be the newer name of REPOSITORY_UPDATES.
-
-*REPOSITORY_PRODUCT_LABEL "repository:product:label"*::
- Should really be called "repository:updates:label". What distribution
- is updated with this repository.
-
-*REPOSITORY_PRODUCT_CPEID "repository:product:cpeid"*::
- The cpeid of the platform updated by this repository. Is both used
- in REPOSITORY_UPDATES and REPOSITORY_DISTROS to maximize confusion.
-
-*REPOSITORY_REPOID "repository:repoid"*::
- An array of Id strings describing keywords/tags about the repository
- itself.
-
-*REPOSITORY_KEYWORDS "repository:keywords"*::
- An array of Id strings describing keywords/tags about the content of
- the repository.
-
-*REPOSITORY_REVISION "repository:revision"*::
- An arbitrary string describing the revision of the repository.
-
-*REPOSITORY_TOOLVERSION "repository:toolversion"*::
- Some string describing somewhat the version of libsolv used to create
- the solv file.
-
-
-Repository Metadata for Susetags Repos
---------------------------------------
-Attributes describing repository files in a susetags repository.
-*SUSETAGS_DATADIR "susetags:datadir"*::
- The directory that contains the packages.
-
-*SUSETAGS_DESCRDIR "susetags:descrdir"*::
- The directory that contains the repository file resources.
-
-*SUSETAGS_DEFAULTVENDOR "susetags:defaultvendor"*::
- The default vendor used when a package does not specify a vendor.
-
-*SUSETAGS_FILE "susetags:file"*::
- An array of file resources of the repository.
-
-*SUSETAGS_FILE_NAME "susetags:file:name"*::
- The filename of the resource.
-
-*SUSETAGS_FILE_TYPE "susetags:file:type"*::
- The type of the resource, e.g. ``META''.
-
-*SUSETAGS_FILE_CHECKSUM "susetags:file:checksum"*::
- The file checksum of the resource.
-
-
-Repository Metadata for RpmMD Repos
------------------------------------
-*REPOSITORY_REPOMD "repository:repomd"*::
- An array of file resources of the repository.
-
-*REPOSITORY_REPOMD_TYPE "repository:repomd:type"*::
- The type of the resource, e.g. ``primary''.
-
-*REPOSITORY_REPOMD_LOCATION "repository:repomd:location"*::
- The location (aka filename) of the resource
-
-*REPOSITORY_REPOMD_TIMESTAMP "repository:repomd:timestamp"*::
- The seconds since the unix epoch when the resource was created.
-
-*REPOSITORY_REPOMD_CHECKSUM "repository:repomd:checksum"*::
- The file checksum of the resource.
-
-*REPOSITORY_REPOMD_OPENCHECKSUM "repository:repomd:openchecksum"*::
- The checksum over the uncompressed contents of the resource.
-
-*REPOSITORY_REPOMD_SIZE "repository:repomd:size"*::
- The size of the resource file.
-
-
-Delta Package Attributes
-------------------------
-*DELTA_PACKAGE_NAME "delta:pkgname"*::
- The target package name for the delta package. Applying the delta
- will recreate the target package.
-
-*DELTA_PACKAGE_EVR "delta:pkgevr"*::
- The version of the target package.
-
-*DELTA_PACKAGE_ARCH "delta:pkgarch"*::
- The architecture of the target package.
-
-*DELTA_LOCATION_DIR "delta:locdir"*::
- The directory in the repository that contains the delta package.
-
-*DELTA_LOCATION_NAME "delta:locname"*::
- The first part of the file name of the delta package.
-
-*DELTA_LOCATION_EVR "delta:locevr"*::
- The version part of the file name of the delta package.
-
-*DELTA_LOCATION_SUFFIX "delta:locsuffix"*::
- The suffix part of the file name of the delta package.
-
-*DELTA_LOCATION_BASE "delta:locbase"*::
- This attribute can be used to overwrite the repositories base url for
- the delta.
-
-*DELTA_DOWNLOADSIZE "delta:downloadsize"*::
- The size of the delta rpm file.
-
-*DELTA_CHECKSUM "delta:checksum"*::
- The checksum of the delta rpm file.
-
-*DELTA_BASE_EVR "delta:baseevr"*::
- The version of the package the delta was built against.
-
-*DELTA_SEQ_NAME "delta:seqname"*::
- The first part of the delta sequence, the base package name.
-
-*DELTA_SEQ_EVR "delta:seqevr"*::
- The evr part of the delta sequence, the base package evr. Identical
- to the DELTA_BASE_EVR attribute.
-
-*DELTA_SEQ_NUM "delta:seqnum"*::
- The last part of the delta sequence, the content selection string.
-
-
-Author
-------
-Michael Schroeder <mls@suse.de>
-
+++ /dev/null
-'\" t
-.\" Title: Libsolv-History
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "LIBSOLV\-HISTORY" "3" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-libsolv-history \- how the libsolv library came into existence
-.SH "HISTORY"
-.sp
-This project was started in May 2007 when the zypp folks decided to switch to a database to speed up installation\&. As I am not a big fan of databases, I (mls) wondered if there would be really some merit of using one for solving, as package dependencies of all packages have to be read in anyway\&.
-.sp
-Back in 2002, I researched that using a dictionary approach for storing dependencies can reduce the packages file to 1/3 of its size\&. Extending this idea a bit more, I decided to store all strings and relations as unique 32\-bit numbers\&. This has three big advantages:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-because of the unification, testing whether two strings are equal is the same as testing the equality of two numbers, thus very fast
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-much space is saved, as numbers do not take up as much space as strings the internal memory representation does not take more space on a 64\-bit system where a pointer is twice the size of a 32\-bit number
-.RE
-.sp
-Thus, the solv format was created, which stores a repository as a string dictionary, a relation dictionary and then all packages dependencies\&. Tests showed that reading and merging multiple solv repositories takes just some milliseconds\&.
-.SS "Early solver experiments"
-.sp
-Having a new repository format was one big step, but the other area where libzypp needed improvement was the solver\&. Libzypp\(cqs solver was a port from the Red Carpet solver, which was written to update packages in an already installed system\&. Using it for the complete installation progress brought it to its limits\&. Also, the added extensions like support for weak dependencies and patches made it fragile and unpredictable\&.
-.sp
-As I was not very pleased with the way the solver worked, I looked at other solver algorithms\&. I checked smart, yum and apt, but could not find a convincing algorithm\&. My own experiments also were not very convincing, they worked fine for some problems but failed miserably for other corner cases\&.
-.SS "Using SAT for solving"
-.sp
-SUSE\(cqs hack week at the end of June 2007 turned out to be a turning point for the solver\&. Googling for solver algorithms, I stumbled over some note saying that some people are trying to use SAT algorithms to improve solving on Debian\&. Looking at the SAT entry in Wikipedia, it was easy to see that this indeed was the missing piece: SAT algorithms are well researched and there are quite some open source implementations\&. I decided to look at the minisat code, as it is one of the fastest solvers while consisting of too many lines of code\&.
-.sp
-Of course, directly using minisat would not work, as a package solver does not need to find just one correct solution, but it also has to optimize some metrics, i\&.e\&. keep as many packages installed as possible\&. Thus, I needed to write my own solver incorporation the ideas and algorithms used in minisat\&. This wasn\(cqt very hard, and at the end of the hack week the solver calculated the first right solutions\&.
-.SS "Selling it to libzypp"
-.sp
-With those encouraging results, I went to Klaus Kaempf, the system management architect at SUSE\&. We spoke about how to convince the team to make libzypp switch to the new solver\&. Fortunately, libzypp comes with a plethora of solver test cases, so we decided to make the solver pass most of the test cases first\&. Klaus wrote a "deptestomatic" implementation to check the test cases\&. Together with Stephan Kulow, who is responsible for the openSUSE distribution, we tweaked and extended the solver until most of the test cases looked good\&.
-.sp
-Duncan Mac\-Vicar Prett, the team lead of the YaST team, also joined development by creating Ruby bindings for the solver\&. Later, Klaus improved the bindings and ported them to some other languages\&.
-.SS "The attribute store"
-.sp
-The progress with the repository format and the solver attracted another hacker to the project: Michael Matz from the compiler team\&. He started with improving the repository parsers so that patches and content files also generate solvables\&. After that, he concentrated on storing all of the other metadata of the repositories that are not used for solving, like the package summaries and descriptions\&. At the end of October, a first version of this "attribute store" was checked in\&. Its design goals were:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-space efficient storage of attributes
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-paging/on demand loading of data
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-page compression
-.RE
-.sp
-The first version of the attribute store used a different format for storing information, we later merged this format with the solv file format\&.
-.SS "libzypp integration"
-.sp
-Integration of the sat\-solver into libzypp also started in October 2007 by Stefan Schubert and Michael Andres from the YaST team\&. The first versions supported both the old solver and the new one by using the old repository read functions and converting the old package data in\-memory into a sat solver pool\&. Solvers could be switched with the environment variable ZYPP_SAT_SOLVER\&. The final decision to move to the new solver was made in January of 2008, first just by making the new solver the default one, later by completely throwing out the old solver code\&. This had the advantage that the internal solvable storage could also be done by using the solver pool, something Michael Matz already played with in a proof of concept implementation showing some drastic speed gains\&. The last traces of the old database code were removed in February\&.
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-Libsolv-History(3)
-==================
-:man manual: LIBSOLV
-:man source: libsolv
-
-Name
-----
-libsolv-history - how the libsolv library came into existence
-
-History
--------
-This project was started in May 2007 when the zypp folks decided to switch
-to a database to speed up installation. As I am not a big fan of databases,
-I (mls) wondered if there would be really some merit of using one for solving,
-as package dependencies of all packages have to be read in anyway.
-
-Back in 2002, I researched that using a dictionary approach for storing
-dependencies can reduce the packages file to 1/3 of its size. Extending
-this idea a bit more, I decided to store all strings and relations
-as unique 32-bit numbers. This has three big advantages:
-
-- because of the unification, testing whether two strings are equal is the
- same as testing the equality of two numbers, thus very fast
-- much space is saved, as numbers do not take up as much space as strings
- the internal memory representation does not take more space on a
- 64-bit system where a pointer is twice the size of a 32-bit number
-
-Thus, the solv format was created, which stores a repository as a string
-dictionary, a relation dictionary and then all packages dependencies.
-Tests showed that reading and merging multiple solv repositories takes
-just some milliseconds.
-
-=== Early solver experiments ===
-Having a new repository format was one big step, but the other area
-where libzypp needed improvement was the solver. Libzypp's solver was
-a port from the Red Carpet solver, which was written to update packages
-in an already installed system. Using it for the complete installation
-progress brought it to its limits. Also, the added extensions like
-support for weak dependencies and patches made it fragile and
-unpredictable.
-
-As I was not very pleased with the way the solver worked, I looked at
-other solver algorithms. I checked smart, yum and apt, but could not
-find a convincing algorithm. My own experiments also were not very
-convincing, they worked fine for some problems but failed miserably
-for other corner cases.
-
-=== Using SAT for solving ===
-SUSE's hack week at the end of June 2007 turned out to be a turning point
-for the solver. Googling for solver algorithms, I stumbled over some note
-saying that some people are trying to use SAT algorithms to improve
-solving on Debian. Looking at the SAT entry in Wikipedia, it was easy
-to see that this indeed was the missing piece: SAT algorithms are well
-researched and there are quite some open source implementations.
-I decided to look at the minisat code, as it is one of the fastest
-solvers while consisting of too many lines of code.
-
-Of course, directly using minisat would not work, as a package solver
-does not need to find just one correct solution, but it also has to
-optimize some metrics, i.e. keep as many packages installed as possible.
-Thus, I needed to write my own solver incorporation the ideas and
-algorithms used in minisat. This wasn't very hard, and at the end of
-the hack week the solver calculated the first right solutions.
-
-=== Selling it to libzypp ===
-With those encouraging results, I went to Klaus Kaempf, the system
-management architect at SUSE. We spoke about how to convince the
-team to make libzypp switch to the new solver. Fortunately, libzypp comes
-with a plethora of solver test cases, so we decided to make the solver pass
-most of the test cases first. Klaus wrote a "deptestomatic" implementation
-to check the test cases. Together with Stephan Kulow, who is responsible for the
-openSUSE distribution, we tweaked and extended the solver until most of
-the test cases looked good.
-
-Duncan Mac-Vicar Prett, the team lead of the YaST team, also joined
-development by creating Ruby bindings for the solver. Later, Klaus
-improved the bindings and ported them to some other languages.
-
-=== The attribute store ===
-The progress with the repository format and the solver attracted another
-hacker to the project: Michael Matz from the compiler team. He started
-with improving the repository parsers so that patches and content files
-also generate solvables. After that, he concentrated on storing all
-of the other metadata of the repositories that are not used for solving,
-like the package summaries and descriptions. At the end of October, a first
-version of this "attribute store" was checked in. Its design goals were:
-
-- space efficient storage of attributes
-- paging/on demand loading of data
-- page compression
-
-The first version of the attribute store used a different format for
-storing information, we later merged this format with the solv file
-format.
-
-=== libzypp integration ===
-Integration of the sat-solver into libzypp also started in October 2007 by
-Stefan Schubert and Michael Andres from the YaST team. The first
-versions supported both the old solver and the new one by using the
-old repository read functions and converting the old package data
-in-memory into a sat solver pool. Solvers could be switched with
-the environment variable ZYPP_SAT_SOLVER. The final decision to
-move to the new solver was made in January of 2008, first just by
-making the new solver the default one, later by completely throwing out
-the old solver code. This had the advantage that the internal solvable
-storage could also be done by using the solver pool, something Michael
-Matz already played with in a proof of concept implementation showing
-some drastic speed gains. The last traces of the old database code
-were removed in February.
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: Libsolv-Pool
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 12/14/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "LIBSOLV\-POOL" "3" "12/14/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-libsolv-pool \- Libsolv\*(Aqs pool object
-.SH "PUBLIC ATTRIBUTES"
-.PP
-\fBvoid *appdata\fR
-.RS 4
-A no\-purpose pointer free to use for the library user\&. Freeing the pool simply discards the pointer\&.
-.RE
-.PP
-\fBStringpool ss\fR
-.RS 4
-The pool of unified strings\&.
-.RE
-.PP
-\fBReldep *rels\fR
-.RS 4
-The pool of unified relation dependencies\&.
-.RE
-.PP
-\fBint nrels\fR
-.RS 4
-Number of allocated relation dependencies\&.
-.RE
-.PP
-\fBRepo **repos\fR
-.RS 4
-The array of repository pointers, indexed by repository Id\&.
-.RE
-.PP
-\fBint nrepos\fR
-.RS 4
-Number of allocated repository array elements, i\&.e\&. the size of the repos array\&.
-.RE
-.PP
-\fBint urepos\fR
-.RS 4
-Number of used (i\&.e\&. non\-zero) repository array elements\&.
-.RE
-.PP
-\fBRepo *installed\fR
-.RS 4
-Pointer to the repo holding the installed packages\&. You are free to read this attribute, but you should use pool_set_installed() if you want to change it\&.
-.RE
-.PP
-\fBSolvable *solvables\fR
-.RS 4
-The array of Solvable objects\&.
-.RE
-.PP
-\fBint nsolvables\fR
-.RS 4
-Number of Solvable objects, i\&.e\&. the size of the solvables array\&. Note that the array may contain freed solvables, in that case the repo pointer of the solvable will be zero\&.
-.RE
-.PP
-\fBint disttype\fR
-.RS 4
-The distribution type of your system, e\&.g\&. DISTTYPE_DEB\&. You are free to read this attribute, but you should use pool_setdisttype() if you want to change it\&.
-.RE
-.PP
-\fBId *whatprovidesdata\fR
-.RS 4
-Multi\-purpose Id storage holding zero terminated arrays of Ids\&. pool_whatprovides() returns an offset into this data\&.
-.RE
-.PP
-\fBMap *considered\fR
-.RS 4
-Optional bitmap that can make the library ignore solvables\&. If a bitmap is set, only solvables that have a set bit in the bitmap at their Id are considered usable\&.
-.RE
-.PP
-\fBint debugmask\fR
-.RS 4
-A mask that defines which debug events should be reported\&. pool_setdebuglevel() sets this mask\&.
-.RE
-.PP
-\fBDatapos pos\fR
-.RS 4
-An object storing some position in the repository data\&. Functions like dataiterator_set_pos() set this object, accessing data with a pseudo solvable Id of SOLVID_POS uses it\&.
-.RE
-.PP
-\fBQueue pooljobs\fR
-.RS 4
-A queue where fixed solver jobs can be stored\&. This jobs are automatically added when solver_solve() is called, they are useful to store configuration data like which packages should be multiversion installed\&.
-.RE
-.SH "CREATION AND DESTRUCTION"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBPool *pool_create()\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a new instance of a pool\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_free(Pool *\fR\fIpool\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Free a pool and all of the data it contains, e\&.g\&. the solvables, repositories, strings\&.
-.SH "DEBUGGING AND ERROR REPORTING"
-.SS "Constants"
-.PP
-\fBSOLV_FATAL\fR
-.RS 4
-Report the error and call \(lqexit(1)\(rq afterwards\&. You cannot mask this level\&. Reports to stderr instead of stdout\&.
-.RE
-.PP
-\fBSOLV_ERROR\fR
-.RS 4
-Used to report errors\&. Reports to stderr instead of stdout\&.
-.RE
-.PP
-\fBSOLV_WARN\fR
-.RS 4
-Used to report warnings\&.
-.RE
-.PP
-\fBSOLV_DEBUG_STATS\fR
-.RS 4
-Used to report statistical data\&.
-.RE
-.PP
-\fBSOLV_DEBUG_RULE_CREATION\fR
-.RS 4
-Used to report information about the solver\(cqs creation of rules\&.
-.RE
-.PP
-\fBSOLV_DEBUG_PROPAGATE\fR
-.RS 4
-Used to report information about the solver\(cqs unit rule propagation process\&.
-.RE
-.PP
-\fBSOLV_DEBUG_ANALYZE\fR
-.RS 4
-Used to report information about the solver\(cqs learnt rule generation mechanism\&.
-.RE
-.PP
-\fBSOLV_DEBUG_UNSOLVABLE\fR
-.RS 4
-Used to report information about the solver dealing with conflicting rules\&.
-.RE
-.PP
-\fBSOLV_DEBUG_SOLUTIONS\fR
-.RS 4
-Used to report information about the solver creating solutions to solve problems\&.
-.RE
-.PP
-\fBSOLV_DEBUG_POLICY\fR
-.RS 4
-Used to report information about the solver searching for an optimal solution\&.
-.RE
-.PP
-\fBSOLV_DEBUG_RESULT\fR
-.RS 4
-Used by the debug functions to output results\&.
-.RE
-.PP
-\fBSOLV_DEBUG_JOB\fR
-.RS 4
-Used to report information about the job rule generation process\&.
-.RE
-.PP
-\fBSOLV_DEBUG_SOLVER\fR
-.RS 4
-Used to report information about what the solver is currently doing\&.
-.RE
-.PP
-\fBSOLV_DEBUG_TRANSACTION\fR
-.RS 4
-Used to report information about the transaction generation and ordering process\&.
-.RE
-.PP
-\fBSOLV_DEBUG_TO_STDERR\fR
-.RS 4
-Write debug messages to stderr instead of stdout\&.
-.RE
-.SS "Functions"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_debug(Pool *\fR\fIpool\fR\fB, int\fR \fItype\fR\fB, const char *\fR\fIformat\fR\fB, \&.\&.\&.)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Report a message of the type \fItype\fR\&. You can filter debug messages by setting a debug mask\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_setdebuglevel(Pool *\fR\fIpool\fR\fB, int\fR \fIlevel\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set a predefined debug mask\&. A higher level generally means more bits in the mask are set, thus more messages are printed\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_setdebugmask(Pool *\fR\fIpool\fR\fB, int\fR \fImask\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the debug mask to filter debug messages\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_error(Pool *\fR\fIpool\fR\fB, int\fR \fIret\fR\fB, const char *\fR\fIformat\fR\fB, \&.\&.\&.)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the pool\(cqs error string\&. The \fIret\fR value is simply used as a return value of the function so that you can write code like return pool_error(\&...);\&. If the debug mask contains the \fBSOLV_ERROR\fR bit, pool_debug() is also called with the message and type \fBSOLV_ERROR\fR\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBextern char *pool_errstr(Pool *\fR\fIpool\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the current error string stored in the pool\&. Like with the libc\(cqs errno value, the string is only meaningful after a function returned an error\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_setdebugcallback(Pool *\fR\fIpool\fR\fB, void (*\fR\fIdebugcallback\fR\fB)(Pool *, void *\fR\fIdata\fR\fB, int\fR \fItype\fR\fB, const char *\fR\fIstr\fR\fB), void *\fR\fIdebugcallbackdata\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set a custom debug callback function\&. Instead of writing to stdout or stderr, the callback function will be called\&.
-.SH "POOL CONFIGURATION"
-.SS "Constants"
-.PP
-\fBDISTTYPE_RPM\fR
-.RS 4
-Used for systems which use rpm as low level package manager\&.
-.RE
-.PP
-\fBDISTTYPE_DEB\fR
-.RS 4
-Used for systems which use dpkg as low level package manager\&.
-.RE
-.PP
-\fBDISTTYPE_ARCH\fR
-.RS 4
-Used for systems which use the arch linux package manager\&.
-.RE
-.PP
-\fBDISTTYPE_HAIKU\fR
-.RS 4
-Used for systems which use haiku packages\&.
-.RE
-.PP
-\fBPOOL_FLAG_PROMOTEEPOCH\fR
-.RS 4
-Promote the epoch of the providing dependency to the requesting dependency if it does not contain an epoch\&. Used at some time in old rpm versions, modern systems should never need this\&.
-.RE
-.PP
-\fBPOOL_FLAG_FORBIDSELFCONFLICTS\fR
-.RS 4
-Disallow the installation of packages that conflict with themselves\&. Debian always allows self\-conflicting packages, rpm used to forbid them but switched to also allowing them recently\&.
-.RE
-.PP
-\fBPOOL_FLAG_OBSOLETEUSESPROVIDES\fR
-.RS 4
-Make obsolete type dependency match against provides instead of just the name and version of packages\&. Very old versions of rpm used the name/version, then it got switched to provides and later switched back again to just name/version\&.
-.RE
-.PP
-\fBPOOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES\fR
-.RS 4
-An implicit obsoletes is the internal mechanism to remove the old package on an update\&. The default is to remove all packages with the same name, rpm\-5 switched to also removing packages providing the same name\&.
-.RE
-.PP
-\fBPOOL_FLAG_OBSOLETEUSESCOLORS\fR
-.RS 4
-Rpm\(cqs multilib implementation (used in RedHat and Fedora) distinguishes between 32bit and 64bit packages (the terminology is that they have a different color)\&. If obsoleteusescolors is set, packages with different colors will not obsolete each other\&.
-.RE
-.PP
-\fBPOOL_FLAG_IMPLICITOBSOLETEUSESCOLORS\fR
-.RS 4
-Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if packages of the same name can be installed in parallel\&. For current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true (this is the default if FEDORA is defined when libsolv is compiled)\&.
-.RE
-.PP
-\fBPOOL_FLAG_NOINSTALLEDOBSOLETES\fR
-.RS 4
-New versions of rpm consider the obsoletes of installed packages when checking for dependency, thus you may not install a package that is obsoleted by some other installed package, unless you also erase the other package\&.
-.RE
-.PP
-\fBPOOL_FLAG_HAVEDISTEPOCH\fR
-.RS 4
-Mandriva added a new field called distepoch that gets checked in version comparison if the epoch/version/release of two packages are the same\&.
-.RE
-.PP
-\fBPOOL_FLAG_NOOBSOLETESMULTIVERSION\fR
-.RS 4
-If a package is installed in multiversionmode, rpm used to ignore both the implicit obsoletes and the obsolete dependency of a package\&. This was changed to ignoring just the implicit obsoletes, thus you may install multiple versions of the same name, but obsoleted packages still get removed\&.
-.RE
-.PP
-\fBPOOL_FLAG_ADDFILEPROVIDESFILTERED\fR
-.RS 4
-Make the addfileprovides method only add files from the standard locations (i\&.e\&. the \(lqbin\(rq and \(lqetc\(rq directories)\&. This is useful if you have only few packages that use non\-standard file dependencies, but you still want the fast speed that addfileprovides() generates\&.
-.RE
-.SS "Functions"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_setdisttype(Pool *\fR\fIpool\fR\fB, int\fR \fIdisttype\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the package type of your system\&. The disttype is used for example to define package comparison semantics\&. Libsolv\(cqs default disttype should match the package manager of your system, so you only need to use this function if you want to use the library to solve packaging problems for different systems\&. The Function returns the old disttype on success, and \-1 if the new disttype is not supported\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_set_flag(Pool *\fR\fIpool\fR\fB, int\fR \fIflag\fR\fB, int\fR \fIvalue\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set a flag to a new value\&. Returns the old value of the flag\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_get_flag(Pool *\fR\fIpool\fR\fB, int\fR \fIflag\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Get the value of a pool flag\&. See the constants section about the meaning of the flags\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_set_rootdir(Pool *\fR\fIpool\fR\fB, const char *\fR\fIrootdir\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set a specific root directory\&. Some library functions support a flag that tells the function to prepend the rootdir to file and directory names\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_get_rootdir(Pool *\fR\fIpool\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the current value of the root directory\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *pool_prepend_rootdir(Pool *\fR\fIpool\fR\fB, const char *\fR\fIdir\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Prepend the root directory to the \fIdir\fR argument string\&. The returned string has been newly allocated and needs to be freed after use\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *pool_prepend_rootdir_tmp(Pool *\fR\fIpool\fR\fB, const char *\fR\fIdir\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Same as pool_prepend_rootdir, but uses the pool\(cqs temporary space for allocation\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_set_installed(Pool *\fR\fIpool\fR\fB, Repo *\fR\fIrepo\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set which repository should be treated as the \(lqinstalled\(rq repository, i\&.e\&. the one that holds information about the installed packages\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_set_languages(Pool *\fR\fIpool\fR\fB, const char **\fR\fIlanguages\fR\fB, int\fR \fInlanguages\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the language of your system\&. The library provides lookup functions that return localized strings, for example for package descriptions\&. You can set an array of languages to provide a fallback mechanism if one language is not available\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_setarch(Pool *\fR\fIpool\fR\fB, const char *\fR\fIarch\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the architecture of your system\&. The architecture is used to determine which packages are installable and which packages cannot be installed\&. The \fIarch\fR argument is normally the \(lqmachine\(rq value of the \(lquname\(rq system call\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_setarchpolicy(Pool *, const char *)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set the architecture policy for your system\&. This is the general version of pool_setarch (in fact pool_setarch calls pool_setarchpolicy internally)\&. See the section about architecture policies for more information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_addvendorclass(Pool *\fR\fIpool\fR\fB, const char **\fR\fIvendorclass\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a new vendor equivalence class to the system\&. A vendor equivalence class defines if an installed package of one vendor can be replaced by a package coming from a different vendor\&. The \fIvendorclass\fR argument must be a NULL terminated array of strings\&. See the section about vendor policies for more information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_setvendorclasses(Pool *\fR\fIpool\fR\fB, const char **\fR\fIvendorclasses\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Set all allowed vendor equivalences\&. The vendorclasses argument must be an NULL terminated array consisting of all allowed classes concatenated\&. Each class itself must be NULL terminated, thus the last class ends with two NULL elements, one to finish the class and one to finish the list of classes\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_set_custom_vendorcheck(Pool *\fR\fIpool\fR\fB, int (*\fR\fIvendorcheck\fR\fB)(Pool *, Solvable *, Solvable *))\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Define a custom vendor check mechanism\&. You can use this if libsolv\(cqs internal vendor equivalence class mechanism does not match your needs\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_setloadcallback(Pool *\fR\fIpool\fR\fB, int (*\fR\fIcb\fR\fB)(Pool *, Repodata *, void *), void *\fR\fIloadcbdata\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Define a callback function that gets called when repository metadata needs to be loaded on demand\&. See the section about on demand loading in the libsolv\-repodata manual\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_setnamespacecallback(Pool *\fR\fIpool\fR\fB, Id (*\fR\fIcb\fR\fB)(Pool *, void *,\fR \fIId\fR\fB,\fR \fIId\fR\fB), void *\fR\fInscbdata\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Define a callback function to implement custom namespace support\&. See the section about namespace dependencies\&.
-.SH "ID POOL MANAGEMENT"
-.SS "Constants"
-.PP
-\fBID_EMPTY\fR
-.RS 4
-The Id of the empty string, it is always Id 1\&.
-.RE
-.PP
-\fBREL_LT\fR
-.RS 4
-Represents a \(lq<\(rq relation\&.
-.RE
-.PP
-\fBREL_EQ\fR
-.RS 4
-Represents a \(lq=\(rq relation\&.
-.RE
-.PP
-\fBREL_GT\fR
-.RS 4
-Represents a \(lq>\(rq relation\&. You can use combinations of REL_GT, REL_EQ, and REL_LT or\-ed together to create any relation you like\&.
-.RE
-.PP
-\fBREL_AND\fR
-.RS 4
-A boolean AND operation, the \(lqname\(rq and \(lqevr\(rq parts of the relation can be two sub\-dependencies\&. Packages must match both parts of the dependency\&.
-.RE
-.PP
-\fBREL_OR\fR
-.RS 4
-A boolean OR operation, the \(lqname\(rq and \(lqevr\(rq parts of the relation can be two sub\-dependencies\&. Packages can match any part of the dependency\&.
-.RE
-.PP
-\fBREL_WITH\fR
-.RS 4
-Like REL_AND, but packages must match both dependencies simultaneously\&. See the section about boolean dependencies about more information\&.
-.RE
-.PP
-\fBREL_NAMESPACE\fR
-.RS 4
-A special namespace relation\&. See the section about namespace dependencies for more information\&.
-.RE
-.PP
-\fBREL_ARCH\fR
-.RS 4
-An architecture filter dependency\&. The \(lqname\(rq part of the relation is a sub\-dependency, the \(lqevr\(rq part is the Id of an architecture that the matching packages must have (note that this is an exact match ignoring architecture policies)\&.
-.RE
-.PP
-\fBREL_FILECONFLICT\fR
-.RS 4
-An internal file conflict dependency used to represent file conflicts\&. See the pool_add_fileconflicts_deps() function\&.
-.RE
-.PP
-\fBREL_COND\fR
-.RS 4
-A conditional dependency, the \(lqname\(rq sub\-dependency is only considered if the \(lqevr\(rq sub\-dependency is fulfilled\&. See the section about boolean dependencies about more information\&.
-.RE
-.PP
-\fBREL_COMPAT\fR
-.RS 4
-A compat dependency used in Haiku to represent version ranges\&. The \(lqname\(rq part is the actual version, the \(lqevr\(rq part is the backwards compatibility version\&.
-.RE
-.SS "Functions"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId pool_str2id(Pool *\fR\fIpool\fR\fB, const char *\fR\fIstr\fR\fB, int\fR \fIcreate\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add a string to the pool of unified strings, returning the Id of the string\&. If \fIcreate\fR is zero, new strings will not be added to the pool, instead Id 0 is returned\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId pool_strn2id(Pool *\fR\fIpool\fR\fB, const char *\fR\fIstr\fR\fB, unsigned int\fR \fIlen\fR\fB, int\fR \fIcreate\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Same as pool_str2id, but only \fIlen\fR characters of the string are used\&. This can be used to add substrings to the pool\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId pool_rel2id(Pool *\fR\fIpool\fR\fB, Id\fR \fIname\fR\fB, Id\fR \fIevr\fR\fB, int\fR \fIflags\fR\fB, int\fR \fIcreate\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create a relational dependency from to other dependencies, \fIname\fR and \fIevr\fR, and a \fIflag\fR\&. See the \fBREL_\fR constants for the supported flags\&. As with pool_str2id, \fIcreate\fR defines if new dependencies will get added or Id zero will be returned instead\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId pool_id2langid(Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB, const char *\fR\fIlang\fR\fB, int\fR \fIcreate\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Attach a language suffix to a string Id\&. This function can be used to create language keyname Ids from keynames, it is functional equivalent to converting the \fIid\fR argument to a string, adding a \(lq:\(rq character and the \fIlang\fR argument to the string and then converting the result back into an Id\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_id2str(const Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert an Id back into a string\&. If the Id is a relational Id, the \(lqname\(rq part will be converted instead\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_id2rel(const Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the relation string of a relational Id\&. Returns an empty string if the passed Id is not a relation\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_id2evr(const Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the \(lqevr\(rq part of a relational Id as string\&. Returns an empty string if the passed Id is not a relation\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_dep2str(Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert an Id back into a string\&. If the passed Id belongs to a relation, a string representing the relation is returned\&. Note that in that case the string is allocated on the pool\(cqs temporary space\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_freeidhashes(Pool *\fR\fIpool\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Free the hashes used to unify strings and relations\&. You can use this function to save memory if you know that you will no longer create new strings and relations\&.
-.SH "SOLVABLE FUNCTIONS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBSolvable *pool_id2solvable(const Pool *\fR\fIpool\fR\fB, Id\fR \fIp\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert a solvable Id into a pointer to the solvable data\&. Note that the pointer may become invalid if new solvables are created or old solvables deleted, because the array storing all solvables may get reallocated\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_solvid2str(Pool *\fR\fIpool\fR\fB, Id\fR \fIp\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a string representing the solvable with the Id \fIp\fR\&. The string will be some canonical representation of the solvable, usually a combination of the name, the version, and the architecture\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_solvable2str(Pool *\fR\fIpool\fR\fB, Solvable *\fR\fIs\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Same as pool_solvid2str, but instead of the Id, a pointer to the solvable is passed\&.
-.SH "DEPENDENCY MATCHING"
-.SS "Constants"
-.PP
-\fBEVRCMP_COMPARE\fR
-.RS 4
-Compare all parts of the version, treat missing parts as empty strings\&.
-.RE
-.PP
-\fBEVRCMP_MATCH_RELEASE\fR
-.RS 4
-A special mode for rpm version string matching\&. If a version misses a release part, it matches all releases\&. In that case the special values \(lq\-2\(rq and \(lq2\(rq are returned, depending on which of the two versions did not have a release part\&.
-.RE
-.PP
-\fBEVRCMP_MATCH\fR
-.RS 4
-A generic match, missing parts always match\&.
-.RE
-.PP
-\fBEVRCMP_COMPARE_EVONLY\fR
-.RS 4
-Only compare the epoch and the version parts, ignore the release part\&.
-.RE
-.SS "Functions"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_evrcmp(const Pool *\fR\fIpool\fR\fB, Id\fR \fIevr1id\fR\fB, Id\fR \fIevr2id\fR\fB, int\fR \fImode\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Compare two version Ids, return \-1 if the first version is less than the second version, 0 if they are identical, and 1 if the first version is bigger than the second one\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_evrcmp_str(const Pool *\fR\fIpool\fR\fB, const char *\fR\fIevr1\fR\fB, const char *\fR\fIevr2\fR\fB, int\fR \fImode\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Same as pool_evrcmp(), but uses strings instead of Ids\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_evrmatch(const Pool *\fR\fIpool\fR\fB, Id\fR \fIevrid\fR\fB, const char *\fR\fIepoch\fR\fB, const char *\fR\fIversion\fR\fB, const char *\fR\fIrelease\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Match a version Id against an epoch, a version and a release string\&. Passing NULL means that the part should match everything\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_match_dep(Pool *\fR\fIpool\fR\fB, Id\fR \fId1\fR\fB, Id\fR \fId2\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Returns \(lq1\(rq if the dependency \fId1\fR (the provider) is matched by the dependency \fId2\fR, otherwise \(lq0\(rq is returned\&. For two dependencies to match, both the \(lqname\(rq parts must match and the version range described by the \(lqevr\(rq parts must overlap\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_match_nevr(Pool *\fR\fIpool\fR\fB, Solvable *\fR\fIs\fR\fB, Id\fR \fId\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Like pool_match_dep, but the provider is the "self\-provides" dependency of the Solvable \fIs\fR, i\&.e\&. the dependency \(lqs→name = s→evr\(rq\&.
-.SH "WHATPROVIDES INDEX"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_createwhatprovides(Pool *\fR\fIpool\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Create an index that maps dependency Ids to sets of packages that provide the dependency\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_freewhatprovides(Pool *\fR\fIpool\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Free the whatprovides index to save memory\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId pool_whatprovides(Pool *\fR\fIpool\fR\fB, Id\fR \fId\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return an offset into the Pool\(cqs whatprovidesdata array\&. The solvables with the Ids stored starting at that offset provide the dependency \fId\fR\&. The solvable list is zero terminated\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId *pool_whatprovides_ptr(Pool *\fR\fIpool\fR\fB, Id\fR \fId\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Instead of returning the offset, return the pointer to the Ids stored at that offset\&. Note that this pointer has a very limit validity time, as any call that adds new values to the whatprovidesdata area may reallocate the array\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId pool_queuetowhatprovides(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIq\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Add the contents of the Queue \fIq\fR to the end of the whatprovidesdata array, returning the offset into the array\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_addfileprovides(Pool *\fR\fIpool\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Some package managers like rpm allow dependencies on files contained in other packages\&. To allow libsolv to deal with those dependencies in an efficient way, you need to call the addfileprovides method after creating and reading all repositories\&. This method will scan all dependency for file names and then scan all packages for matching files\&. If a filename has been matched, it will be added to the provides list of the corresponding package\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_addfileprovides_queue(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIidq\fR\fB, Queue *\fR\fIidqinst\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Same as pool_addfileprovides, but the added Ids are returned in two Queues, \fIidq\fR for all repositories except the one containing the \(lqinstalled\(rq packages, \fIidqinst\fR for the latter one\&. This information can be stored in the meta section of the repositories to speed up the next time the repository is loaded and addfileprovides is called
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_flush_namespaceproviders(Pool *\fR\fIpool\fR\fB, Id\fR \fIns\fR\fB, Id\fR \fIevr\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Clear the cache of the providers for namespace dependencies matching namespace \fIns\fR\&. If the \fIevr\fR argument is non\-zero, the namespace dependency for exactly that dependency is cleared, otherwise all matching namespace dependencies are cleared\&. See the section about Namespace dependencies for further information\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_add_fileconflicts_deps(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIconflicts\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Some package managers like rpm report conflicts when a package installation overwrites a file of another installed package with different content\&. As file content information is not stored in the repository metadata, those conflicts can only be detected after the packages are downloaded\&. Libsolv provides a function to check for such conflicts, pool_findfileconflicts()\&. If conflicts are found, they can be added as special \fBREL_FILECONFLICT\fR provides dependencies, so that the solver will know about the conflict when it is re\-run\&.
-.SH "UTILITY FUNCTIONS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *pool_alloctmpspace(Pool *\fR\fIpool\fR\fB, int\fR \fIlen\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Allocate space on the pool\(cqs temporary space area\&. This space has a limited lifetime, it will be automatically freed after a fixed amount (currently 16) of other pool_alloctmpspace() calls are done\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_freetmpspace(Pool *\fR\fIpool\fR\fB, const char *\fR\fIspace\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Give the space allocated with pool_alloctmpspace back to the system\&. You do not have to use this function, as the space is automatically reclaimed, but it can be useful to extend the lifetime of other pointers to the pool\(cqs temporary space area\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_bin2hex(Pool *\fR\fIpool\fR\fB, const unsigned char *\fR\fIbuf\fR\fB, int\fR \fIlen\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert some binary data to hexadecimal, returning a string allocated in the pool\(cqs temporary space area\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *pool_tmpjoin(Pool *\fR\fIpool\fR\fB, const char *\fR\fIstr1\fR\fB, const char *\fR\fIstr2\fR\fB, const char *\fR\fIstr3\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Join three strings and return the result in the pool\(cqs temporary space area\&. You can use NULL arguments if you just want to join less strings\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBchar *pool_tmpappend(Pool *\fR\fIpool\fR\fB, const char *\fR\fIstr1\fR\fB, const char *\fR\fIstr2\fR\fB, const char *\fR\fIstr3\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Like pool_tmpjoin(), but if the first argument is the last allocated space in the pool\(cqs temporary space area, it will be replaced with the result of the join and no new temporary space slot will be used\&. Thus you can join more than three strings by a combination of one pool_tmpjoin() and multiple pool_tmpappend() calls\&. Note that the \fIstr1\fR pointer is no longer usable after the call\&.
-.SH "DATA LOOKUP"
-.SS "Constants"
-.PP
-\fBSOLVID_POS\fR
-.RS 4
-Use the data position stored in the pool for the lookup instead of looking up the data of a solvable\&.
-.RE
-.PP
-\fBSOLVID_META\fR
-.RS 4
-Use the data stored in the meta section of a repository (or repodata area) instead of looking up the data of a solvable\&. This constant does not work for the pool\(cqs lookup functions, use it for the repo\(cqs or repodata\(cqs lookup functions instead\&. It\(cqs just listed for completeness\&.
-.RE
-.SS "Functions"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_lookup_str(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the string value stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBunsigned long long pool_lookup_num(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInotfound\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the 64bit unsigned number stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&. If no such number is found, the value of the \fInotfound\fR argument is returned instead\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBId pool_lookup_id(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the Id stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_lookup_idarray(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Queue *\fR\fIq\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Fill the queue \fIq\fR with the content of the Id array stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&. Returns \(lq1\(rq if an array was found, otherwise the queue will be empty and \(lq0\(rq will be returned\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_lookup_void(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Returns \(lq1\(rq if a void value is stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR, otherwise \(lq0\(rq\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_lookup_checksum(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id *\fR\fItypep\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the checksum that is stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&. The type of the checksum will be returned over the \fItypep\fR pointer\&. If no such checksum is found, NULL will be returned and the type will be set to zero\&. Note that the result is stored in the Pool\(cqs temporary space area\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst unsigned char *pool_lookup_bin_checksum(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id *\fR\fItypep\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return the checksum that is stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&. Returns the checksum as binary data, you can use the returned type to calculate the length of the checksum\&. No temporary space area is needed\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_lookup_deltalocation(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, unsigned int *\fR\fImedianrp\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-This is a utility lookup function to return the delta location for a delta rpm\&. As solvables cannot store deltas, you have to use SOLVID_POS as argument and set the Pool\(cqs datapos pointer to point to valid delta rpm data\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_search(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR\fB, int\fR \fIflags\fR\fB, int (*\fR\fIcallback\fR\fB)(void *\fR\fIcbdata\fR\fB, Solvable *\fR\fIs\fR\fB, Repodata *\fR\fIdata\fR\fB, Repokey *\fR\fIkey\fR\fB, KeyValue *\fR\fIkv\fR\fB), void *\fR\fIcbdata\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Perform a search on all data stored in the pool\&. You can limit the search area by using the \fIsolvid\fR and \fIkeyname\fR arguments\&. The values can be optionally matched against the \fImatch\fR argument, use NULL if you do not want this matching\&. See the Dataiterator manpage about the possible matches modes and the \fIflags\fR argument\&. For all (matching) values, the callback function is called with the \fIcbdata\fR callback argument and the data describing the value\&.
-.SH "JOB AND SELECTION FUNCTIONS"
-.sp
-A Job consists of two Ids, \fIhow\fR and \fIwhat\fR\&. The \fIhow\fR part describes the action, the job flags, and the selection method while the \fIwhat\fR part is in input for the selection\&. A Selection is a queue consisting of multiple jobs (thus the number of elements in the queue must be a multiple of two)\&. See the Solver manpage for more information about jobs\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_job2str(Pool *\fR\fIpool\fR\fB, Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB, Id\fR \fIflagmask\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert a job into a string\&. Useful for debugging purposes\&. The \fIflagmask\fR can be used to mask the flags of the job, use \(lq0\(rq if you do not want to see such flags, \(lq\-1\(rq to see all flags, or a combination of the flags you want to see\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_job2solvables(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIpkgs\fR\fB, Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return a list of solvables that the specified job selects\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBint pool_isemptyupdatejob(Pool *\fR\fIpool\fR\fB, Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Return \(lq1\(rq if the job is an update job that does not work with any installed package, i\&.e\&. the job is basically a no\-op\&. You can use this to turn no\-op update jobs into install jobs (as done by package managers like \(lqzypper\(rq)\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBconst char *pool_selection2str(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIselection\fR\fB, Id\fR \fIflagmask\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Convert a selection into a string\&. Useful for debugging purposes\&. See the pool_job2str() function for the \fIflagmask\fR argument\&.
-.SH "ODDS AND ENDS"
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_freeallrepos(Pool *\fR\fIpool\fR\fB, int\fR \fIreuseids\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Free all repos from the pool (including all solvables)\&. If \fIreuseids\fR is true, all Ids of the solvables are free to be reused the next time solvables are created\&.
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-\fBvoid pool_clear_pos(Pool *\fR\fIpool\fR\fB)\fR;
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-Clear the data position stored in the pool\&.
-.SH "ARCHITECTURE POLICIES"
-.sp
-An architecture policy defines a list of architectures that can be installed on the system, and also the relationship between them (i\&.e\&. the ordering)\&. Architectures can be delimited with three different characters:
-.PP
-\fB\*(Aq:\*(Aq\fR
-.RS 4
-No relationship between the architectures\&. A package of one architecture can not be replaced with one of the other architecture\&.
-.RE
-.PP
-\fB\*(Aq>\*(Aq\fR
-.RS 4
-The first architecture is better than the second one\&. An installed package of the second architecture may be replaced with one from the first architecture and vice versa\&. The solver will select the better architecture if the versions are the same\&.
-.RE
-.PP
-\fB\*(Aq=\*(Aq\fR
-.RS 4
-The two architectures are freely exchangeable\&. Used to define aliases for architectures\&.
-.RE
-.sp
-An example would be \*(Aqx86_64:i686=athlon>i586\*(Aq\&. This means that x86_64 packages can only be replaced by other x86_64 packages, i686 packages can be replaced by i686 and i586 packages (but i686 packages will be preferred) and athlon is another name for the i686 architecture\&.
-.sp
-You can turn off the architecture replacement checks with the Solver\(cqs SOLVER_FLAG_ALLOW_ARCHCHANGE flag\&.
-.SH "VENDOR POLICIES"
-.sp
-Different vendors often compile packages with different features, so Libsolv only replace installed packages of one vendor with packages coming from the same vendor\&. Also, while the version of a package is normally defined by the upstream project, the release part of the version is set by the vendor\(cqs package maintainer, so it\(cqs not meaningful to do version comparisons for packages coming from different vendors\&.
-.sp
-Vendor in this case means the SOLVABLE_VENDOR string stored in each solvable\&. Sometimes a vendor changes names, or multiple vendors form a group that coordinate their package building, so libsolv offers a way to define that a group of vendors are compatible\&. You do that be defining vendor equivalence classes, packages from a vendor from one class may be replaced with packages from all the other vendors in the class\&.
-.sp
-There can be multiple equivalence classes, the set of allowed vendor changes for an installed package is calculated by building the union of all of the equivalence classes the vendor of the installed package is part of\&.
-.sp
-You can turn off the architecture replacement checks with the Solver\(cqs SOLVER_FLAG_ALLOW_VENDORCHANGE flag\&.
-.SH "BOOLEAN DEPENDENCIES"
-.sp
-Boolean Dependencies allow to build complex expressions from simple dependencies\&. While rpm does not support boolean expressions in dependencies and debian only allows an "OR" expression, libsolv allows arbitrary complex expressions\&. The following basic types are supported:
-.PP
-\fBREL_OR\fR
-.RS 4
-The expression is true if either the first dependency or the second one is true\&. This is useful for package dependencies like \(lqRequires\(rq, where you can specify that either one of the packages need to be installed\&.
-.RE
-.PP
-\fBREL_AND\fR
-.RS 4
-The expression is true if both dependencies are true\&. The packages fulfilling the dependencies may be different, i\&.e\&. \(lqSupplements: perl AND python\(rq is true if both a package providing perl and a package providing python are installed\&. The solver currently only supports REL_AND in Supplements/Enhances dependencies, in other types of dependencies it gets treated as REL_WITH\&.
-.RE
-.PP
-\fBREL_WITH\fR
-.RS 4
-The expression is true if both dependencies are true and are fulfilled by the same package\&. Thus \(lqSupplements: perl AND python\(rq would only be true if a package is installed that provides both dependencies (some kind of multi\-language interpreter)\&.
-.RE
-.PP
-\fBREL_COND\fR
-.RS 4
-The expression is true if the first dependency is true or the second dependency is false\&. Libsolv currently does not support this type of dependency in the solver code\&.
-.RE
-.sp
-Each sub\-dependency of a boolean dependency can in turn be a boolean dependency, so you can chain them to create complex dependencies\&.
-.SH "NAMESPACE DEPENDENCIES"
-.sp
-Namespace dependencies can be used to implement dependencies on attributes external to libsolv\&. An example would be a dependency on the language set by the user\&. This types of dependencies are usually only used for \(lqConflicts\(rq or \(lqSupplements\(rq dependencies, as the underlying package manager does not know how to deal with them\&.
-.sp
-If the library needs to evaluate a namespace dependency, it calls the namespace callback function set in the pool\&. The callback function can return a set of packages that \(lqprovide\(rq the dependency\&. If the dependency is provided by the system, the returned set should consist of just the system solvable (Solvable Id 1)\&.
-.sp
-The returned set of packages must be returned as offset into the whatprovidesdata array\&. You can use the pool_queuetowhatprovides function to convert a queue into such an offset\&. To ease programming the callback function, the return values \(lq0\(rq and \(lq1\(rq are not interpreted as an offset\&. \(lq0\(rq means that no package is in the return set, \(lq1\(rq means that just the system solvable is in the set\&.
-.sp
-The returned set is cached, so that for each namespace dependency the callback is just called once\&. If you need to flush the cache (maybe because the user has selected a different language), use the pool_flush_namespaceproviders() function\&.
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-Libsolv-Pool(3)
-===============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-libsolv-pool - Libsolv's pool object
-
-
-Public Attributes
------------------
-
-*void *appdata*::
-A no-purpose pointer free to use for the library user. Freeing the pool
-simply discards the pointer.
-
-*Stringpool ss*::
-The pool of unified strings.
-
-*Reldep *rels*::
-The pool of unified relation dependencies.
-
-*int nrels*::
-Number of allocated relation dependencies.
-
-*Repo **repos*::
-The array of repository pointers, indexed by repository Id.
-
-*int nrepos*::
-Number of allocated repository array elements, i.e. the size
-of the repos array.
-
-*int urepos*::
-Number of used (i.e. non-zero) repository array elements.
-
-*Repo *installed*::
-Pointer to the repo holding the installed packages. You are free to read
-this attribute, but you should use pool_set_installed() if you want to
-change it.
-
-*Solvable *solvables*::
-The array of Solvable objects.
-
-*int nsolvables*::
-Number of Solvable objects, i.e. the size of the solvables array. Note
-that the array may contain freed solvables, in that case the repo pointer
-of the solvable will be zero.
-
-*int disttype*::
-The distribution type of your system, e.g. DISTTYPE_DEB. You are free to
-read this attribute, but you should use pool_setdisttype() if you want to
-change it.
-
-*Id *whatprovidesdata*::
-Multi-purpose Id storage holding zero terminated arrays of Ids.
-pool_whatprovides() returns an offset into this data.
-
-*Map *considered*::
-Optional bitmap that can make the library ignore solvables. If a bitmap is
-set, only solvables that have a set bit in the bitmap at their Id are
-considered usable.
-
-*int debugmask*::
-A mask that defines which debug events should be reported.
-pool_setdebuglevel() sets this mask.
-
-*Datapos pos*::
-An object storing some position in the repository data. Functions like
-dataiterator_set_pos() set this object, accessing data with a pseudo
-solvable Id of SOLVID_POS uses it.
-
-*Queue pooljobs*::
-A queue where fixed solver jobs can be stored. This jobs are automatically
-added when solver_solve() is called, they are useful to store configuration
-data like which packages should be multiversion installed.
-
-Creation and Destruction
-------------------------
-
- Pool *pool_create();
-
-Create a new instance of a pool.
-
- void pool_free(Pool *pool);
-
-Free a pool and all of the data it contains, e.g. the solvables,
-repositories, strings.
-
-
-Debugging and error reporting
------------------------------
-
-=== Constants ===
-
-*SOLV_FATAL*::
-Report the error and call ``exit(1)'' afterwards. You cannot mask this
-level. Reports to stderr instead of stdout.
-
-*SOLV_ERROR*::
-Used to report errors. Reports to stderr instead of stdout.
-
-*SOLV_WARN*::
-Used to report warnings.
-
-*SOLV_DEBUG_STATS*::
-Used to report statistical data.
-
-*SOLV_DEBUG_RULE_CREATION*::
-Used to report information about the solver's creation of rules.
-
-*SOLV_DEBUG_PROPAGATE*::
-Used to report information about the solver's unit rule propagation
-process.
-
-*SOLV_DEBUG_ANALYZE*::
-Used to report information about the solver's learnt rule generation
-mechanism.
-
-*SOLV_DEBUG_UNSOLVABLE*::
-Used to report information about the solver dealing with conflicting
-rules.
-
-*SOLV_DEBUG_SOLUTIONS*::
-Used to report information about the solver creating solutions to solve
-problems.
-
-*SOLV_DEBUG_POLICY*::
-Used to report information about the solver searching for an optimal
-solution.
-
-*SOLV_DEBUG_RESULT*::
-Used by the debug functions to output results.
-
-*SOLV_DEBUG_JOB*::
-Used to report information about the job rule generation process.
-
-*SOLV_DEBUG_SOLVER*::
-Used to report information about what the solver is currently
-doing.
-
-*SOLV_DEBUG_TRANSACTION*::
-Used to report information about the transaction generation and
-ordering process.
-
-*SOLV_DEBUG_TO_STDERR*::
-Write debug messages to stderr instead of stdout.
-
-=== Functions ===
-
- void pool_debug(Pool *pool, int type, const char *format, ...);
-
-Report a message of the type _type_. You can filter debug messages by
-setting a debug mask.
-
- void pool_setdebuglevel(Pool *pool, int level);
-
-Set a predefined debug mask. A higher level generally means more bits in
-the mask are set, thus more messages are printed.
-
- void pool_setdebugmask(Pool *pool, int mask);
-
-Set the debug mask to filter debug messages.
-
- int pool_error(Pool *pool, int ret, const char *format, ...);
-
-Set the pool's error string. The _ret_ value is simply used as a
-return value of the function so that you can write code like
-+return pool_error(...);+. If the debug mask contains the *SOLV_ERROR*
-bit, pool_debug() is also called with the message and type *SOLV_ERROR*.
-
- extern char *pool_errstr(Pool *pool);
-
-Return the current error string stored in the pool. Like with the libc's
-errno value, the string is only meaningful after a function returned an
-error.
-
- void pool_setdebugcallback(Pool *pool, void (*debugcallback)(Pool *, void *data, int type, const char *str), void *debugcallbackdata);
-
-Set a custom debug callback function. Instead of writing to stdout or
-stderr, the callback function will be called.
-
-
-Pool configuration
-------------------
-
-=== Constants ===
-
-*DISTTYPE_RPM*::
-Used for systems which use rpm as low level package manager.
-
-*DISTTYPE_DEB*::
-Used for systems which use dpkg as low level package manager.
-
-*DISTTYPE_ARCH*::
-Used for systems which use the arch linux package manager.
-
-*DISTTYPE_HAIKU*::
-Used for systems which use haiku packages.
-
-*POOL_FLAG_PROMOTEEPOCH*::
-Promote the epoch of the providing dependency to the requesting
-dependency if it does not contain an epoch. Used at some time
-in old rpm versions, modern systems should never need this.
-
-*POOL_FLAG_FORBIDSELFCONFLICTS*::
-Disallow the installation of packages that conflict with themselves.
-Debian always allows self-conflicting packages, rpm used to forbid
-them but switched to also allowing them recently.
-
-*POOL_FLAG_OBSOLETEUSESPROVIDES*::
-Make obsolete type dependency match against provides instead of
-just the name and version of packages. Very old versions of rpm
-used the name/version, then it got switched to provides and later
-switched back again to just name/version.
-
-*POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES*::
-An implicit obsoletes is the internal mechanism to remove the
-old package on an update. The default is to remove all packages
-with the same name, rpm-5 switched to also removing packages
-providing the same name.
-
-*POOL_FLAG_OBSOLETEUSESCOLORS*::
-Rpm's multilib implementation (used in RedHat and Fedora)
-distinguishes between 32bit and 64bit packages (the terminology
-is that they have a different color). If obsoleteusescolors is
-set, packages with different colors will not obsolete each other.
-
-*POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS*::
-Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if
-packages of the same name can be installed in parallel. For
-current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be
-false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true
-(this is the default if FEDORA is defined when libsolv is
-compiled).
-
-*POOL_FLAG_NOINSTALLEDOBSOLETES*::
-New versions of rpm consider the obsoletes of installed packages
-when checking for dependency, thus you may not install a package
-that is obsoleted by some other installed package, unless you
-also erase the other package.
-
-*POOL_FLAG_HAVEDISTEPOCH*::
-Mandriva added a new field called distepoch that gets checked in
-version comparison if the epoch/version/release of two packages
-are the same.
-
-*POOL_FLAG_NOOBSOLETESMULTIVERSION*::
-If a package is installed in multiversionmode, rpm used to ignore
-both the implicit obsoletes and the obsolete dependency of a
-package. This was changed to ignoring just the implicit obsoletes,
-thus you may install multiple versions of the same name, but
-obsoleted packages still get removed.
-
-*POOL_FLAG_ADDFILEPROVIDESFILTERED*::
-Make the addfileprovides method only add files from the standard
-locations (i.e. the ``bin'' and ``etc'' directories). This is
-useful if you have only few packages that use non-standard file
-dependencies, but you still want the fast speed that addfileprovides()
-generates.
-
-
-=== Functions ===
- int pool_setdisttype(Pool *pool, int disttype);
-
-Set the package type of your system. The disttype is used for example
-to define package comparison semantics. Libsolv's default disttype
-should match the package manager of your system, so you only need to
-use this function if you want to use the library to solve packaging
-problems for different systems. The Function returns the old
-disttype on success, and -1 if the new disttype is not supported.
-
- int pool_set_flag(Pool *pool, int flag, int value);
-
-Set a flag to a new value. Returns the old value of the flag.
-
- int pool_get_flag(Pool *pool, int flag);
-
-Get the value of a pool flag. See the constants section about the meaning
-of the flags.
-
- void pool_set_rootdir(Pool *pool, const char *rootdir);
-
-Set a specific root directory. Some library functions support a flag that
-tells the function to prepend the rootdir to file and directory names.
-
- const char *pool_get_rootdir(Pool *pool);
-
-Return the current value of the root directory.
-
- char *pool_prepend_rootdir(Pool *pool, const char *dir);
-
-Prepend the root directory to the _dir_ argument string. The returned
-string has been newly allocated and needs to be freed after use.
-
- char *pool_prepend_rootdir_tmp(Pool *pool, const char *dir);
-
-Same as pool_prepend_rootdir, but uses the pool's temporary space for
-allocation.
-
- void pool_set_installed(Pool *pool, Repo *repo);
-
-Set which repository should be treated as the ``installed'' repository,
-i.e. the one that holds information about the installed packages.
-
- void pool_set_languages(Pool *pool, const char **languages, int nlanguages);
-
-Set the language of your system. The library provides lookup functions that
-return localized strings, for example for package descriptions. You can
-set an array of languages to provide a fallback mechanism if one language
-is not available.
-
- void pool_setarch(Pool *pool, const char *arch);
-
-Set the architecture of your system. The architecture is used to determine
-which packages are installable and which packages cannot be installed.
-The _arch_ argument is normally the ``machine'' value of the ``uname''
-system call.
-
- void pool_setarchpolicy(Pool *, const char *);
-
-Set the architecture policy for your system. This is the general version
-of pool_setarch (in fact pool_setarch calls pool_setarchpolicy internally).
-See the section about architecture policies for more information.
-
- void pool_addvendorclass(Pool *pool, const char **vendorclass);
-
-Add a new vendor equivalence class to the system. A vendor equivalence class
-defines if an installed package of one vendor can be replaced by a package
-coming from a different vendor. The _vendorclass_ argument must be a
-NULL terminated array of strings. See the section about vendor policies for
-more information.
-
- void pool_setvendorclasses(Pool *pool, const char **vendorclasses);
-
-Set all allowed vendor equivalences. The vendorclasses argument must be an
-NULL terminated array consisting of all allowed classes concatenated.
-Each class itself must be NULL terminated, thus the last class ends with
-two NULL elements, one to finish the class and one to finish the list
-of classes.
-
- void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(Pool *, Solvable *, Solvable *));
-
-Define a custom vendor check mechanism. You can use this if libsolv's
-internal vendor equivalence class mechanism does not match your needs.
-
- void pool_setloadcallback(Pool *pool, int (*cb)(Pool *, Repodata *, void *), void *loadcbdata);
-
-Define a callback function that gets called when repository metadata needs
-to be loaded on demand. See the section about on demand loading in the
-libsolv-repodata manual.
-
- void pool_setnamespacecallback(Pool *pool, Id (*cb)(Pool *, void *, Id, Id), void *nscbdata);
-
-Define a callback function to implement custom namespace support. See the
-section about namespace dependencies.
-
-
-Id pool management
-------------------
-=== Constants ===
-
-*ID_EMPTY*::
-The Id of the empty string, it is always Id 1.
-
-*REL_LT*::
-Represents a ``<'' relation.
-
-*REL_EQ*::
-Represents a ``='' relation.
-
-*REL_GT*::
-Represents a ``>'' relation. You can use combinations of REL_GT, REL_EQ,
-and REL_LT or-ed together to create any relation you like.
-
-*REL_AND*::
-A boolean AND operation, the ``name'' and ``evr'' parts of the relation can
-be two sub-dependencies. Packages must match both parts of the dependency.
-
-*REL_OR*::
-A boolean OR operation, the ``name'' and ``evr'' parts of the relation can
-be two sub-dependencies. Packages can match any part of the dependency.
-
-*REL_WITH*::
-Like REL_AND, but packages must match both dependencies simultaneously. See
-the section about boolean dependencies about more information.
-
-*REL_NAMESPACE*::
-A special namespace relation. See the section about namespace dependencies
-for more information.
-
-*REL_ARCH*::
-An architecture filter dependency. The ``name'' part of the relation is a
-sub-dependency, the ``evr'' part is the Id of an architecture that the
-matching packages must have (note that this is an exact match ignoring
-architecture policies).
-
-*REL_FILECONFLICT*::
-An internal file conflict dependency used to represent file conflicts. See
-the pool_add_fileconflicts_deps() function.
-
-*REL_COND*::
-A conditional dependency, the ``name'' sub-dependency is only considered if
-the ``evr'' sub-dependency is fulfilled. See the section about boolean
-dependencies about more information.
-
-*REL_COMPAT*::
-A compat dependency used in Haiku to represent version ranges. The
-``name'' part is the actual version, the ``evr'' part is the backwards
-compatibility version.
-
-=== Functions ===
- Id pool_str2id(Pool *pool, const char *str, int create);
-
-Add a string to the pool of unified strings, returning the Id of the string.
-If _create_ is zero, new strings will not be added to the pool, instead
-Id 0 is returned.
-
- Id pool_strn2id(Pool *pool, const char *str, unsigned int len, int create);
-
-Same as pool_str2id, but only _len_ characters of the string are used. This
-can be used to add substrings to the pool.
-
- Id pool_rel2id(Pool *pool, Id name, Id evr, int flags, int create);
-
-Create a relational dependency from to other dependencies, _name_ and _evr_,
-and a _flag_. See the *REL_* constants for the supported flags. As with
-pool_str2id, _create_ defines if new dependencies will get added or Id zero
-will be returned instead.
-
- Id pool_id2langid(Pool *pool, Id id, const char *lang, int create);
-
-Attach a language suffix to a string Id. This function can be used to
-create language keyname Ids from keynames, it is functional equivalent
-to converting the _id_ argument to a string, adding a ``:'' character
-and the _lang_ argument to the string and then converting the result back
-into an Id.
-
- const char *pool_id2str(const Pool *pool, Id id);
-
-Convert an Id back into a string. If the Id is a relational Id, the
-``name'' part will be converted instead.
-
- const char *pool_id2rel(const Pool *pool, Id id);
-
-Return the relation string of a relational Id. Returns an empty string if
-the passed Id is not a relation.
-
- const char *pool_id2evr(const Pool *pool, Id id);
-
-Return the ``evr'' part of a relational Id as string. Returns an empty
-string if the passed Id is not a relation.
-
- const char *pool_dep2str(Pool *pool, Id id);
-
-Convert an Id back into a string. If the passed Id belongs to a relation,
-a string representing the relation is returned. Note that in that case
-the string is allocated on the pool's temporary space.
-
- void pool_freeidhashes(Pool *pool);
-
-Free the hashes used to unify strings and relations. You can use this
-function to save memory if you know that you will no longer create new
-strings and relations.
-
-
-Solvable functions
-------------------
-
- Solvable *pool_id2solvable(const Pool *pool, Id p);
-
-Convert a solvable Id into a pointer to the solvable data. Note that the
-pointer may become invalid if new solvables are created or old solvables
-deleted, because the array storing all solvables may get reallocated.
-
- const char *pool_solvid2str(Pool *pool, Id p);
-
-Return a string representing the solvable with the Id _p_. The string will
-be some canonical representation of the solvable, usually a combination of
-the name, the version, and the architecture.
-
- const char *pool_solvable2str(Pool *pool, Solvable *s);
-
-Same as pool_solvid2str, but instead of the Id, a pointer to the solvable
-is passed.
-
-
-Dependency matching
--------------------
-
-=== Constants ===
-*EVRCMP_COMPARE*::
-Compare all parts of the version, treat missing parts as empty strings.
-
-*EVRCMP_MATCH_RELEASE*::
-A special mode for rpm version string matching. If a version misses a
-release part, it matches all releases. In that case the special values
-``-2'' and ``2'' are returned, depending on which of the two versions
-did not have a release part.
-
-*EVRCMP_MATCH*::
-A generic match, missing parts always match.
-
-*EVRCMP_COMPARE_EVONLY*::
-Only compare the epoch and the version parts, ignore the release part.
-
-=== Functions ===
- int pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode);
-
-Compare two version Ids, return -1 if the first version is less than the
-second version, 0 if they are identical, and 1 if the first version is
-bigger than the second one.
-
- int pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode);
-
-Same as pool_evrcmp(), but uses strings instead of Ids.
-
- int pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release);
-
-Match a version Id against an epoch, a version and a release string. Passing
-NULL means that the part should match everything.
-
- int pool_match_dep(Pool *pool, Id d1, Id d2);
-
-Returns ``1'' if the dependency _d1_ (the provider) is matched by the
-dependency _d2_, otherwise ``0'' is returned. For two dependencies to
-match, both the ``name'' parts must match and the version range described
-by the ``evr'' parts must overlap.
-
- int pool_match_nevr(Pool *pool, Solvable *s, Id d);
-
-Like pool_match_dep, but the provider is the "self-provides" dependency
-of the Solvable _s_, i.e. the dependency ``s->name = s->evr''.
-
-
-Whatprovides Index
-------------------
- void pool_createwhatprovides(Pool *pool);
-
-Create an index that maps dependency Ids to sets of packages that provide the
-dependency.
-
- void pool_freewhatprovides(Pool *pool);
-
-Free the whatprovides index to save memory.
-
- Id pool_whatprovides(Pool *pool, Id d);
-
-Return an offset into the Pool's whatprovidesdata array. The solvables with
-the Ids stored starting at that offset provide the dependency _d_. The
-solvable list is zero terminated.
-
- Id *pool_whatprovides_ptr(Pool *pool, Id d);
-
-Instead of returning the offset, return the pointer to the Ids stored at
-that offset. Note that this pointer has a very limit validity time, as any
-call that adds new values to the whatprovidesdata area may reallocate the
-array.
-
- Id pool_queuetowhatprovides(Pool *pool, Queue *q);
-
-Add the contents of the Queue _q_ to the end of the whatprovidesdata array,
-returning the offset into the array.
-
- void pool_addfileprovides(Pool *pool);
-
-Some package managers like rpm allow dependencies on files contained in
-other packages. To allow libsolv to deal with those dependencies in an
-efficient way, you need to call the addfileprovides method after creating
-and reading all repositories. This method will scan all dependency for file
-names and then scan all packages for matching files. If a filename has been
-matched, it will be added to the provides list of the corresponding
-package.
-
- void pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst);
-
-Same as pool_addfileprovides, but the added Ids are returned in two Queues,
-_idq_ for all repositories except the one containing the ``installed''
-packages, _idqinst_ for the latter one. This information can be stored in
-the meta section of the repositories to speed up the next time the
-repository is loaded and addfileprovides is called
-
- void pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr);
-
-Clear the cache of the providers for namespace dependencies matching
-namespace _ns_. If the _evr_ argument is non-zero, the namespace dependency
-for exactly that dependency is cleared, otherwise all matching namespace
-dependencies are cleared. See the section about Namespace dependencies
-for further information.
-
- void pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts);
-
-Some package managers like rpm report conflicts when a package installation
-overwrites a file of another installed package with different content. As
-file content information is not stored in the repository metadata, those
-conflicts can only be detected after the packages are downloaded. Libsolv
-provides a function to check for such conflicts, pool_findfileconflicts().
-If conflicts are found, they can be added as special *REL_FILECONFLICT*
-provides dependencies, so that the solver will know about the conflict when
-it is re-run.
-
-
-Utility functions
------------------
- char *pool_alloctmpspace(Pool *pool, int len);
-
-Allocate space on the pool's temporary space area. This space has a limited
-lifetime, it will be automatically freed after a fixed amount (currently
-16) of other pool_alloctmpspace() calls are done.
-
- void pool_freetmpspace(Pool *pool, const char *space);
-
-Give the space allocated with pool_alloctmpspace back to the system. You
-do not have to use this function, as the space is automatically reclaimed,
-but it can be useful to extend the lifetime of other pointers to the pool's
-temporary space area.
-
- const char *pool_bin2hex(Pool *pool, const unsigned char *buf, int len);
-
-Convert some binary data to hexadecimal, returning a string allocated in
-the pool's temporary space area.
-
- char *pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const char *str3);
-
-Join three strings and return the result in the pool's temporary space
-area. You can use NULL arguments if you just want to join less strings.
-
- char *pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3);
-
-Like pool_tmpjoin(), but if the first argument is the last allocated space
-in the pool's temporary space area, it will be replaced with the result of
-the join and no new temporary space slot will be used. Thus you can join
-more than three strings by a combination of one pool_tmpjoin() and multiple
-pool_tmpappend() calls. Note that the _str1_ pointer is no longer usable
-after the call.
-
-
-Data lookup
------------
-=== Constants ===
-
-*SOLVID_POS*::
-Use the data position stored in the pool for the lookup instead of looking
-up the data of a solvable.
-
-*SOLVID_META*::
-Use the data stored in the meta section of a repository (or repodata
-area) instead of looking up the data of a solvable. This constant does
-not work for the pool's lookup functions, use it for the repo's or
-repodata's lookup functions instead. It's just listed for completeness.
-
-=== Functions ===
- const char *pool_lookup_str(Pool *pool, Id solvid, Id keyname);
-
-Return the string value stored under the attribute _keyname_ in solvable
-_solvid_.
-
- unsigned long long pool_lookup_num(Pool *pool, Id solvid, Id keyname, unsigned long long notfound);
-
-Return the 64bit unsigned number stored under the attribute _keyname_ in
-solvable _solvid_. If no such number is found, the value of the _notfound_
-argument is returned instead.
-
- Id pool_lookup_id(Pool *pool, Id solvid, Id keyname);
-
-Return the Id stored under the attribute _keyname_ in solvable _solvid_.
-
- int pool_lookup_idarray(Pool *pool, Id solvid, Id keyname, Queue *q);
-
-Fill the queue _q_ with the content of the Id array stored under the
-attribute _keyname_ in solvable _solvid_. Returns ``1'' if an array was
-found, otherwise the queue will be empty and ``0'' will be returned.
-
- int pool_lookup_void(Pool *pool, Id solvid, Id keyname);
-
-Returns ``1'' if a void value is stored under the attribute _keyname_ in
-solvable _solvid_, otherwise ``0''.
-
- const char *pool_lookup_checksum(Pool *pool, Id solvid, Id keyname, Id *typep);
-
-Return the checksum that is stored under the attribute _keyname_ in
-solvable _solvid_. The type of the checksum will be returned over the
-_typep_ pointer. If no such checksum is found, NULL will be returned and
-the type will be set to zero. Note that the result is stored in the Pool's
-temporary space area.
-
- const unsigned char *pool_lookup_bin_checksum(Pool *pool, Id solvid, Id keyname, Id *typep);
-
-Return the checksum that is stored under the attribute _keyname_ in
-solvable _solvid_. Returns the checksum as binary data, you can use the
-returned type to calculate the length of the checksum. No temporary space
-area is needed.
-
- const char *pool_lookup_deltalocation(Pool *pool, Id solvid, unsigned int *medianrp);
-
-This is a utility lookup function to return the delta location for a delta
-rpm. As solvables cannot store deltas, you have to use SOLVID_POS as
-argument and set the Pool's datapos pointer to point to valid delta rpm
-data.
-
- void pool_search(Pool *pool, Id solvid, Id keyname, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata);
-
-Perform a search on all data stored in the pool. You can limit the search
-area by using the _solvid_ and _keyname_ arguments. The values can be
-optionally matched against the _match_ argument, use NULL if you do not
-want this matching. See the Dataiterator manpage about the possible matches
-modes and the _flags_ argument. For all (matching) values, the callback
-function is called with the _cbdata_ callback argument and the data
-describing the value.
-
-
-Job and Selection functions
----------------------------
-A Job consists of two Ids, _how_ and _what_. The _how_ part describes the
-action, the job flags, and the selection method while the _what_ part is
-in input for the selection. A Selection is a queue consisting of multiple
-jobs (thus the number of elements in the queue must be a multiple of two).
-See the Solver manpage for more information about jobs.
-
- const char *pool_job2str(Pool *pool, Id how, Id what, Id flagmask);
-
-Convert a job into a string. Useful for debugging purposes. The _flagmask_
-can be used to mask the flags of the job, use ``0'' if you do not want to
-see such flags, ``-1'' to see all flags, or a combination of the flags
-you want to see.
-
- void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what);
-
-Return a list of solvables that the specified job selects.
-
- int pool_isemptyupdatejob(Pool *pool, Id how, Id what);
-
-Return ``1'' if the job is an update job that does not work with any
-installed package, i.e. the job is basically a no-op. You can use this
-to turn no-op update jobs into install jobs (as done by package managers
-like ``zypper'').
-
- const char *pool_selection2str(Pool *pool, Queue *selection, Id flagmask);
-
-Convert a selection into a string. Useful for debugging purposes. See the
-pool_job2str() function for the _flagmask_ argument.
-
-
-Odds and Ends
--------------
- void pool_freeallrepos(Pool *pool, int reuseids);
-
-Free all repos from the pool (including all solvables). If _reuseids_ is
-true, all Ids of the solvables are free to be reused the next time
-solvables are created.
-
- void pool_clear_pos(Pool *pool);
-
-Clear the data position stored in the pool.
-
-
-Architecture Policies
----------------------
-An architecture policy defines a list of architectures that can be
-installed on the system, and also the relationship between them (i.e. the
-ordering). Architectures can be delimited with three different characters:
-
-*\':'*::
-No relationship between the architectures. A package of one architecture
-can not be replaced with one of the other architecture.
-
-*\'>'*::
-The first architecture is better than the second one. An installed package
-of the second architecture may be replaced with one from the first
-architecture and vice versa. The solver will select the better architecture
-if the versions are the same.
-
-*\'='*::
-The two architectures are freely exchangeable. Used to define aliases
-for architectures.
-
-An example would be \'+x86_64:i686=athlon>i586+'. This means that x86_64
-packages can only be replaced by other x86_64 packages, i686 packages
-can be replaced by i686 and i586 packages (but i686 packages will be
-preferred) and athlon is another name for the i686 architecture.
-
-You can turn off the architecture replacement checks with the Solver's
-SOLVER_FLAG_ALLOW_ARCHCHANGE flag.
-
-Vendor Policies
----------------
-Different vendors often compile packages with different features, so
-Libsolv only replace installed packages of one vendor with packages coming
-from the same vendor. Also, while the version of a package is normally
-defined by the upstream project, the release part of the version is
-set by the vendor's package maintainer, so it's not meaningful to
-do version comparisons for packages coming from different vendors.
-
-Vendor in this case means the SOLVABLE_VENDOR string stored in each
-solvable. Sometimes a vendor changes names, or multiple vendors form a
-group that coordinate their package building, so libsolv offers a way
-to define that a group of vendors are compatible. You do that be
-defining vendor equivalence classes, packages from a vendor from
-one class may be replaced with packages from all the other vendors
-in the class.
-
-There can be multiple equivalence classes, the set of allowed vendor
-changes for an installed package is calculated by building the union
-of all of the equivalence classes the vendor of the installed package
-is part of.
-
-You can turn off the architecture replacement checks with the Solver's
-SOLVER_FLAG_ALLOW_VENDORCHANGE flag.
-
-
-Boolean Dependencies
---------------------
-Boolean Dependencies allow to build complex expressions from simple
-dependencies. While rpm does not support boolean expressions in
-dependencies and debian only allows an "OR" expression, libsolv
-allows arbitrary complex expressions. The following basic types
-are supported:
-
-*REL_OR*::
-The expression is true if either the first dependency or the second
-one is true. This is useful for package dependencies like ``Requires'',
-where you can specify that either one of the packages need to be
-installed.
-
-*REL_AND*::
-The expression is true if both dependencies are true. The packages
-fulfilling the dependencies may be different, i.e.
-``Supplements: perl AND python'' is true if both a package providing
-perl and a package providing python are installed. The solver currently
-only supports REL_AND in Supplements/Enhances dependencies, in other
-types of dependencies it gets treated as REL_WITH.
-
-*REL_WITH*::
-The expression is true if both dependencies are true and are fulfilled by
-the same package. Thus ``Supplements: perl AND python'' would only be true
-if a package is installed that provides both dependencies (some kind
-of multi-language interpreter).
-
-*REL_COND*::
-The expression is true if the first dependency is true or the second
-dependency is false. Libsolv currently does not support this type of
-dependency in the solver code.
-
-Each sub-dependency of a boolean dependency can in turn be a boolean
-dependency, so you can chain them to create complex dependencies.
-
-
-Namespace Dependencies
-----------------------
-Namespace dependencies can be used to implement dependencies on
-attributes external to libsolv. An example would be a dependency
-on the language set by the user. This types of dependencies are
-usually only used for ``Conflicts'' or ``Supplements'' dependencies,
-as the underlying package manager does not know how to deal with
-them.
-
-If the library needs to evaluate a namespace dependency, it calls
-the namespace callback function set in the pool. The callback
-function can return a set of packages that ``provide'' the
-dependency. If the dependency is provided by the system, the
-returned set should consist of just the system solvable (Solvable
-Id 1).
-
-The returned set of packages must be returned as offset into
-the whatprovidesdata array. You can use the pool_queuetowhatprovides
-function to convert a queue into such an offset. To ease programming
-the callback function, the return values ``0'' and ``1'' are not
-interpreted as an offset. ``0'' means that no package is in the
-return set, ``1'' means that just the system solvable is in the set.
-
-The returned set is cached, so that for each namespace dependency
-the callback is just called once. If you need to flush the cache (maybe
-because the user has selected a different language), use the
-pool_flush_namespaceproviders() function.
-
-
-Author
-------
-Michael Schroeder <mls@suse.de>
-
+++ /dev/null
-'\" t
-.\" Title: Libsolv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "LIBSOLV" "3" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-libsolv \- package dependency solver library using a satisfiability algorithm
-.SH "DOCUMENTATION"
-.sp
-The libsolv documentation is split into multiple parts:
-.PP
-\fBlibsolv\-history\fR
-.RS 4
-how the libsolv library came into existence
-.RE
-.PP
-\fBlibsolv\-constantids\fR
-.RS 4
-fixed Ids for often used strings
-.RE
-.PP
-\fBlibsolv\-bindings\fR
-.RS 4
-access libsolv from perl/python/ruby
-.RE
-.PP
-\fBlibsolv\-pool\fR
-.RS 4
-libsolv\(cqs pool object
-.RE
-.SH "POINTER VALIDITY"
-.sp
-Note that all pointers to objects that have an Id have only a limited validity period, with the exception of Repo pointers\&. There are only guaranteed to be valid until a new object of that type is added or an object of that type is removed\&. Thus pointers to Solvable objects are only valid until another solvable is created, because adding a Solvable may relocate the Pool\(cqs Solvable array\&. This is also true for Pool strings, you should use solv_strdup() to create a copy of the string if you want to use it at some later time\&. You should use the Ids in the code and not the pointers, except for short times where you know that the pointer is safe\&.
-.sp
-Note also that the data lookup functions or the dataiterator also return values with limited lifetime, this is especially true for data stored in the paged data segment of solv files\&. This is normally data that consists of big strings like package descriptions or is not often needed like package checksums\&. Thus looking up a description of a solvable and then looking up the description of a different solvable or even the checksum of the same solvable may invalidate the first result\&. (The dataiterator supports a dataiterator_strdup() function to create a safe copy\&.)
-.sp
-The language bindings already deal with pointer validity, so you do not have to worry about this issue when using the bindings\&.
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-Libsolv(3)
-==========
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-libsolv - package dependency solver library using a satisfiability algorithm
-
-
-Documentation
--------------
-The libsolv documentation is split into multiple parts:
-
-*libsolv-history*::
- how the libsolv library came into existence
-
-*libsolv-constantids*::
- fixed Ids for often used strings
-
-*libsolv-bindings*::
- access libsolv from perl/python/ruby
-
-*libsolv-pool*::
- libsolv's pool object
-
-Pointer Validity
-----------------
-Note that all pointers to objects that have an Id have only a limited
-validity period, with the exception of Repo pointers. There are only
-guaranteed to be valid until a new object of that type is added or an
-object of that type is removed. Thus pointers to Solvable objects are only
-valid until another solvable is created, because adding a Solvable may
-relocate the Pool's Solvable array. This is also true for Pool strings,
-you should use solv_strdup() to create a copy of the string if you
-want to use it at some later time. You should use the Ids in the code
-and not the pointers, except for short times where you know that the
-pointer is safe.
-
-Note also that the data lookup functions or the dataiterator also
-return values with limited lifetime, this is especially true for data
-stored in the paged data segment of solv files. This is normally
-data that consists of big strings like package descriptions or is not
-often needed like package checksums. Thus looking up a description of
-a solvable and then looking up the description of a different solvable
-or even the checksum of the same solvable may invalidate the first
-result. (The dataiterator supports a dataiterator_strdup() function
-to create a safe copy.)
-
-The language bindings already deal with pointer validity, so you do
-not have to worry about this issue when using the bindings.
-
-
-Author
-------
-Michael Schroeder <mls@suse.de>
-
+++ /dev/null
-'\" t
-.\" Title: mdk2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "MDK2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-mdk2solv \- convert files in Mandriva synthesis format into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBmdk2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The mdk2solv tool reads Mandriva synthesis data (\fBhdlist\fR) from stdin, and writes it as solv file to standard output\&.
-.PP
-\fB\-i\fR \fIINFO\&.xml\fR
-.RS 4
-Also read the info file containing url, license, and src information from the specified xml file\&.
-.RE
-.PP
-\fB\-f\fR \fIFILES\&.xml\fR
-.RS 4
-Also read filelist information from the specified xml file\&.
-.RE
-.SH "SEE ALSO"
-.sp
-genhdlist2(1)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-mdk2solv(1)
-===========
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-mdk2solv - convert files in Mandriva synthesis format into a solv file
-
-Synopsis
---------
-*mdk2solv* ['OPTIONS']
-
-Description
------------
-The mdk2solv tool reads Mandriva synthesis data (*hdlist*) from stdin, and writes
-it as solv file to standard output.
-
-*-i* 'INFO.xml'::
-Also read the info file containing url, license, and src information from
-the specified xml file.
-
-*-f* 'FILES.xml'::
-Also read filelist information from the specified xml file.
-
-See Also
---------
-genhdlist2(1)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: mergesolv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "MERGESOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-mergesolv \- merge multiple files in solv format into a single one
-.SH "SYNOPSIS"
-.sp
-\fBmergesolv\fR [\fIOPTIONS\fR] \fIFILE1\&.solv\fR \fIFILE2\&.solv\fR \&...
-.SH "DESCRIPTION"
-.sp
-The mergesolv tool reads all solv files specified on the command line, and writes a merged version to standard output\&.
-.PP
-\fB\-X\fR
-.RS 4
-Autoexpand SUSE pattern and product provides into packages\&.
-.RE
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-mergesolv(1)
-============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-mergesolv - merge multiple files in solv format into a single one
-
-Synopsis
---------
-*mergesolv* ['OPTIONS'] 'FILE1.solv' 'FILE2.solv' ...
-
-Description
------------
-The mergesolv tool reads all solv files specified on the command line,
-and writes a merged version to standard output.
-
-*-X*::
-Autoexpand SUSE pattern and product provides into packages.
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: repomdxml2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "REPOMDXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-repomdxml2solv \- convert a repomd\&.xml file into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBrepomdxml2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The repomd\&.xml file is the index file of a rpm\-md repository, containing references to all data file with checksums\&. The repomdxml2solv tool reads the repomd\&.xml file from stdin and writes the parsed data as solv file to standard output\&. The data is stored as meta attributes in the result\&.
-.PP
-\fB\-q\fR \fIWHAT\fR
-.RS 4
-Data query mode: instead of writing a solv file, select the
-\fIWHAT\fR
-element in the input data and write it to standard output\&. Examples for
-\fIWHAT\fR
-are
-\fBtype\fR
-to get a list of all types, and
-\fBprimary:location\fR
-to get the location of the element with type
-\fBprimary\fR\&.
-.RE
-.SH "SEE ALSO"
-.sp
-rpmmd2solv(1), mergesolv(1), createrepo(8)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-repomdxml2solv(1)
-=================
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-repomdxml2solv - convert a repomd.xml file into a solv file
-
-Synopsis
---------
-*repomdxml2solv* ['OPTIONS']
-
-Description
------------
-The repomd.xml file is the index file of a rpm-md repository,
-containing references to all data file with checksums. The
-repomdxml2solv tool reads the repomd.xml file from stdin and
-writes the parsed data as solv file to standard output. The
-data is stored as meta attributes in the result.
-
-*-q* 'WHAT'::
-Data query mode: instead of writing a solv file, select the
-'WHAT' element in the input data and write it to standard output.
-Examples for 'WHAT' are *type* to get a list of all types, and
-*primary:location* to get the location of the element with
-type *primary*.
-
-See Also
---------
-rpmmd2solv(1), mergesolv(1), createrepo(8)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: rpmdb2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "RPMDB2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-rpmdb2solv \- convert the rpm database into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBrpmdb2solv\fR [\fIOPTIONS\fR] [\fIREFFILE\&.solv\fR]
-.SH "DESCRIPTION"
-.sp
-The rpmdb2solv tool reads rpm\(cqs installed packages database and writes it in solv file format to standard output\&. You can make use of an old version of the database by specifying a \fIREFFILE\&.solv\fR file\&.
-.PP
-\fB\-o\fR \fIOUTFILE\fR
-.RS 4
-Write the generated solv to
-\fIOUTFILE\fR
-instead of standard output\&.
-.RE
-.PP
-\fB\-P\fR
-.RS 4
-Print percentages as packages are being read in\&. The output format is like rpm\(cqs \-\-percent option\&.
-.RE
-.PP
-\fB\-r\fR \fIROOTDIR\fR
-.RS 4
-Use
-\fIROOTDIR\fR
-as root directory\&.
-.RE
-.PP
-\fB\-k\fR
-.RS 4
-Read pubkeys from the rpm database instead of installed packages\&. Note that many distributions stopped storing pubkeys in the database but use a directory like
-\fB/var/lib/rpm/pubkeys\fR
-instead\&.
-.RE
-.PP
-\fB\-A\fR
-.RS 4
-Also scan the
-\fB/usr/share/appdata\fR
-for installed appdata files and create pseudo packages for each file\&.
-.RE
-.PP
-\fB\-p\fR \fIPRODDIR\fR
-.RS 4
-Also read SUSE product files from directory
-\fIPRODDIR\fR\&. The standard directory is
-\fB/etc/products\&.d\fR\&.
-.RE
-.PP
-\fB\-n\fR
-.RS 4
-Do not read any packages from the rpm database\&. This is useful together with
-\fB\-p\fR
-to only convert SUSE products\&.
-.RE
-.PP
-\fB\-X\fR
-.RS 4
-Autoexpand SUSE pattern and product provides into packages\&.
-.RE
-.SH "SEE ALSO"
-.sp
-rpms2solv(1)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-rpmdb2solv(1)
-=============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-rpmdb2solv - convert the rpm database into a solv file
-
-Synopsis
---------
-*rpmdb2solv* ['OPTIONS'] ['REFFILE.solv']
-
-Description
------------
-The rpmdb2solv tool reads rpm's installed packages database
-and writes it in solv file format to standard output. You can
-make use of an old version of the database by specifying a
-'REFFILE.solv' file.
-
-*-o* 'OUTFILE'::
-Write the generated solv to 'OUTFILE' instead of standard output.
-
-*-P*::
-Print percentages as packages are being read in. The output
-format is like rpm's --percent option.
-
-*-r* 'ROOTDIR'::
-Use 'ROOTDIR' as root directory.
-
-*-k*::
-Read pubkeys from the rpm database instead of installed packages.
-Note that many distributions stopped storing pubkeys in the
-database but use a directory like */var/lib/rpm/pubkeys*
-instead.
-
-*-A*::
-Also scan the */usr/share/appdata* for installed appdata files
-and create pseudo packages for each file.
-
-*-p* 'PRODDIR'::
-Also read SUSE product files from directory 'PRODDIR'. The
-standard directory is */etc/products.d*.
-
-*-n*::
-Do not read any packages from the rpm database. This is useful
-together with *-p* to only convert SUSE products.
-
-*-X*::
-Autoexpand SUSE pattern and product provides into packages.
-
-See Also
---------
-rpms2solv(1)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: rpmmd2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "RPMMD2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-rpmmd2solv \- convert files in rpm\-md format into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBrpmmd2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The rpmmd2solv tool reads rpm\-md xml data from stdin, and writes it as solv file to standard output\&. It understands the \fBprimary\fR, \fBfilelist\fR, \fBother\fR, and \fBsusedata\fR format\&.
-.PP
-\fB\-X\fR
-.RS 4
-Autoexpand SUSE pattern and product provides into packages\&.
-.RE
-.SH "SEE ALSO"
-.sp
-repomdxml2solv(1), mergesolv(1), createrepo(8)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-rpmmd2solv(1)
-=============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-rpmmd2solv - convert files in rpm-md format into a solv file
-
-Synopsis
---------
-*rpmmd2solv* ['OPTIONS']
-
-Description
------------
-The rpmmd2solv tool reads rpm-md xml data from stdin, and writes
-it as solv file to standard output. It understands the *primary*,
-*filelist*, *other*, and *susedata* format.
-
-*-X*::
-Autoexpand SUSE pattern and product provides into packages.
-
-See Also
---------
-repomdxml2solv(1), mergesolv(1), createrepo(8)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: rpms2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "RPMS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-rpms2solv \- convert one or more rpms into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBrpms2solv\fR [\fIOPTIONS\fR] \fIRPM1\&.rpm\fR \&...
-.SH "DESCRIPTION"
-.sp
-The rpms2solv tool converts the header data from one or more rpms into the solv file written to standard output\&.
-.PP
-\fB\-m\fR \fIMANIFESTFILE\fR
-.RS 4
-Read the rpm file names from the specified
-\fIMANIFESTFILE\fR\&. You can use
-\fB\-\fR
-to read the manifest from standard input\&.
-.RE
-.PP
-\fB\-0\fR
-.RS 4
-Use a null byte as line terminator for manifest files instead of a newline\&. This is useful if the file names can contain newlines\&. See also the
-\fB\-print0\fR
-option in
-\fBfind\fR\&.
-.RE
-.PP
-\fB\-F\fR
-.RS 4
-Do not put all files from the headers into the file list, but instead use the filtering also found in
-\fBcreaterepo\fR\&.
-.RE
-.PP
-\fB\-k\fR
-.RS 4
-Read pubkeys instead of rpms\&.
-.RE
-.PP
-\fB\-K\fR
-.RS 4
-Read pubkey keyrings instead of rpms\&.
-.RE
-.PP
-\fB\-X\fR
-.RS 4
-Autoexpand SUSE pattern and product provides into packages\&.
-.RE
-.SH "SEE ALSO"
-.sp
-rpmdb2solv(1)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-rpms2solv(1)
-============
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-rpms2solv - convert one or more rpms into a solv file
-
-Synopsis
---------
-*rpms2solv* ['OPTIONS'] 'RPM1.rpm' ...
-
-Description
------------
-The rpms2solv tool converts the header data from one or more
-rpms into the solv file written to standard output.
-
-*-m* 'MANIFESTFILE'::
-Read the rpm file names from the specified 'MANIFESTFILE'. You can
-use *-* to read the manifest from standard input.
-
-*-0*::
-Use a null byte as line terminator for manifest files instead of
-a newline. This is useful if the file names can contain newlines.
-See also the *-print0* option in *find*.
-
-*-F*::
-Do not put all files from the headers into the file list, but
-instead use the filtering also found in *createrepo*.
-
-*-k*::
-Read pubkeys instead of rpms.
-
-*-K*::
-Read pubkey keyrings instead of rpms.
-
-*-X*::
-Autoexpand SUSE pattern and product provides into packages.
-
-See Also
---------
-rpmdb2solv(1)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: susetags2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "SUSETAGS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-susetags2solv \- convert the susetags repository format into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBsusetags2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The susetags format is most as repository format on most products created by SUSE\&. The susetags2solv reads data from standard input, converts the format into a solv file, and writes it to standard output\&.
-.PP
-\fB\-c\fR \fICONTENTFILE\fR
-.RS 4
-Also parse the specified content file containing meta information about the repository\&.
-.RE
-.PP
-\fB\-q\fR \fIWHAT\fR
-.RS 4
-Data query mode: instead of writing a solv file, select the
-\fIWHAT\fR
-element in the input data and write it to standard output\&. An example for
-\fIWHAT\fR
-is
-\fBdefaultvendor\fR
-to get a default vendor for the repository\&.
-.RE
-.PP
-\fB\-M\fR \fIMERGEFILE\&.solv\fR
-.RS 4
-Merge the content of the specified solv file into the output\&.
-.RE
-.PP
-\fB\-X\fR
-.RS 4
-Autoexpand SUSE pattern and product provides into packages\&.
-.RE
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-susetags2solv(1)
-================
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-susetags2solv - convert the susetags repository format into a solv file
-
-Synopsis
---------
-*susetags2solv* ['OPTIONS']
-
-Description
------------
-The susetags format is most as repository format on most products
-created by SUSE. The susetags2solv reads data from standard input,
-converts the format into a solv file, and writes it to standard output.
-
-*-c* 'CONTENTFILE'::
-Also parse the specified content file containing meta information
-about the repository.
-
-*-q* 'WHAT'::
-Data query mode: instead of writing a solv file, select the
-'WHAT' element in the input data and write it to standard output.
-An example for 'WHAT' is *defaultvendor* to get a default vendor for
-the repository.
-
-*-M* 'MERGEFILE.solv'::
-Merge the content of the specified solv file into the output.
-
-*-X*::
-Autoexpand SUSE pattern and product provides into packages.
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: testsolv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "TESTSOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-testsolv \- run a libsolv testcase through the solver
-.SH "SYNOPSIS"
-.sp
-\fBtestsolv\fR [\fIOPTIONS\fR] \fITESTCASE\fR
-.SH "DESCRIPTION"
-.sp
-The testsolv tools can be used to run a testcase\&. Testcases can either be manually created to test specific features, or they can be written by libsolv\(cqs testcase_write function\&. This is useful to evaluate bug reports about the solver\&.
-.PP
-\fB\-v\fR
-.RS 4
-Increase the debug level of the solver\&. This option can be specified multiple times to further increase the amount of debug data\&.
-.RE
-.PP
-\fB\-r\fR
-.RS 4
-Write the output in testcase format instead of human readable text\&. The output can then be used in the result section of the test case\&. If the
-\fB\-r\fR
-option is given twice, the output is formated for verbatim inclusion\&.
-.RE
-.PP
-\fB\-l\fR \fIPKGSPEC\fR
-.RS 4
-Instead of running the solver, list packages in the repositories\&.
-.RE
-.PP
-\fB\-s\fR \fISOLUTIONSPEC\fR
-.RS 4
-This is used in the solver test suite to test the calculated solutions to encountered problems\&.
-.RE
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-testsolv(1)
-===========
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-testsolv - run a libsolv testcase through the solver
-
-Synopsis
---------
-*testsolv* ['OPTIONS'] 'TESTCASE'
-
-Description
------------
-The testsolv tools can be used to run a testcase. Testcases can
-either be manually created to test specific features, or they
-can be written by libsolv's testcase_write function. This is useful
-to evaluate bug reports about the solver.
-
-*-v*::
-Increase the debug level of the solver. This option can be specified
-multiple times to further increase the amount of debug data.
-
-*-r*::
-Write the output in testcase format instead of human readable text.
-The output can then be used in the result section of the test case.
-If the *-r* option is given twice, the output is formated for
-verbatim inclusion.
-
-*-l* 'PKGSPEC'::
-Instead of running the solver, list packages in the repositories.
-
-*-s* 'SOLUTIONSPEC'::
-This is used in the solver test suite to test the calculated solutions
-to encountered problems.
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-'\" t
-.\" Title: updateinfoxml2solv
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/26/2015
-.\" Manual: LIBSOLV
-.\" Source: libsolv
-.\" Language: English
-.\"
-.TH "UPDATEINFOXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-updateinfoxml2solv \- convert rpm\-md\*(Aqs updateinfo\&.xml format into a solv file
-.SH "SYNOPSIS"
-.sp
-\fBupdateinfoxml2solv\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-The updateinfoxml2solv tool reads rpm\-md\(cqs updateinfo xml data from stdin, and writes it as solv file to standard output\&. Update elements are converted into special \fBpatch:\fR pseudo packages\&.
-.SH "SEE ALSO"
-.sp
-mergesolv(1), createrepo(8)
-.SH "AUTHOR"
-.sp
-Michael Schroeder <mls@suse\&.de>
+++ /dev/null
-updateinfoxml2solv(1)
-=====================
-:man manual: LIBSOLV
-:man source: libsolv
-
-
-Name
-----
-updateinfoxml2solv - convert rpm-md's updateinfo.xml format into a solv file
-
-Synopsis
---------
-*updateinfoxml2solv* ['OPTIONS']
-
-Description
------------
-The updateinfoxml2solv tool reads rpm-md's updateinfo xml data from stdin,
-and writes it as solv file to standard output. Update elements are converted
-into special *patch:* pseudo packages.
-
-See Also
---------
-mergesolv(1), createrepo(8)
-
-Author
-------
-Michael Schroeder <mls@suse.de>
+++ /dev/null
-IF (SUSE OR FEDORA OR DEBIAN OR MANDRIVA OR MAGEIA)
-
-ADD_SUBDIRECTORY (solv)
-
-ENDIF (SUSE OR FEDORA OR DEBIAN OR MANDRIVA OR MAGEIA)
+++ /dev/null
-#!/usr/bin/perl -w
-
-use POSIX;
-use Fcntl;
-use Config::IniFiles;
-use Data::Dumper;
-use solv;
-use Devel::Peek;
-use FileHandle;
-use File::Temp ();
-use strict;
-
-package Repo::generic;
-
-sub new {
- my ($class, $alias, $type, $attr) = @_;
- my $r = { %{$attr || {}} };
- $r->{alias} = $alias;
- $r->{type} = $type;
- return bless $r, $class;
-}
-
-sub calc_cookie_fp {
- my ($self, $fp) = @_;
- my $chksum = solv::Chksum->new($solv::REPOKEY_TYPE_SHA256);
- $chksum->add("1.1");
- $chksum->add_fp($fp);
- return $chksum->raw();
-}
-
-sub calc_cookie_file {
- my ($self, $filename) = @_;
- my $chksum = solv::Chksum->new($solv::REPOKEY_TYPE_SHA256);
- $chksum->add("1.1");
- $chksum->add_stat($filename);
- return $chksum->raw();
-}
-
-sub calc_cookie_ext {
- my ($self, $f, $cookie) = @_;
- my $chksum = solv::Chksum->new($solv::REPOKEY_TYPE_SHA256);
- $chksum->add("1.1");
- $chksum->add($cookie);
- $chksum->add_fstat(fileno($f));
- return $chksum->raw();
-}
-
-sub cachepath {
- my ($self, $ext) = @_;
- my $path = $self->{alias};
- $path =~ s/^\./_/s;
- $path .= $ext ? "_$ext.solvx" : '.solv';
- $path =~ s!/!_!gs;
- return "/var/cache/solv/$path";
-}
-
-sub load {
- my ($self, $pool) = @_;
- $self->{handle} = $pool->add_repo($self->{alias});
- $self->{handle}->{appdata} = $self;
- $self->{handle}->{priority} = 99 - $self->{priority};
- my $dorefresh = $self->{autorefresh};
- if ($dorefresh) {
- my @s = stat($self->cachepath());
- $dorefresh = 0 if @s && ($self->{metadata_expire} == -1 || time() - $s[9] < $self->{metadata_expire});
- }
- $self->{cookie} = '';
- $self->{extcookie} = '';
- if (!$dorefresh && $self->usecachedrepo()) {
- print "repo: '$self->{alias}' cached\n";
- return 1;
- }
- return 0;
-}
-
-sub load_ext {
- return 0;
-}
-
-sub download {
- my ($self, $file, $uncompress, $chksum, $markincomplete) = @_;
- if (!$self->{baseurl}) {
- print "$self->{alias}: no baseurl\n";
- return undef;
- }
- my $url = $self->{baseurl};
- $url =~ s!/$!!;
- $url .= "/$file";
- open(my $f, '+>', undef) || die;
- fcntl($f, Fcntl::F_SETFD, 0); # turn off CLOEXEC
- my $st = system('curl', '-f', '-s', '-L', '-o', "/dev/fd/" . fileno($f), '--', $url);
- if (POSIX::lseek(fileno($f), 0, POSIX::SEEK_END) == 0 && ($st == 0 || !$chksum)) {
- return undef;
- }
- POSIX::lseek(fileno($f), 0, POSIX::SEEK_SET);
- if ($st) {
- print "$file: download error #$st\n";
- $self->{incomplete} = 1 if $markincomplete;
- return undef;
- }
- if ($chksum) {
- my $fchksum = solv::Chksum->new($chksum->{type});
- $fchksum->add_fd(fileno($f));
- if ($fchksum != $chksum) {
- print "$file: checksum error\n";
- $self->{incomplete} = 1 if $markincomplete;
- return undef;
- }
- }
- if ($uncompress) {
- return solv::xfopen_fd($file, fileno($f));
- } else {
- return solv::xfopen_fd(undef, fileno($f));
- }
-}
-
-sub usecachedrepo {
- my ($self, $ext, $mark) = @_;
- my $cookie = $ext ? $self->{extcookie} : $self->{cookie};
- my $cachepath = $self->cachepath($ext);
- my $fextcookie;
- if (sysopen(my $f, $cachepath, POSIX::O_RDONLY)) {
- sysseek($f, -32, Fcntl::SEEK_END);
- my $fcookie = '';
- return undef if sysread($f, $fcookie, 32) != 32;
- return undef if $cookie && $fcookie ne $cookie;
- if ($self->{type} ne 'system' && !$ext) {
- sysseek($f, -32 * 2, Fcntl::SEEK_END);
- return undef if sysread($f, $fextcookie, 32) != 32;
- }
- sysseek($f, 0, Fcntl::SEEK_SET);
- my $fd = solv::xfopen_fd(undef, fileno($f));
- my $flags = $ext ? $solv::Repo::REPO_USE_LOADING|$solv::Repo::REPO_EXTEND_SOLVABLES : 0;
- $flags |= $solv::Repo::REPO_LOCALPOOL if $ext && $ext ne 'DL';
- if (!$self->{handle}->add_solv($fd, $flags)) {
- return undef;
- }
- $self->{cookie} = $fcookie unless $ext;
- $self->{extcookie} = $fextcookie if $fextcookie;
- utime undef, undef, $f if $mark;
- return 1;
- }
- return undef;
-}
-
-sub writecachedrepo {
- my ($self, $ext, $repodata) = @_;
- return if $self->{incomplete};
- mkdir("/var/cache/solv", 0755) unless -d "/var/cache/solv";
- my ($f, $tmpname);
- eval {
- ($f, $tmpname) = File::Temp::tempfile(".newsolv-XXXXXX", 'DIR' => '/var/cache/solv');
- };
- return unless $f;
- chmod 0444, $f;
- my $ff = solv::xfopen_fd(undef, fileno($f));
- if (!$repodata) {
- $self->{handle}->write($ff);
- } elsif ($ext) {
- $repodata->write($ff);
- } else {
- $self->{handle}->write_first_repodata($ff);
- }
- undef $ff; # also flushes
- if ($self->{type} ne 'system' && !$ext) {
- $self->{extcookie} ||= $self->calc_cookie_ext($f, $self->{cookie});
- syswrite($f, $self->{extcookie});
- }
- syswrite($f, $ext ? $self->{extcookie} : $self->{cookie});
- close($f);
- if ($self->{handle}->iscontiguous()) {
- $f = solv::xfopen($tmpname);
- if ($f) {
- if (!$ext) {
- $self->{handle}->empty();
- die("internal error, cannot reload solv file\n") unless $self->{handle}->add_solv($f, $repodata ? 0 : $solv::Repo::SOLV_ADD_NO_STUBS);
- } else {
- $repodata->extend_to_repo();
- my $flags = $solv::Repo::REPO_EXTEND_SOLVABLES;
- $flags |= $solv::Repo::REPO_LOCALPOOL if $ext ne 'DL';
- $repodata->add_solv($f, $flags);
- }
- }
- }
- rename($tmpname, $self->cachepath($ext));
-}
-
-sub packagespath {
- my ($self) = @_;
- return '';
-}
-
-my %langtags = (
- $solv::SOLVABLE_SUMMARY => $solv::REPOKEY_TYPE_STR,
- $solv::SOLVABLE_DESCRIPTION => $solv::REPOKEY_TYPE_STR,
- $solv::SOLVABLE_EULA => $solv::REPOKEY_TYPE_STR,
- $solv::SOLVABLE_MESSAGEINS => $solv::REPOKEY_TYPE_STR,
- $solv::SOLVABLE_MESSAGEDEL => $solv::REPOKEY_TYPE_STR,
- $solv::SOLVABLE_CATEGORY => $solv::REPOKEY_TYPE_ID,
-);
-
-sub add_ext_keys {
- my ($self, $ext, $repodata, $handle) = @_;
- if ($ext eq 'DL') {
- $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOSITORY_DELTAINFO);
- $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_FLEXARRAY);
- } elsif ($ext eq 'DU') {
- $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_DISKUSAGE);
- $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRNUMNUMARRAY);
- } elsif ($ext eq 'FL') {
- $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_FILELIST);
- $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRSTRARRAY);
- } else {
- for my $langid (sort { $a <=> $b } keys %langtags) {
- $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $self->{handle}->{pool}->id2langid($langid, $ext, 1));
- $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $langtags{$langid});
- }
- }
-}
-
-package Repo::rpmmd;
-
-our @ISA = ('Repo::generic');
-
-sub find {
- my ($self, $what) = @_;
- my $di = $self->{handle}->Dataiterator_meta($solv::REPOSITORY_REPOMD_TYPE, $what, $solv::Dataiterator::SEARCH_STRING);
- $di->prepend_keyname($solv::REPOSITORY_REPOMD);
- for my $d (@$di) {
- my $dp = $d->parentpos();
- my $filename = $dp->lookup_str($solv::REPOSITORY_REPOMD_LOCATION);
- next unless $filename;
- my $chksum = $dp->lookup_checksum($solv::REPOSITORY_REPOMD_CHECKSUM);
- if (!$chksum) {
- print "no $filename file checksum!\n";
- return (undef, undef);
- }
- return ($filename, $chksum);
- }
- return (undef, undef);
-}
-
-sub add_ext {
- my ($self, $repodata, $what, $ext) = @_;
- my ($filename, $chksum) = $self->find($what);
- ($filename, $chksum) = $self->find('prestodelta') if !$filename && $what eq 'deltainfo';
- return unless $filename;
- my $handle = $repodata->new_handle();
- $repodata->set_poolstr($handle, $solv::REPOSITORY_REPOMD_TYPE, $what);
- $repodata->set_str($handle, $solv::REPOSITORY_REPOMD_LOCATION, $filename);
- $repodata->set_checksum($handle, $solv::REPOSITORY_REPOMD_CHECKSUM, $chksum);
- $self->add_ext_keys($ext, $repodata, $handle);
- $repodata->add_flexarray($solv::SOLVID_META, $solv::REPOSITORY_EXTERNAL, $handle);
-}
-
-sub add_exts {
- my ($self) = @_;
- my $repodata = $self->{handle}->add_repodata(0);
- $self->add_ext($repodata, 'deltainfo', 'DL');
- $self->add_ext($repodata, 'filelists', 'FL');
- $repodata->internalize();
-}
-
-sub load_ext {
- my ($self, $repodata) = @_;
- my $repomdtype = $repodata->lookup_str($solv::SOLVID_META, $solv::REPOSITORY_REPOMD_TYPE);
- my $ext;
- if ($repomdtype eq 'filelists') {
- $ext = 'FL';
- } elsif ($repomdtype eq 'deltainfo') {
- $ext = 'DL';
- } else {
- return 0;
- }
- print("[$self->{alias}:$ext: ");
- STDOUT->flush();
- if ($self->usecachedrepo($ext)) {
- print "cached]\n";
- return 1;
- }
- print "fetching]\n";
- my $filename = $repodata->lookup_str($solv::SOLVID_META, $solv::REPOSITORY_REPOMD_LOCATION);
- my $filechksum = $repodata->lookup_checksum($solv::SOLVID_META, $solv::REPOSITORY_REPOMD_CHECKSUM);
- my $f = $self->download($filename, 1, $filechksum);
- return 0 unless $f;
- if ($ext eq 'FL') {
- $self->{handle}->add_rpmmd($f, 'FL', $solv::Repo::REPO_USE_LOADING|$solv::Repo::REPO_EXTEND_SOLVABLES|$solv::Repo::REPO_LOCALPOOL);
- } elsif ($ext eq 'DL') {
- $self->{handle}->add_deltainfoxml($f, $solv::Repo::REPO_USE_LOADING);
- }
- $self->writecachedrepo($ext, $repodata);
- return 1;
-}
-
-sub load {
- my ($self, $pool) = @_;
- return 1 if $self->Repo::generic::load($pool);
- print "rpmmd repo '$self->{alias}': ";
- STDOUT->flush();
- my $f = $self->download("repodata/repomd.xml");
- if (!$f) {
- print "no repomd.xml file, skipped\n";
- $self->{handle}->free(1);
- delete $self->{handle};
- return undef;
- }
- $self->{cookie} = $self->calc_cookie_fp($f);
- if ($self->usecachedrepo(undef, 1)) {
- print "cached\n";
- return 1;
- }
- $self->{handle}->add_repomdxml($f, 0);
- print "fetching\n";
- my ($filename, $filechksum) = $self->find('primary');
- if ($filename) {
- $f = $self->download($filename, 1, $filechksum, 1);
- if ($f) {
- $self->{handle}->add_rpmmd($f, undef, 0);
- }
- return undef if $self->{incomplete};
- }
- ($filename, $filechksum) = $self->find('updateinfo');
- if ($filename) {
- $f = $self->download($filename, 1, $filechksum, 1);
- if ($f) {
- $self->{handle}->add_updateinfoxml($f, 0);
- }
- }
- $self->add_exts();
- $self->writecachedrepo();
- $self->{handle}->create_stubs();
- return 1;
-}
-
-package Repo::susetags;
-
-our @ISA = ('Repo::generic');
-
-sub find {
- my ($self, $what) = @_;
-
- my $di = $self->{handle}->Dataiterator_meta($solv::SUSETAGS_FILE_NAME, $what, $solv::Dataiterator::SEARCH_STRING);
- $di->prepend_keyname($solv::SUSETAGS_FILE);
- for my $d (@$di) {
- my $dp = $d->parentpos();
- my $chksum = $dp->lookup_checksum($solv::SUSETAGS_FILE_CHECKSUM);
- return ($what, $chksum);
- }
- return (undef, undef);
-}
-
-
-sub add_ext {
- my ($self, $repodata, $what, $ext) = @_;
- my ($filename, $chksum) = $self->find($what);
- return unless $filename;
- my $handle = $repodata->new_handle();
- $repodata->set_str($handle, $solv::SUSETAGS_FILE_NAME, $filename);
- $repodata->set_checksum($handle, $solv::SUSETAGS_FILE_CHECKSUM, $chksum);
- $self->add_ext_keys($ext, $repodata, $handle);
- $repodata->add_flexarray($solv::SOLVID_META, $solv::REPOSITORY_EXTERNAL, $handle);
-}
-
-sub add_exts {
- my ($self) = @_;
- my $repodata = $self->{handle}->add_repodata(0);
- my $di = $self->{handle}->Dataiterator_meta($solv::SUSETAGS_FILE_NAME, undef, 0);
- $di->prepend_keyname($solv::SUSETAGS_FILE);
- for my $d (@$di) {
- my $filename = $d->str();
- next unless $filename && $filename =~ /^packages\.(..)(?:\..*)$/;
- next if $1 eq 'en' || $1 eq 'gz';
- $self->add_ext($repodata, $filename, $1);
- }
- $repodata->internalize();
-}
-
-sub load_ext {
- my ($self, $repodata) = @_;
- my $filename = $repodata->lookup_str($solv::SOLVID_META, $solv::SUSETAGS_FILE_NAME);
- my $ext = substr($filename, 9, 2);
- print("[$self->{alias}:$ext: ");
- STDOUT->flush();
- if ($self->usecachedrepo($ext)) {
- print "cached]\n";
- return 1;
- }
- print "fetching]\n";
- my $defvendorid = $self->{handle}->{meta}->lookup_id($solv::SUSETAGS_DEFAULTVENDOR);
- my $descrdir = $self->{handle}->{meta}->lookup_str($solv::SUSETAGS_DESCRDIR) || 'suse/setup/descr';
- my $filechksum = $repodata->lookup_checksum($solv::SOLVID_META, $solv::SUSETAGS_FILE_CHECKSUM);
- my $f = $self->download("$descrdir/$filename", 1, $filechksum);
- return 0 unless $f;
- my $flags = $solv::Repo::REPO_USE_LOADING|$solv::Repo::REPO_EXTEND_SOLVABLES;
- $flags |= $solv::Repo::REPO_LOCALPOOL if $ext ne 'DL';
- $self->{handle}->add_susetags($f, $defvendorid, $ext, $flags);
- $self->writecachedrepo($ext, $repodata);
- return 1;
-}
-
-sub load {
- my ($self, $pool) = @_;
- return 1 if $self->Repo::generic::load($pool);
- print "susetags repo '$self->{alias}': ";
- STDOUT->flush();
- my $f = $self->download("content");
- if (!$f) {
- print "no content file, skipped\n";
- $self->{handle}->free(1);
- delete $self->{handle};
- return undef;
- }
- $self->{cookie} = $self->calc_cookie_fp($f);
- if ($self->usecachedrepo(undef, 1)) {
- print "cached\n";
- return 1;
- }
- $self->{handle}->add_content($f, 0);
- print "fetching\n";
- my $defvendorid = $self->{handle}->{meta}->lookup_id($solv::SUSETAGS_DEFAULTVENDOR);
- my $descrdir = $self->{handle}->{meta}->lookup_str($solv::SUSETAGS_DESCRDIR) || 'suse/setup/descr';
- my ($filename, $filechksum) = $self->find('packages.gz');
- ($filename, $filechksum) = $self->find('packages') unless $filename;
- if ($filename) {
- $f = $self->download("$descrdir/$filename", 1, $filechksum, 1);
- if ($f) {
- $self->{handle}->add_susetags($f, $defvendorid, undef, $solv::Repo::REPO_NO_INTERNALIZE|$solv::Repo::SUSETAGS_RECORD_SHARES);
- ($filename, $filechksum) = $self->find('packages.en.gz');
- ($filename, $filechksum) = $self->find('packages.en') unless $filename;
- if ($filename) {
- $f = $self->download("$descrdir/$filename", 1, $filechksum, 1);
- if ($f) {
- $self->{handle}->add_susetags($f, $defvendorid, undef, $solv::Repo::REPO_NO_INTERNALIZE|$solv::Repo::REPO_REUSE_REPODATA|$solv::Repo::REPO_EXTEND_SOLVABLES);
- }
- }
- $self->{handle}->internalize();
- }
- }
- $self->add_exts();
- $self->writecachedrepo();
- $self->{handle}->create_stubs();
- return undef;
-}
-
-sub packagespath {
- my ($self) = @_;
- return ($self->{handle}->{meta}->lookup_str($solv::SUSETAGS_DATADIR) || 'suse') . '/';
-}
-
-package Repo::unknown;
-
-our @ISA = ('Repo::generic');
-
-sub load {
- my ($self) = @_;
- print "unsupported repo '$self->{alias}': skipped\n";
- return 0;
-}
-
-package Repo::system;
-
-our @ISA = ('Repo::generic');
-
-sub load {
- my ($self, $pool) = @_;
-
- $self->{handle} = $pool->add_repo($self->{alias});
- $self->{handle}->{appdata} = $self;
- $pool->{installed} = $self->{handle};
- print "rpm database: ";
- $self->{cookie} = $self->calc_cookie_file('/var/lib/rpm/Packages');
- if ($self->usecachedrepo()) {
- print "cached\n";
- return 1;
- }
- print "reading\n";
- if (defined(&solv::Repo::add_products)) {
- $self->{handle}->add_products("/etc/products.d", $solv::Repo::REPO_NO_INTERNALIZE);
- }
- my $f = solv::xfopen($self->cachepath());
- $self->{handle}->add_rpmdb_reffp($f, $solv::Repo::REPO_REUSE_REPODATA);
- $self->writecachedrepo();
- return 1;
-}
-
-package main;
-
-sub load_stub {
- my ($repodata) = @_;
- my $repo = $repodata->{repo}->{appdata};
- return $repo ? $repo->load_ext($repodata) : 0;
-}
-
-die("Usage: p5solv COMMAND [ARGS]\n") unless @ARGV;
-my $cmd = shift @ARGV;
-my %cmdabbrev = ( 'ls' => 'list', 'in' => 'install', 'rm' => 'erase',
- 've' => 'verify', 'se' => 'search' );
-$cmd = $cmdabbrev{$cmd} if $cmdabbrev{$cmd};
-
-my %cmdactionmap = (
- 'install' => $solv::Job::SOLVER_INSTALL,
- 'erase' => $solv::Job::SOLVER_ERASE,
- 'up' => $solv::Job::SOLVER_UPDATE,
- 'dup' => $solv::Job::SOLVER_DISTUPGRADE,
- 'verify' => $solv::Job::SOLVER_VERIFY,
- 'list' => 0,
- 'info' => 0,
-);
-
-my @repos;
-my @reposdirs;
-if (-d '/etc/zypp/repos.d') {
- @reposdirs = ( '/etc/zypp/repos.d' );
-} else {
- @reposdirs = ( '/etc/yum/repos.d' );
-}
-for my $reposdir (@reposdirs) {
- next unless -d $reposdir;
- my $dir;
- next unless opendir($dir, $reposdir);
- for my $reponame (sort(grep {/\.repo$/} readdir($dir))) {
- my $cfg = new Config::IniFiles('-file' => "$reposdir/$reponame");
- for my $alias ($cfg->Sections()) {
- my $repoattr = {'alias' => $alias, 'enabled' => 0, 'priority' => 99, 'autorefresh' => 1, 'type' => 'rpm-md', 'metadata_expire' => 900};
- for my $p ($cfg->Parameters($alias)) {
- $repoattr->{$p} = $cfg->val($alias, $p);
- }
- my $repo;
- if ($repoattr->{type} eq 'rpm-md') {
- $repo = Repo::rpmmd->new($alias, 'repomd', $repoattr);
- } elsif ($repoattr->{type} eq 'yast2') {
- $repo = Repo::susetags->new($alias, 'susetags', $repoattr);
- } else {
- $repo = Repo::unknown->new($alias, 'unknown', $repoattr);
- }
- push @repos, $repo;
- }
- }
-}
-
-my $pool = solv::Pool->new();
-$pool->setarch();
-$pool->set_loadcallback(\&load_stub);
-
-my $sysrepo = Repo::system->new('@System', 'system');
-$sysrepo->load($pool);
-for my $repo (@repos) {
- $repo->load($pool) if $repo->{enabled};
-}
-
-if ($cmd eq 'search') {
- $pool->createwhatprovides();
- my $sel = $pool->Selection();
- my $di = $pool->Dataiterator($solv::SOLVABLE_NAME, $ARGV[0], $solv::Dataiterator::SEARCH_SUBSTRING | $solv::Dataiterator::SEARCH_NOCASE);
- for my $d (@$di) {
- $sel->add_raw($solv::Job::SOLVER_SOLVABLE, $d->{solvid});
- }
- for my $s ($sel->solvables()) {
- print "- ".$s->str()." [$s->{repo}->{name}]: ".$s->lookup_str($solv::SOLVABLE_SUMMARY)."\n";
- }
- exit(0);
-}
-
-die("unknown command '$cmd'\n") unless defined $cmdactionmap{$cmd};
-
-$pool->addfileprovides();
-$pool->createwhatprovides();
-
-my @jobs;
-for my $arg (@ARGV) {
- my $flags = $solv::Selection::SELECTION_NAME | $solv::Selection::SELECTION_PROVIDES | $solv::Selection::SELECTION_GLOB;
- $flags |= $solv::Selection::SELECTION_CANON | $solv::Selection::SELECTION_DOTARCH | $solv::Selection::SELECTION_REL;
- if ($arg =~ m!^/!) {
- $flags |= $solv::Selection::SELECTION_FILELIST;
- $flags |= $solv::Selection::SELECTION_INSTALLED_ONLY if $cmd eq 'erase';
- }
- my $sel = $pool->select($arg, $flags);
- if ($sel->isempty()) {
- $sel = $pool->select($arg, $flags | $solv::Selection::SELECTION_NOCASE);
- print "[ignoring case for '$arg']\n" unless $sel->isempty();
- }
- die("nothing matches '$arg'\n") if $sel->isempty();
- print "[using file list match for '$arg']\n" if $sel->flags() & $solv::Selection::SELECTION_FILELIST;
- print "[using capability match for '$arg']\n" if $sel->flags() & $solv::Selection::SELECTION_PROVIDES;
- push @jobs, $sel->jobs($cmdactionmap{$cmd});
-}
-
-if (!@jobs && ($cmd eq 'up' || $cmd eq 'dup' || $cmd eq 'verify')) {
- my $sel = $pool->Selection_all();
- push @jobs, $sel->jobs($cmdactionmap{$cmd});
-}
-
-die("no package matched.\n") unless @jobs;
-
-if ($cmd eq 'list' || $cmd eq 'info') {
- for my $job (@jobs) {
- for my $s ($job->solvables()) {
- if ($cmd eq 'info') {
- printf "Name: %s\n", $s->str();
- printf "Repo: %s\n", $s->{repo}->{name};
- printf "Summary: %s\n", $s->lookup_str($solv::SOLVABLE_SUMMARY);
- my $str = $s->lookup_str($solv::SOLVABLE_URL);
- printf "Url: %s\n", $str if $str;
- $str = $s->lookup_str($solv::SOLVABLE_LICENSE);
- printf "License: %s\n", $str if $str;
- printf "Description:\n%s\n", $s->lookup_str($solv::SOLVABLE_DESCRIPTION);
- } else {
- printf " - %s [%s]\n", $s->str(), $s->{repo}->{name};
- printf " %s\n", $s->lookup_str($solv::SOLVABLE_SUMMARY);
- }
- }
- }
- exit 0;
-}
-
-# up magic, turn into install if nothing matches
-for my $job (@jobs) {
- $job->{how} ^= $solv::Job::SOLVER_UPDATE ^ $solv::Job::SOLVER_INSTALL if $cmd eq 'up' && $job->isemptyupdate();
-}
-
-my $solver = $pool->Solver();
-$solver->set_flag($solv::Solver::SOLVER_FLAG_SPLITPROVIDES, 1);
-$solver->set_flag($solv::Solver::SOLVER_FLAG_ALLOW_UNINSTALL, 1) if $cmd eq 'erase';
-
-while (1) {
- my @problems = $solver->solve(\@jobs);
- last unless @problems;
- for my $problem (@problems) {
- print "Problem $problem->{id}/".@problems.":\n";
- print $problem->str()."\n";
- my @solutions = $problem->solutions();
- for my $solution (@solutions) {
- print " Solution $solution->{id}:\n";
- for my $element ($solution->elements(1)) {
- print " - ".$element->str()."\n";
- }
- print "\n";
- }
- my $sol;
- while (1) {
- print "Please choose a solution: ";
- $sol = <STDIN>;
- chomp $sol;
- last if $sol eq 's' || $sol eq 'q' || ($sol =~ /^\d+$/ && $sol >= 1 && $sol <= @solutions);
- }
- next if $sol eq 's';
- exit(1) if $sol eq 'q';
- my $solution = $solutions[$sol - 1];
- for my $element ($solution->elements()) {
- my $newjob = $element->Job();
- if ($element->{type} == $solv::Solver::SOLVER_SOLUTION_JOB) {
- $jobs[$element->{jobidx}] = $newjob;
- } else {
- push @jobs, $newjob if $newjob && !grep {$_ == $newjob} @jobs;
- }
- }
- }
-}
-
-my $trans = $solver->transaction();
-undef $solver;
-if ($trans->isempty()) {
- print "Nothing to do.\n";
- exit 0;
-}
-
-print "\nTransaction summary:\n\n";
-for my $c ($trans->classify($solv::Transaction::SOLVER_TRANSACTION_SHOW_OBSOLETES|$solv::Transaction::SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE)) {
- if ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_ERASE) {
- print "$c->{count} erased packages:\n";
- } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_INSTALL) {
- print "$c->{count} installed packages:\n";
- } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_REINSTALLED) {
- print "$c->{count} reinstalled packages:\n";
- } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED) {
- print "$c->{count} downgraded packages:\n";
- } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_CHANGED) {
- print "$c->{count} changed packages:\n";
- } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_UPGRADED) {
- print "$c->{count} upgraded packages:\n";
- } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_VENDORCHANGE) {
- printf "$c->{count} vendor changes from '%s' to '%s':\n", $c->{fromstr}, $c->{tostr};
- } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_ARCHCHANGE) {
- printf "$c->{count} arch changes from '%s' to '%s':\n", $c->{fromstr}, $c->{tostr};
- } else {
- next;
- }
- for my $p ($c->solvables()) {
- if ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_UPGRADED || $c->{type} == $solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED) {
- my $other = $trans->othersolvable($p);
- printf " - %s -> %s\n", $p->str(), $other->str();
- } else {
- printf " - %s\n", $p->str();
- }
- }
- print "\n";
-}
-printf "install size change: %d K\n\n", $trans->calc_installsizechange();
-
-while (1) {
- print("OK to continue (y/n)? ");
- my $yn = <STDIN>;
- chomp $yn;
- last if $yn eq 'y';
- exit(1) if $yn eq 'n' || $yn eq 'q';
-}
-
-my @newpkgs = $trans->newsolvables();
-my %newpkgsfps;
-if (@newpkgs) {
- my $downloadsize = 0;
- $downloadsize += $_->lookup_num($solv::SOLVABLE_DOWNLOADSIZE) for @newpkgs;
- printf "Downloading %d packages, %d K\n", scalar(@newpkgs), $downloadsize / 1024;
- for my $p (@newpkgs) {
- my $repo = $p->{repo}->{appdata};
- my ($location) = $p->lookup_location();
- next unless $location;
- $location = $repo->packagespath() . $location;
- my $chksum = $p->lookup_checksum($solv::SOLVABLE_CHECKSUM);
- my $f = $repo->download($location, 0, $chksum);
- die("\n$repo->{alias}: $location not found in repository\n") unless $f;
- $newpkgsfps{$p->{id}} = $f;
- print ".";
- STDOUT->flush();
- }
- print "\n";
-}
-
-print "Committing transaction:\n\n";
-$trans->order();
-for my $p ($trans->steps()) {
- my $steptype = $trans->steptype($p, $solv::Transaction::SOLVER_TRANSACTION_RPM_ONLY);
- if ($steptype == $solv::Transaction::SOLVER_TRANSACTION_ERASE) {
- print "erase ".$p->str()."\n";
- next unless $p->lookup_num($solv::RPM_RPMDBID);
- my $evr = $p->{evr};
- $evr =~ s/^[0-9]+://; # strip epoch
- system('rpm', '-e', '--nodeps', '--nodigest', '--nosignature', "$p->{name}-$evr.$p->{arch}") && die("rpm failed: $?\n");
- } elsif ($steptype == $solv::Transaction::SOLVER_TRANSACTION_INSTALL || $steptype == $solv::Transaction::SOLVER_TRANSACTION_MULTIINSTALL) {
- print "install ".$p->str()."\n";
- my $f = $newpkgsfps{$p->{id}};
- my $mode = $steptype == $solv::Transaction::SOLVER_TRANSACTION_INSTALL ? '-U' : '-i';
- $f->cloexec(0);
- system('rpm', $mode, '--force', '--nodeps', '--nodigest', '--nosignature', "/dev/fd/".$f->fileno()) && die("rpm failed: $?\n");
- delete $newpkgsfps{$p->{id}};
- }
-}
-
-exit 0;
+++ /dev/null
-#!/usr/bin/python
-
-#
-# Copyright (c) 2011, Novell Inc.
-#
-# This program is licensed under the BSD license, read LICENSE.BSD
-# for further information
-#
-
-# pysolv a little software installer demoing the sat solver library/bindings
-
-# things it does:
-# - understands globs for package names / dependencies
-# - understands .arch suffix
-# - repository data caching
-# - on demand loading of secondary repository data
-# - checksum verification
-# - deltarpm support
-# - installation of commandline packages
-#
-# things not yet ported:
-# - gpg verification
-# - file conflicts
-# - fastestmirror implementation
-#
-# things available in the library but missing from pysolv:
-# - vendor policy loading
-# - soft locks file handling
-# - multi version handling
-
-import sys
-import os
-import glob
-import solv
-import re
-import tempfile
-import time
-import subprocess
-import rpm
-from stat import *
-from iniparse import INIConfig
-from optparse import OptionParser
-
-#import gc
-#gc.set_debug(gc.DEBUG_LEAK)
-
-class repo_generic(dict):
- def __init__(self, name, type, attribs = {}):
- for k in attribs:
- self[k] = attribs[k]
- self.name = name
- self.type = type
-
- def calc_cookie_file(self, filename):
- chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
- chksum.add("1.1")
- chksum.add_stat(filename)
- return chksum.raw()
-
- def calc_cookie_fp(self, fp):
- chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
- chksum.add("1.1");
- chksum.add_fp(fp)
- return chksum.raw()
-
- def calc_cookie_ext(self, f, cookie):
- chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
- chksum.add("1.1");
- chksum.add(cookie)
- chksum.add_fstat(f.fileno())
- return chksum.raw()
-
- def cachepath(self, ext = None):
- path = re.sub(r'^\.', '_', self.name)
- if ext:
- path += "_" + ext + ".solvx"
- else:
- path += ".solv"
- return "/var/cache/solv/" + re.sub(r'[/]', '_', path)
-
- def load(self, pool):
- self.handle = pool.add_repo(self.name)
- self.handle.appdata = self
- self.handle.priority = 99 - self['priority']
- dorefresh = bool(int(self['autorefresh']))
- if dorefresh:
- try:
- st = os.stat(self.cachepath())
- if self['metadata_expire'] == -1 or time.time() - st[ST_MTIME] < self['metadata_expire']:
- dorefresh = False
- except OSError:
- pass
- self['cookie'] = ''
- self['extcookie'] = ''
- if not dorefresh and self.usecachedrepo(None):
- print("repo: '%s': cached" % self.name)
- return True
- return False
-
- def load_ext(self, repodata):
- return False
-
- def setfromurls(self, urls):
- if not urls:
- return
- url = urls[0]
- print("[using mirror %s]" % re.sub(r'^(.*?/...*?)/.*$', r'\1', url))
- self['baseurl'] = url
-
- def setfrommetalink(self, metalink):
- f = self.download(metalink, False, None)
- if not f:
- return None
- f = os.fdopen(f.dup(), 'r')
- urls = []
- chksum = None
- for l in f.readlines():
- l = l.strip()
- m = re.match(r'^<hash type="sha256">([0-9a-fA-F]{64})</hash>', l)
- if m:
- chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256, m.group(1))
- m = re.match(r'^<url.*>(https?://.+)repodata/repomd.xml</url>', l)
- if m:
- urls.append(m.group(1))
- if not urls:
- chksum = None # in case the metalink is about a different file
- f.close()
- self.setfromurls(urls)
- return chksum
-
- def setfrommirrorlist(self, mirrorlist):
- f = self.download(mirrorlist, False, None)
- if not f:
- return
- f = os.fdopen(f.dup(), 'r')
- urls = []
- for l in f.readline():
- l = l.strip()
- if l[0:6] == 'http://' or l[0:7] == 'https://':
- urls.append(l)
- self.setfromurls(urls)
- f.close()
-
- def download(self, file, uncompress, chksum, markincomplete=False):
- url = None
- if 'baseurl' not in self:
- if 'metalink' in self:
- if file != self['metalink']:
- metalinkchksum = self.setfrommetalink(self['metalink'])
- if file == 'repodata/repomd.xml' and metalinkchksum and not chksum:
- chksum = metalinkchksum
- else:
- url = file
- elif 'mirrorlist' in self:
- if file != self['mirrorlist']:
- self.setfrommirrorlist(self['mirrorlist'])
- else:
- url = file
- if not url:
- if 'baseurl' not in self:
- print("%s: no baseurl" % self.name)
- return None
- url = re.sub(r'/$', '', self['baseurl']) + '/' + file
- f = tempfile.TemporaryFile()
- st = subprocess.call(['curl', '-f', '-s', '-L', url], stdout=f.fileno())
- if os.lseek(f.fileno(), 0, os.SEEK_CUR) == 0 and (st == 0 or not chksum):
- return None
- os.lseek(f.fileno(), 0, os.SEEK_SET)
- if st:
- print("%s: download error %d" % (file, st))
- if markincomplete:
- self['incomplete'] = True
- return None
- if chksum:
- fchksum = solv.Chksum(chksum.type)
- if not fchksum:
- print("%s: unknown checksum type" % file)
- if markincomplete:
- self['incomplete'] = True
- return None
- fchksum.add_fd(f.fileno())
- if fchksum != chksum:
- print("%s: checksum mismatch" % file)
- if markincomplete:
- self['incomplete'] = True
- return None
- if uncompress:
- return solv.xfopen_fd(file, f.fileno())
- return solv.xfopen_fd(None, f.fileno())
-
- def usecachedrepo(self, ext, mark=False):
- try:
- repopath = self.cachepath(ext)
- f = open(repopath, 'rb')
- f.seek(-32, os.SEEK_END)
- fcookie = f.read(32)
- if len(fcookie) != 32:
- return False
- if not ext:
- cookie = self['cookie']
- else:
- cookie = self['extcookie']
- if cookie and fcookie != cookie:
- return False
- if self.type != 'system' and not ext:
- f.seek(-32 * 2, os.SEEK_END)
- fextcookie = f.read(32)
- if len(fextcookie) != 32:
- return False
- f.seek(0)
- f = solv.xfopen_fd('', f.fileno())
- flags = 0
- if ext:
- flags = solv.Repo.REPO_USE_LOADING|solv.Repo.REPO_EXTEND_SOLVABLES
- if ext != 'DL':
- flags |= solv.Repo.REPO_LOCALPOOL
- if not self.handle.add_solv(f, flags):
- return False
- if self.type != 'system' and not ext:
- self['cookie'] = fcookie
- self['extcookie'] = fextcookie
- if mark:
- # no futimes in python?
- try:
- os.utime(repopath, None)
- except Exception:
- pass
- except IOError:
- return False
- return True
-
- def writecachedrepo(self, ext, repodata=None):
- if 'incomplete' in self:
- return
- tmpname = None
- try:
- if not os.path.isdir("/var/cache/solv"):
- os.mkdir("/var/cache/solv", 0o755)
- (fd, tmpname) = tempfile.mkstemp(prefix='.newsolv-', dir='/var/cache/solv')
- os.fchmod(fd, 0o444)
- f = os.fdopen(fd, 'wb+')
- f = solv.xfopen_fd(None, f.fileno())
- if not repodata:
- self.handle.write(f)
- elif ext:
- repodata.write(f)
- else: # rewrite_repos case, do not write stubs
- self.handle.write_first_repodata(f)
- f.flush()
- if self.type != 'system' and not ext:
- if not self['extcookie']:
- self['extcookie'] = self.calc_cookie_ext(f, self['cookie'])
- f.write(self['extcookie'])
- if not ext:
- f.write(self['cookie'])
- else:
- f.write(self['extcookie'])
- f.close
- if self.handle.iscontiguous():
- # switch to saved repo to activate paging and save memory
- nf = solv.xfopen(tmpname)
- if not ext:
- # main repo
- self.handle.empty()
- flags = solv.Repo.SOLV_ADD_NO_STUBS
- if repodata:
- flags = 0 # rewrite repos case, recreate stubs
- if not self.handle.add_solv(nf, flags):
- sys.exit("internal error, cannot reload solv file")
- else:
- # extension repodata
- # need to extend to repo boundaries, as this is how
- # repodata.write() has written the data
- repodata.extend_to_repo()
- flags = solv.Repo.REPO_EXTEND_SOLVABLES
- if ext != 'DL':
- flags |= solv.Repo.REPO_LOCALPOOL
- repodata.add_solv(nf, flags)
- os.rename(tmpname, self.cachepath(ext))
- except (OSError, IOError):
- if tmpname:
- os.unlink(tmpname)
-
- def updateaddedprovides(self, addedprovides):
- if 'incomplete' in self:
- return
- if not hasattr(self, 'handle'):
- return
- if self.handle.isempty():
- return
- # make sure there's just one real repodata with extensions
- repodata = self.handle.first_repodata()
- if not repodata:
- return
- oldaddedprovides = repodata.lookup_idarray(solv.SOLVID_META, solv.REPOSITORY_ADDEDFILEPROVIDES)
- if not set(addedprovides) <= set(oldaddedprovides):
- for id in addedprovides:
- repodata.add_idarray(solv.SOLVID_META, solv.REPOSITORY_ADDEDFILEPROVIDES, id)
- repodata.internalize()
- self.writecachedrepo(None, repodata)
-
- def packagespath(self):
- return ''
-
- def add_ext_keys(self, ext, repodata, handle):
- if ext == 'DL':
- repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOSITORY_DELTAINFO)
- repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_FLEXARRAY)
- elif ext == 'DU':
- repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_DISKUSAGE)
- repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRNUMNUMARRAY)
- elif ext == 'FL':
- repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_FILELIST)
- repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRSTRARRAY)
- else:
- for langtag, langtagtype in [
- (solv.SOLVABLE_SUMMARY, solv.REPOKEY_TYPE_STR),
- (solv.SOLVABLE_DESCRIPTION, solv.REPOKEY_TYPE_STR),
- (solv.SOLVABLE_EULA, solv.REPOKEY_TYPE_STR),
- (solv.SOLVABLE_MESSAGEINS, solv.REPOKEY_TYPE_STR),
- (solv.SOLVABLE_MESSAGEDEL, solv.REPOKEY_TYPE_STR),
- (solv.SOLVABLE_CATEGORY, solv.REPOKEY_TYPE_ID)
- ]:
- repodata.add_idarray(handle, solv.REPOSITORY_KEYS, self.handle.pool.id2langid(langtag, ext, 1))
- repodata.add_idarray(handle, solv.REPOSITORY_KEYS, langtagtype)
-
-
-class repo_repomd(repo_generic):
- def load(self, pool):
- if super(repo_repomd, self).load(pool):
- return True
- sys.stdout.write("rpmmd repo '%s': " % self.name)
- sys.stdout.flush()
- f = self.download("repodata/repomd.xml", False, None, None)
- if not f:
- print("no repomd.xml file, skipped")
- self.handle.free(True)
- del self.handle
- return False
- self['cookie'] = self.calc_cookie_fp(f)
- if self.usecachedrepo(None, True):
- print("cached")
- return True
- self.handle.add_repomdxml(f, 0)
- print("fetching")
- (filename, filechksum) = self.find('primary')
- if filename:
- f = self.download(filename, True, filechksum, True)
- if f:
- self.handle.add_rpmmd(f, None, 0)
- if 'incomplete' in self:
- return False # hopeless, need good primary
- (filename, filechksum) = self.find('updateinfo')
- if filename:
- f = self.download(filename, True, filechksum, True)
- if f:
- self.handle.add_updateinfoxml(f, 0)
- self.add_exts()
- self.writecachedrepo(None)
- # must be called after writing the repo
- self.handle.create_stubs()
- return True
-
- def find(self, what):
- di = self.handle.Dataiterator_meta(solv.REPOSITORY_REPOMD_TYPE, what, solv.Dataiterator.SEARCH_STRING)
- di.prepend_keyname(solv.REPOSITORY_REPOMD)
- for d in di:
- dp = d.parentpos()
- filename = dp.lookup_str(solv.REPOSITORY_REPOMD_LOCATION)
- chksum = dp.lookup_checksum(solv.REPOSITORY_REPOMD_CHECKSUM)
- if filename and not chksum:
- print("no %s file checksum!" % filename)
- filename = None
- chksum = None
- if filename:
- return (filename, chksum)
- return (None, None)
-
- def add_ext(self, repodata, what, ext):
- filename, chksum = self.find(what)
- if not filename and what == 'deltainfo':
- filename, chksum = self.find('prestodelta')
- if not filename:
- return
- handle = repodata.new_handle()
- repodata.set_poolstr(handle, solv.REPOSITORY_REPOMD_TYPE, what)
- repodata.set_str(handle, solv.REPOSITORY_REPOMD_LOCATION, filename)
- repodata.set_checksum(handle, solv.REPOSITORY_REPOMD_CHECKSUM, chksum)
- self.add_ext_keys(ext, repodata, handle)
- repodata.add_flexarray(solv.SOLVID_META, solv.REPOSITORY_EXTERNAL, handle)
-
- def add_exts(self):
- repodata = self.handle.add_repodata(0)
- self.add_ext(repodata, 'deltainfo', 'DL')
- self.add_ext(repodata, 'filelists', 'FL')
- repodata.internalize()
-
- def load_ext(self, repodata):
- repomdtype = repodata.lookup_str(solv.SOLVID_META, solv.REPOSITORY_REPOMD_TYPE)
- if repomdtype == 'filelists':
- ext = 'FL'
- elif repomdtype == 'deltainfo':
- ext = 'DL'
- else:
- return False
- sys.stdout.write("[%s:%s: " % (self.name, ext))
- if self.usecachedrepo(ext):
- sys.stdout.write("cached]\n")
- sys.stdout.flush()
- return True
- sys.stdout.write("fetching]\n")
- sys.stdout.flush()
- filename = repodata.lookup_str(solv.SOLVID_META, solv.REPOSITORY_REPOMD_LOCATION)
- filechksum = repodata.lookup_checksum(solv.SOLVID_META, solv.REPOSITORY_REPOMD_CHECKSUM)
- f = self.download(filename, True, filechksum)
- if not f:
- return False
- if ext == 'FL':
- self.handle.add_rpmmd(f, 'FL', solv.Repo.REPO_USE_LOADING|solv.Repo.REPO_EXTEND_SOLVABLES|solv.Repo.REPO_LOCALPOOL)
- elif ext == 'DL':
- self.handle.add_deltainfoxml(f, solv.Repo.REPO_USE_LOADING)
- self.writecachedrepo(ext, repodata)
- return True
-
-class repo_susetags(repo_generic):
- def load(self, pool):
- if super(repo_susetags, self).load(pool):
- return True
- sys.stdout.write("susetags repo '%s': " % self.name)
- sys.stdout.flush()
- f = self.download("content", False, None, None)
- if not f:
- print("no content file, skipped")
- self.handle.free(True)
- del self.handle
- return False
- self['cookie'] = self.calc_cookie_fp(f)
- if self.usecachedrepo(None, True):
- print("cached")
- return True
- self.handle.add_content(f, 0)
- print("fetching")
- defvendorid = self.handle.meta.lookup_id(solv.SUSETAGS_DEFAULTVENDOR)
- descrdir = self.handle.meta.lookup_str(solv.SUSETAGS_DESCRDIR)
- if not descrdir:
- descrdir = "suse/setup/descr"
- (filename, filechksum) = self.find('packages.gz')
- if not filename:
- (filename, filechksum) = self.find('packages')
- if filename:
- f = self.download(descrdir + '/' + filename, True, filechksum, True)
- if f:
- self.handle.add_susetags(f, defvendorid, None, solv.Repo.REPO_NO_INTERNALIZE|solv.Repo.SUSETAGS_RECORD_SHARES)
- (filename, filechksum) = self.find('packages.en.gz')
- if not filename:
- (filename, filechksum) = self.find('packages.en')
- if filename:
- f = self.download(descrdir + '/' + filename, True, filechksum, True)
- if f:
- self.handle.add_susetags(f, defvendorid, None, solv.Repo.REPO_NO_INTERNALIZE|solv.Repo.REPO_REUSE_REPODATA|solv.Repo.REPO_EXTEND_SOLVABLES)
- self.handle.internalize()
- self.add_exts()
- self.writecachedrepo(None)
- # must be called after writing the repo
- self.handle.create_stubs()
- return True
-
- def find(self, what):
- di = self.handle.Dataiterator_meta(solv.SUSETAGS_FILE_NAME, what, solv.Dataiterator.SEARCH_STRING)
- di.prepend_keyname(solv.SUSETAGS_FILE)
- for d in di:
- dp = d.parentpos()
- chksum = dp.lookup_checksum(solv.SUSETAGS_FILE_CHECKSUM)
- return (what, chksum)
- return (None, None)
-
- def add_ext(self, repodata, what, ext):
- (filename, chksum) = self.find(what)
- if not filename:
- return
- handle = repodata.new_handle()
- repodata.set_str(handle, solv.SUSETAGS_FILE_NAME, filename)
- if chksum:
- repodata.set_checksum(handle, solv.SUSETAGS_FILE_CHECKSUM, chksum)
- self.add_ext_keys(ext, repodata, handle)
- repodata.add_flexarray(solv.SOLVID_META, solv.REPOSITORY_EXTERNAL, handle)
-
- def add_exts(self):
- repodata = self.handle.add_repodata(0)
- di = self.handle.Dataiterator_meta(solv.SUSETAGS_FILE_NAME, None, 0)
- di.prepend_keyname(solv.SUSETAGS_FILE)
- for d in di:
- filename = d.str
- if not filename:
- continue
- if filename[0:9] != "packages.":
- continue
- if len(filename) == 11 and filename != "packages.gz":
- ext = filename[9:11]
- elif filename[11:12] == ".":
- ext = filename[9:11]
- else:
- continue
- if ext == "en":
- continue
- self.add_ext(repodata, filename, ext)
- repodata.internalize()
-
- def load_ext(self, repodata):
- filename = repodata.lookup_str(solv.SOLVID_META, solv.SUSETAGS_FILE_NAME)
- ext = filename[9:11]
- sys.stdout.write("[%s:%s: " % (self.name, ext))
- if self.usecachedrepo(ext):
- sys.stdout.write("cached]\n")
- sys.stdout.flush()
- return True
- sys.stdout.write("fetching]\n")
- sys.stdout.flush()
- defvendorid = self.handle.meta.lookup_id(solv.SUSETAGS_DEFAULTVENDOR)
- descrdir = self.handle.meta.lookup_str(solv.SUSETAGS_DESCRDIR)
- if not descrdir:
- descrdir = "suse/setup/descr"
- filechksum = repodata.lookup_checksum(solv.SOLVID_META, solv.SUSETAGS_FILE_CHECKSUM)
- f = self.download(descrdir + '/' + filename, True, filechksum)
- if not f:
- return False
- flags = solv.Repo.REPO_USE_LOADING|solv.Repo.REPO_EXTEND_SOLVABLES
- if ext != 'DL':
- flags |= solv.Repo.REPO_LOCALPOOL
- self.handle.add_susetags(f, defvendorid, ext, flags)
- self.writecachedrepo(ext, repodata)
- return True
-
- def packagespath(self):
- datadir = repo.handle.meta.lookup_str(solv.SUSETAGS_DATADIR)
- if not datadir:
- datadir = 'suse'
- return datadir + '/'
-
-class repo_unknown(repo_generic):
- def load(self, pool):
- print("unsupported repo '%s': skipped" % self.name)
- return False
-
-class repo_system(repo_generic):
- def load(self, pool):
- self.handle = pool.add_repo(self.name)
- self.handle.appdata = self
- pool.installed = self.handle
- sys.stdout.write("rpm database: ")
- self['cookie'] = self.calc_cookie_file("/var/lib/rpm/Packages")
- if self.usecachedrepo(None):
- print("cached")
- return True
- print("reading")
- if hasattr(self.handle.__class__, 'add_products'):
- self.handle.add_products("/etc/products.d", solv.Repo.REPO_NO_INTERNALIZE)
- f = solv.xfopen(self.cachepath())
- self.handle.add_rpmdb_reffp(f, solv.Repo.REPO_REUSE_REPODATA)
- self.writecachedrepo(None)
- return True
-
-class repo_cmdline(repo_generic):
- def load(self, pool):
- self.handle = pool.add_repo(self.name)
- self.handle.appdata = self
- return True
-
-def load_stub(repodata):
- repo = repodata.repo.appdata
- if repo:
- return repo.load_ext(repodata)
- return False
-
-
-parser = OptionParser(usage="usage: solv.py [options] COMMAND")
-parser.add_option('-r', '--repo', action="append", type="string", dest="repos", help="limit to specified repositories")
-parser.add_option('--best', action="store_true", dest="best", help="force installation/update to best packages")
-parser.add_option('--clean', action="store_true", dest="clean", help="delete no longer needed packages")
-(options, args) = parser.parse_args()
-if not args:
- parser.print_help(sys.stderr)
- sys.exit(1)
-
-cmd = args[0]
-args = args[1:]
-
-cmdabbrev = {'ls': 'list', 'in': 'install', 'rm': 'erase', 've': 'verify', 'se': 'search'}
-if cmd in cmdabbrev:
- cmd = cmdabbrev[cmd]
-
-cmdactionmap = {
- 'install': solv.Job.SOLVER_INSTALL,
- 'erase': solv.Job.SOLVER_ERASE,
- 'up': solv.Job.SOLVER_UPDATE,
- 'dup': solv.Job.SOLVER_DISTUPGRADE,
- 'verify': solv.Job.SOLVER_VERIFY,
- 'list': 0,
- 'info': 0
-}
-
-# read all repo configs
-repos = []
-reposdirs = []
-if os.path.isdir("/etc/zypp/repos.d"):
- reposdirs = [ "/etc/zypp/repos.d" ]
-else:
- reposdirs = [ "/etc/yum/repos.d" ]
-
-for reposdir in reposdirs:
- if not os.path.isdir(reposdir):
- continue
- for reponame in sorted(glob.glob('%s/*.repo' % reposdir)):
- cfg = INIConfig(open(reponame))
- for alias in cfg:
- repoattr = {'enabled': 0, 'priority': 99, 'autorefresh': 1, 'type': 'rpm-md', 'metadata_expire': 900}
- for k in cfg[alias]:
- repoattr[k] = cfg[alias][k]
- if 'mirrorlist' in repoattr and 'metalink' not in repoattr:
- if repoattr['mirrorlist'].find('/metalink'):
- repoattr['metalink'] = repoattr['mirrorlist']
- del repoattr['mirrorlist']
- if repoattr['type'] == 'rpm-md':
- repo = repo_repomd(alias, 'repomd', repoattr)
- elif repoattr['type'] == 'yast2':
- repo = repo_susetags(alias, 'susetags', repoattr)
- else:
- repo = repo_unknown(alias, 'unknown', repoattr)
- repos.append(repo)
-
-pool = solv.Pool()
-pool.setarch()
-pool.set_loadcallback(load_stub)
-
-# now load all enabled repos into the pool
-sysrepo = repo_system('@System', 'system')
-sysrepo.load(pool)
-for repo in repos:
- if int(repo['enabled']):
- repo.load(pool)
-
-repofilter = None
-if options.repos:
- for reponame in options.repos:
- mrepos = [ repo for repo in repos if repo.name == reponame ]
- if not mrepos:
- print("no repository matches '%s'" % reponame)
- sys.exit(1)
- repo = mrepos[0]
- if hasattr(repo, 'handle'):
- if not repofilter:
- repofilter = pool.Selection()
- repofilter.add(repo.handle.Selection(solv.Job.SOLVER_SETVENDOR))
-
-if cmd == 'search':
- pool.createwhatprovides()
- sel = pool.Selection()
- di = pool.Dataiterator(solv.SOLVABLE_NAME, args[0], solv.Dataiterator.SEARCH_SUBSTRING|solv.Dataiterator.SEARCH_NOCASE)
- for d in di:
- sel.add_raw(solv.Job.SOLVER_SOLVABLE, d.solvid)
- if repofilter:
- sel.filter(repofilter)
- for s in sel.solvables():
- print(" - %s [%s]: %s" % (s, s.repo.name, s.lookup_str(solv.SOLVABLE_SUMMARY)))
- sys.exit(0)
-
-if cmd not in cmdactionmap:
- print("unknown command %s" % cmd)
- sys.exit(1)
-
-cmdlinerepo = None
-if cmd == 'list' or cmd == 'info' or cmd == 'install':
- for arg in args:
- if arg.endswith(".rpm") and os.access(arg, os.R_OK):
- if not cmdlinerepo:
- cmdlinerepo = repo_cmdline('@commandline', 'cmdline')
- cmdlinerepo.load(pool)
- cmdlinerepo['packages'] = {}
- s = cmdlinerepo.handle.add_rpm(arg, solv.Repo.REPO_REUSE_REPODATA|solv.Repo.REPO_NO_INTERNALIZE)
- if not s:
- print(pool.errstr)
- sys.exit(1)
- cmdlinerepo['packages'][arg] = s
- if cmdlinerepo:
- cmdlinerepo.handle.internalize()
-
-addedprovides = pool.addfileprovides_queue()
-if addedprovides:
- sysrepo.updateaddedprovides(addedprovides)
- for repo in repos:
- repo.updateaddedprovides(addedprovides)
-
-pool.createwhatprovides()
-
-# convert arguments into jobs
-jobs = []
-for arg in args:
- if cmdlinerepo and arg in cmdlinerepo['packages']:
- jobs.append(pool.Job(solv.Job.SOLVER_SOLVABLE, cmdlinerepo['packages'][arg].id))
- else:
- flags = solv.Selection.SELECTION_NAME|solv.Selection.SELECTION_PROVIDES|solv.Selection.SELECTION_GLOB
- flags |= solv.Selection.SELECTION_CANON|solv.Selection.SELECTION_DOTARCH|solv.Selection.SELECTION_REL
- if len(arg) and arg[0] == '/':
- flags |= solv.Selection.SELECTION_FILELIST
- if cmd == 'erase':
- flags |= solv.Selection.SELECTION_INSTALLED_ONLY
- sel = pool.select(arg, flags)
- if repofilter:
- sel.filter(repofilter)
- if sel.isempty():
- sel = pool.select(arg, flags | solv.Selection.SELECTION_NOCASE)
- if repofilter:
- sel.filter(repofilter)
- if not sel.isempty():
- print("[ignoring case for '%s']" % arg)
- if sel.isempty():
- print("nothing matches '%s'" % arg)
- sys.exit(1)
- if sel.flags() & solv.Selection.SELECTION_FILELIST:
- print("[using file list match for '%s']" % arg)
- if sel.flags() & solv.Selection.SELECTION_PROVIDES:
- print("[using capability match for '%s']" % arg)
- jobs += sel.jobs(cmdactionmap[cmd])
-
-if not jobs and (cmd == 'up' or cmd == 'dup' or cmd == 'verify' or repofilter):
- sel = pool.Selection_all()
- if repofilter:
- sel.filter(repofilter)
- jobs += sel.jobs(cmdactionmap[cmd])
-
-if not jobs:
- print("no package matched.")
- sys.exit(1)
-
-if cmd == 'list' or cmd == 'info':
- for job in jobs:
- for s in job.solvables():
- if cmd == 'info':
- print("Name: %s" % s)
- print("Repo: %s" % s.repo)
- print("Summary: %s" % s.lookup_str(solv.SOLVABLE_SUMMARY))
- str = s.lookup_str(solv.SOLVABLE_URL)
- if str:
- print("Url: %s" % str)
- str = s.lookup_str(solv.SOLVABLE_LICENSE)
- if str:
- print("License: %s" % str)
- print("Description:\n%s" % s.lookup_str(solv.SOLVABLE_DESCRIPTION))
- print('')
- else:
- print(" - %s [%s]" % (s, s.repo))
- print(" %s" % s.lookup_str(solv.SOLVABLE_SUMMARY))
- sys.exit(0)
-
-# up magic: use install instead of update if no installed package matches
-for job in jobs:
- if cmd == 'up' and job.isemptyupdate():
- job.how ^= solv.Job.SOLVER_UPDATE ^ solv.Job.SOLVER_INSTALL
- if options.best:
- job.how |= solv.Job.SOLVER_FORCEBEST
- if options.clean:
- job.how |= solv.Job.SOLVER_CLEANDEPS
-
-#pool.set_debuglevel(2)
-solver = pool.Solver()
-solver.set_flag(solv.Solver.SOLVER_FLAG_SPLITPROVIDES, 1);
-if cmd == 'erase':
- solver.set_flag(solv.Solver.SOLVER_FLAG_ALLOW_UNINSTALL, 1);
-
-while True:
- problems = solver.solve(jobs)
- if not problems:
- break
- for problem in problems:
- print("Problem %d/%d:" % (problem.id, len(problems)))
- print(problem)
- solutions = problem.solutions()
- for solution in solutions:
- print(" Solution %d:" % solution.id)
- elements = solution.elements(True)
- for element in elements:
- print(" - %s" % element.str())
- print('')
- sol = ''
- while not (sol == 's' or sol == 'q' or (sol.isdigit() and int(sol) >= 1 and int(sol) <= len(solutions))):
- sys.stdout.write("Please choose a solution: ")
- sys.stdout.flush()
- sol = sys.stdin.readline().strip()
- if sol == 's':
- continue # skip problem
- if sol == 'q':
- sys.exit(1)
- solution = solutions[int(sol) - 1]
- for element in solution.elements():
- newjob = element.Job()
- if element.type == solv.Solver.SOLVER_SOLUTION_JOB:
- jobs[element.jobidx] = newjob
- else:
- if newjob and newjob not in jobs:
- jobs.append(newjob)
-
-# no problems, show transaction
-trans = solver.transaction()
-del solver
-if trans.isempty():
- print("Nothing to do.")
- sys.exit(0)
-print('')
-print("Transaction summary:")
-print('')
-for cl in trans.classify(solv.Transaction.SOLVER_TRANSACTION_SHOW_OBSOLETES | solv.Transaction.SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE):
- if cl.type == solv.Transaction.SOLVER_TRANSACTION_ERASE:
- print("%d erased packages:" % cl.count)
- elif cl.type == solv.Transaction.SOLVER_TRANSACTION_INSTALL:
- print("%d installed packages:" % cl.count)
- elif cl.type == solv.Transaction.SOLVER_TRANSACTION_REINSTALLED:
- print("%d reinstalled packages:" % cl.count)
- elif cl.type == solv.Transaction.SOLVER_TRANSACTION_DOWNGRADED:
- print("%d downgraded packages:" % cl.count)
- elif cl.type == solv.Transaction.SOLVER_TRANSACTION_CHANGED:
- print("%d changed packages:" % cl.count)
- elif cl.type == solv.Transaction.SOLVER_TRANSACTION_UPGRADED:
- print("%d upgraded packages:" % cl.count)
- elif cl.type == solv.Transaction.SOLVER_TRANSACTION_VENDORCHANGE:
- print("%d vendor changes from '%s' to '%s':" % (cl.count, cl.fromstr, cl.tostr))
- elif cl.type == solv.Transaction.SOLVER_TRANSACTION_ARCHCHANGE:
- print("%d arch changes from '%s' to '%s':" % (cl.count, cl.fromstr, cl.tostr))
- else:
- continue
- for p in cl.solvables():
- if cl.type == solv.Transaction.SOLVER_TRANSACTION_UPGRADED or cl.type == solv.Transaction.SOLVER_TRANSACTION_DOWNGRADED:
- op = trans.othersolvable(p)
- print(" - %s -> %s" % (p, op))
- else:
- print(" - %s" % p)
- print('')
-print("install size change: %d K" % trans.calc_installsizechange())
-print('')
-
-while True:
- sys.stdout.write("OK to continue (y/n)? ")
- sys.stdout.flush()
- yn = sys.stdin.readline().strip()
- if yn == 'y': break
- if yn == 'n' or yn == 'q': sys.exit(1)
-newpkgs = trans.newsolvables()
-newpkgsfp = {}
-if newpkgs:
- downloadsize = 0
- for p in newpkgs:
- downloadsize += p.lookup_num(solv.SOLVABLE_DOWNLOADSIZE)
- print("Downloading %d packages, %d K" % (len(newpkgs), downloadsize / 1024))
- for p in newpkgs:
- repo = p.repo.appdata
- location, medianr = p.lookup_location()
- if not location:
- continue
- if repo.type == 'commandline':
- f = solv.xfopen(location)
- if not f:
- sys.exit("\n%s: %s not found" % location)
- newpkgsfp[p.id] = f
- continue
- if not sysrepo.handle.isempty() and os.access('/usr/bin/applydeltarpm', os.X_OK):
- pname = p.name
- di = p.repo.Dataiterator_meta(solv.DELTA_PACKAGE_NAME, pname, solv.Dataiterator.SEARCH_STRING)
- di.prepend_keyname(solv.REPOSITORY_DELTAINFO)
- for d in di:
- dp = d.parentpos()
- if dp.lookup_id(solv.DELTA_PACKAGE_EVR) != p.evrid or dp.lookup_id(solv.DELTA_PACKAGE_ARCH) != p.archid:
- continue
- baseevrid = dp.lookup_id(solv.DELTA_BASE_EVR)
- candidate = None
- for installedp in pool.whatprovides(p.nameid):
- if installedp.isinstalled() and installedp.nameid == p.nameid and installedp.archid == p.archid and installedp.evrid == baseevrid:
- candidate = installedp
- if not candidate:
- continue
- seq = dp.lookup_deltaseq()
- st = subprocess.call(['/usr/bin/applydeltarpm', '-a', p.arch, '-c', '-s', seq])
- if st:
- continue
- chksum = dp.lookup_checksum(solv.DELTA_CHECKSUM)
- if not chksum:
- continue
- dloc, dmedianr = dp.lookup_deltalocation()
- dloc = repo.packagespath() + dloc
- f = repo.download(dloc, False, chksum)
- if not f:
- continue
- nf = tempfile.TemporaryFile()
- nf = os.dup(nf.fileno()) # get rid of CLOEXEC
- f.cloexec(0)
- st = subprocess.call(['/usr/bin/applydeltarpm', '-a', p.arch, "/dev/fd/%d" % f.fileno(), "/dev/fd/%d" % nf])
- if st:
- os.close(nf)
- continue
- os.lseek(nf, 0, os.SEEK_SET)
- newpkgsfp[p.id] = solv.xfopen_fd("", nf)
- os.close(nf)
- break
- if p.id in newpkgsfp:
- sys.stdout.write("d")
- sys.stdout.flush()
- continue
-
- chksum = p.lookup_checksum(solv.SOLVABLE_CHECKSUM)
- location = repo.packagespath() + location
- f = repo.download(location, False, chksum)
- if not f:
- sys.exit("\n%s: %s not found in repository" % (repo.name, location))
- newpkgsfp[p.id] = f
- sys.stdout.write(".")
- sys.stdout.flush()
- print('')
-print("Committing transaction:")
-print('')
-ts = rpm.TransactionSet('/')
-ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
-erasenamehelper = {}
-for p in trans.steps():
- type = trans.steptype(p, solv.Transaction.SOLVER_TRANSACTION_RPM_ONLY)
- if type == solv.Transaction.SOLVER_TRANSACTION_ERASE:
- rpmdbid = p.lookup_num(solv.RPM_RPMDBID)
- erasenamehelper[p.name] = p
- if not rpmdbid:
- sys.exit("\ninternal error: installed package %s has no rpmdbid\n" % p)
- ts.addErase(rpmdbid)
- elif type == solv.Transaction.SOLVER_TRANSACTION_INSTALL:
- f = newpkgsfp[p.id]
- h = ts.hdrFromFdno(f.fileno())
- os.lseek(f.fileno(), 0, os.SEEK_SET)
- ts.addInstall(h, p, 'u')
- elif type == solv.Transaction.SOLVER_TRANSACTION_MULTIINSTALL:
- f = newpkgsfp[p.id]
- h = ts.hdrFromFdno(f.fileno())
- os.lseek(f.fileno(), 0, os.SEEK_SET)
- ts.addInstall(h, p, 'i')
-checkproblems = ts.check()
-if checkproblems:
- print(checkproblems)
- sys.exit("Sorry.")
-ts.order()
-def runCallback(reason, amount, total, p, d):
- if reason == rpm.RPMCALLBACK_INST_OPEN_FILE:
- return newpkgsfp[p.id].fileno()
- if reason == rpm.RPMCALLBACK_INST_START:
- print("install %s" % p)
- if reason == rpm.RPMCALLBACK_UNINST_START:
- # argh, p is just the name of the package
- if p in erasenamehelper:
- p = erasenamehelper[p]
- print("erase %s" % p)
-runproblems = ts.run(runCallback, '')
-if runproblems:
- print(runproblems)
- sys.exit(1)
-sys.exit(0)
-
-# vim: sw=4 et
+++ /dev/null
-#!/usr/bin/ruby
-
-require 'solv'
-require 'rubygems'
-require 'inifile'
-require 'tempfile'
-
-class Repo_generic
- def initialize(name, type, attribs = {})
- @name = name
- @type = type
- @attribs = attribs.dup
- @incomplete = false
- end
-
- def enabled?
- return @attribs['enabled'].to_i != 0
- end
-
- def autorefresh?
- return @attribs['autorefresh'].to_i != 0
- end
-
- def id
- return @handle ? @handle.id : 0
- end
-
- def calc_cookie_fp(f)
- chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
- chksum.add("1.1")
- chksum.add_fp(f)
- return chksum.raw
- end
-
- def calc_cookie_file(filename)
- chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
- chksum.add("1.1")
- chksum.add_stat(filename)
- return chksum.raw
- end
-
- def calc_cookie_ext(f, cookie)
- chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
- chksum.add("1.1")
- chksum.add(cookie)
- chksum.add_fstat(f.fileno)
- return chksum.raw()
- end
-
- def cachepath(ext = nil)
- path = @name.sub(/^\./, '_')
- path += ext ? "_#{ext}.solvx" : '.solv'
- return '/var/cache/solv/' + path.gsub(/\//, '_')
- end
-
- def load(pool)
- @handle = pool.add_repo(@name)
- @handle.appdata = self
- @handle.priority = 99 - @attribs['priority'].to_i if @attribs['priority']
- dorefresh = autorefresh?
- if dorefresh
- begin
- s = File.stat(cachepath)
- dorefresh = false if s && (@attribs['metadata_expire'].to_i == -1 || Time.now - s.mtime < @attribs['metadata_expire'].to_i)
- rescue SystemCallError
- end
- end
- @cookie = nil
- @extcookie = nil
- if !dorefresh && usecachedrepo(nil)
- puts "repo: '#{@name}' cached"
- return true
- end
- return false
- end
-
- def load_ext(repodata)
- return false
- end
-
- def download(file, uncompress, chksum, markincomplete = false)
- url = @attribs['baseurl']
- if !url
- puts "%{@name}: no baseurl"
- return nil
- end
- url = url.sub(/\/$/, '') + "/#{file}"
- f = Tempfile.new('rbsolv')
- f.unlink
- st = system('curl', '-f', '-s', '-L', '-o', "/dev/fd/" + f.fileno.to_s, '--', url)
- return nil if f.stat.size == 0 && (st || !chksum)
- if !st
- puts "#{file}: download error #{$? >> 8}"
- @incomplete = true if markincomplete
- return nil
- end
- if chksum
- fchksum = Solv::Chksum.new(chksum.type)
- fchksum.add_fd(f.fileno)
- if !fchksum == chksum
- puts "#{file}: checksum error"
- @incomplete = true if markincomplete
- return nil
- end
- end
- rf = nil
- if uncompress
- rf = Solv::xfopen_fd(file, f.fileno)
- else
- rf = Solv::xfopen_fd('', f.fileno)
- end
- f.close
- return rf
- end
-
- def usecachedrepo(ext, mark = false)
- cookie = ext ? @extcookie : @cookie
- begin
- repopath = cachepath(ext)
- f = File.new(repopath, "r")
- f.sysseek(-32, IO::SEEK_END)
- fcookie = f.sysread(32)
- return false if fcookie.length != 32
- return false if cookie && fcookie != cookie
- if !ext && @type != 'system'
- f.sysseek(-32 * 2, IO::SEEK_END)
- fextcookie = f.sysread(32)
- return false if fextcookie.length != 32
- end
- f.sysseek(0, IO::SEEK_SET)
- nf = Solv::xfopen_fd('', f.fileno)
- f.close
- flags = ext ? Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES : 0
- flags |= Solv::Repo::REPO_LOCALPOOL if ext && ext != 'DL'
- if ! @handle.add_solv(nf, flags)
- nf.close
- return false
- end
- nf.close()
- @cookie = fcookie unless ext
- @extcookie = fextcookie if !ext && @type != 'system'
- now = Time.now
- begin
- File::utime(now, now, repopath) if mark
- rescue SystemCallError
- end
- return true
- rescue SystemCallError
- return false
- end
- return true
- end
-
- def writecachedrepo(ext, repodata = nil)
- return if @incomplete
- begin
- Dir::mkdir("/var/cache/solv", 0755) unless FileTest.directory?("/var/cache/solv")
- f = Tempfile.new('.newsolv-', '/var/cache/solv')
- f.chmod(0444)
- sf = Solv::xfopen_fd('', f.fileno)
- if !repodata
- @handle.write(sf)
- elsif ext
- repodata.write(sf)
- else
- @handle.write_first_repodata(sf)
- end
- sf.close
- f.sysseek(0, IO::SEEK_END)
- if @type != 'system' && !ext
- @extcookie = calc_cookie_ext(f, @cookie) unless @extcookie
- f.syswrite(@extcookie)
- end
- f.syswrite(ext ? @extcookie : @cookie)
- f.close
- if @handle.iscontiguous?
- sf = Solv::xfopen(f.path)
- if sf
- if !ext
- @handle.empty()
- abort("internal error, cannot reload solv file") unless @handle.add_solv(sf, repodata ? 0 : Solv::Repo::SOLV_ADD_NO_STUBS)
- else
- repodata.extend_to_repo()
- flags = Solv::Repo::REPO_EXTEND_SOLVABLES
- flags |= Solv::Repo::REPO_LOCALPOOL if ext != 'DL'
- repodata.add_solv(sf, flags)
- end
- sf.close
- end
- end
- File.rename(f.path, cachepath(ext))
- f.unlink
- return true
- rescue SystemCallError
- return false
- end
- end
-
- def updateaddedprovides(addedprovides)
- return if @incomplete
- return unless @handle && !@handle.isempty?
- repodata = @handle.first_repodata()
- return unless repodata
- oldaddedprovides = repodata.lookup_idarray(Solv::SOLVID_META, Solv::REPOSITORY_ADDEDFILEPROVIDES)
- return if (oldaddedprovides | addedprovides) == oldaddedprovides
- for id in addedprovides
- repodata.add_idarray(Solv::SOLVID_META, Solv::REPOSITORY_ADDEDFILEPROVIDES, id)
- end
- repodata.internalize()
- writecachedrepo(nil, repodata)
- end
-
- def packagespath()
- return ''
- end
-
- @@langtags = {
- Solv::SOLVABLE_SUMMARY => Solv::REPOKEY_TYPE_STR,
- Solv::SOLVABLE_DESCRIPTION => Solv::REPOKEY_TYPE_STR,
- Solv::SOLVABLE_EULA => Solv::REPOKEY_TYPE_STR,
- Solv::SOLVABLE_MESSAGEINS => Solv::REPOKEY_TYPE_STR,
- Solv::SOLVABLE_MESSAGEDEL => Solv::REPOKEY_TYPE_STR,
- Solv::SOLVABLE_CATEGORY => Solv::REPOKEY_TYPE_ID,
- }
-
- def add_ext_keys(ext, repodata, h)
- if ext == 'DL'
- repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOSITORY_DELTAINFO)
- repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_FLEXARRAY)
- elsif ext == 'DU'
- repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_DISKUSAGE)
- repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRNUMNUMARRAY)
- elsif ext == 'FL'
- repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_FILELIST)
- repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRSTRARRAY)
- else
- @@langtags.sort.each do |langid, langtype|
- repodata.add_idarray(h, Solv::REPOSITORY_KEYS, @handle.pool.id2langid(langid, ext, true))
- repodata.add_idarray(h, Solv::REPOSITORY_KEYS, langtype)
- end
- end
- end
-end
-
-class Repo_rpmmd < Repo_generic
-
- def find(what)
- di = @handle.Dataiterator_meta(Solv::REPOSITORY_REPOMD_TYPE, what, Solv::Dataiterator::SEARCH_STRING)
- di.prepend_keyname(Solv::REPOSITORY_REPOMD)
- for d in di
- dp = d.parentpos()
- filename = dp.lookup_str(Solv::REPOSITORY_REPOMD_LOCATION)
- next unless filename
- checksum = dp.lookup_checksum(Solv::REPOSITORY_REPOMD_CHECKSUM)
- if !checksum
- puts "no #{filename} checksum!"
- return nil, nil
- end
- return filename, checksum
- end
- return nil, nil
- end
-
- def load(pool)
- return true if super(pool)
- print "rpmmd repo '#{@name}: "
- f = download("repodata/repomd.xml", false, nil, nil)
- if !f
- puts "no repomd.xml file, skipped"
- @handle.free(true)
- @handle = nil
- return false
- end
- @cookie = calc_cookie_fp(f)
- if usecachedrepo(nil, true)
- puts "cached"
- f.close
- return true
- end
- @handle.add_repomdxml(f, 0)
- f.close
- puts "fetching"
- filename, filechksum = find('primary')
- if filename
- f = download(filename, true, filechksum, true)
- if f
- @handle.add_rpmmd(f, nil, 0)
- f.close
- end
- return false if @incomplete
- end
- filename, filechksum = find('updateinfo')
- if filename
- f = download(filename, true, filechksum, true)
- if f
- @handle.add_updateinfoxml(f, 0)
- f.close
- end
- end
- add_exts()
- writecachedrepo(nil)
- @handle.create_stubs()
- return true
- end
-
- def add_ext(repodata, what, ext)
- filename, filechksum = find(what)
- filename, filechksum = find('prestodelta') if !filename && what == 'deltainfo'
- return unless filename
- h = repodata.new_handle()
- repodata.set_poolstr(h, Solv::REPOSITORY_REPOMD_TYPE, what)
- repodata.set_str(h, Solv::REPOSITORY_REPOMD_LOCATION, filename)
- repodata.set_checksum(h, Solv::REPOSITORY_REPOMD_CHECKSUM, filechksum)
- add_ext_keys(ext, repodata, h)
- repodata.add_flexarray(Solv::SOLVID_META, Solv::REPOSITORY_EXTERNAL, h)
- end
-
- def add_exts
- repodata = @handle.add_repodata(0)
- add_ext(repodata, 'deltainfo', 'DL')
- add_ext(repodata, 'filelists', 'FL')
- repodata.internalize()
- end
-
- def load_ext(repodata)
- repomdtype = repodata.lookup_str(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_TYPE)
- if repomdtype == 'filelists'
- ext = 'FL'
- elsif repomdtype == 'deltainfo'
- ext = 'DL'
- else
- return false
- end
- print "[#{@name}:#{ext}: "
- STDOUT.flush
- if usecachedrepo(ext)
- puts "cached]\n"
- return true
- end
- puts "fetching]\n"
- filename = repodata.lookup_str(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_LOCATION)
- filechksum = repodata.lookup_checksum(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_CHECKSUM)
- f = download(filename, true, filechksum)
- return false unless f
- if ext == 'FL'
- @handle.add_rpmmd(f, 'FL', Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES|Solv::Repo::REPO_LOCALPOOL)
- elsif ext == 'DL'
- @handle.add_deltainfoxml(f, Solv::Repo::REPO_USE_LOADING)
- end
- f.close
- writecachedrepo(ext, repodata)
- return true
- end
-
-end
-
-class Repo_susetags < Repo_generic
-
- def find(what)
- di = @handle.Dataiterator_meta(Solv::SUSETAGS_FILE_NAME, what, Solv::Dataiterator::SEARCH_STRING)
- di.prepend_keyname(Solv::SUSETAGS_FILE)
- for d in di
- dp = d.parentpos()
- checksum = dp.lookup_checksum(Solv::SUSETAGS_FILE_CHECKSUM)
- return what, checksum
- end
- return nil, nil
- end
-
- def load(pool)
- return true if super(pool)
- print "susetags repo '#{@name}: "
- f = download("content", false, nil, nil)
- if !f
- puts "no content file, skipped"
- @handle.free(true)
- @handle = nil
- return false
- end
- @cookie = calc_cookie_fp(f)
- if usecachedrepo(nil, true)
- puts "cached"
- f.close
- return true
- end
- @handle.add_content(f, 0)
- f.close
- puts "fetching"
- defvendorid = @handle.meta.lookup_id(Solv::SUSETAGS_DEFAULTVENDOR)
- descrdir = @handle.meta.lookup_str(Solv::SUSETAGS_DESCRDIR)
- descrdir = "suse/setup/descr" unless descrdir
- (filename, filechksum) = find('packages.gz')
- (filename, filechksum) = find('packages') unless filename
- if filename
- f = download("#{descrdir}/#{filename}", true, filechksum, true)
- if f
- @handle.add_susetags(f, defvendorid, nil, Solv::Repo::REPO_NO_INTERNALIZE|Solv::Repo::SUSETAGS_RECORD_SHARES)
- f.close
- (filename, filechksum) = find('packages.en.gz')
- (filename, filechksum) = find('packages.en') unless filename
- if filename
- f = download("#{descrdir}/#{filename}", true, filechksum, true)
- if f
- @handle.add_susetags(f, defvendorid, nil, Solv::Repo::REPO_NO_INTERNALIZE|Solv::Repo::REPO_REUSE_REPODATA|Solv::Repo::REPO_EXTEND_SOLVABLES)
- f.close
- end
- end
- @handle.internalize()
- end
- end
- add_exts()
- writecachedrepo(nil)
- @handle.create_stubs()
- return true
- end
-
- def add_ext(repodata, what, ext)
- (filename, filechksum) = find(what)
- h = repodata.new_handle()
- repodata.set_str(h, Solv::SUSETAGS_FILE_NAME, filename)
- repodata.set_checksum(h, Solv::SUSETAGS_FILE_CHECKSUM, filechksum)
- add_ext_keys(ext, repodata, h)
- repodata.add_flexarray(Solv::SOLVID_META, Solv::REPOSITORY_EXTERNAL, h)
- end
-
- def add_exts
- repodata = @handle.add_repodata(0)
- di = @handle.Dataiterator_meta(Solv::SUSETAGS_FILE_NAME, nil, 0)
- di.prepend_keyname(Solv::SUSETAGS_FILE)
- for d in di
- filename = d.str
- next unless filename && filename =~ /^packages\.(..)(?:\..*)$/
- next if $1 == 'en' || $1 == 'gz'
- add_ext(repodata, filename, $1)
- end
- repodata.internalize()
- end
-
- def load_ext(repodata)
- filename = repodata.lookup_str(Solv::SOLVID_META, Solv::SUSETAGS_FILE_NAME)
- ext = filename[9,2]
- print "[#{@name}:#{ext}: "
- STDOUT.flush
- if usecachedrepo(ext)
- puts "cached]\n"
- return true
- end
- puts "fetching]\n"
- defvendorid = @handle.meta.lookup_id(Solv::SUSETAGS_DEFAULTVENDOR)
- descrdir = @handle.meta.lookup_str(Solv::SUSETAGS_DESCRDIR)
- descrdir = "suse/setup/descr" unless descrdir
- filechksum = repodata.lookup_checksum(Solv::SOLVID_META, Solv::SUSETAGS_FILE_CHECKSUM)
- f = download("#{descrdir}/#{filename}", true, filechksum)
- return false unless f
- flags = Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES
- flags |= Solv::Repo::REPO_LOCALPOOL if ext != 'DL'
- @handle.add_susetags(f, defvendorid, ext, flags)
- f.close
- writecachedrepo(ext, repodata)
- return true
- end
-
- def packagespath()
- datadir = @handle.meta.lookup_str(Solv::SUSETAGS_DATADIR)
- datadir = "suse" unless datadir
- return datadir + '/'
- end
-end
-
-class Repo_unknown < Repo_generic
- def load(pool)
- puts "unsupported repo '#{@name}: skipped"
- return false
- end
-end
-
-class Repo_system < Repo_generic
- def load(pool)
- @handle = pool.add_repo(@name)
- @handle.appdata = self
- pool.installed = @handle
- print "rpm database: "
- @cookie = calc_cookie_file("/var/lib/rpm/Packages")
- if usecachedrepo(nil)
- puts "cached"
- return true
- end
- puts "reading"
- if @handle.respond_to? :add_products
- @handle.add_products("/etc/products.d", Solv::Repo::REPO_NO_INTERNALIZE)
- end
- f = Solv::xfopen(cachepath())
- @handle.add_rpmdb_reffp(f, Solv::Repo::REPO_REUSE_REPODATA)
- f.close if f
- writecachedrepo(nil)
- return true
- end
-end
-
-args = ARGV
-cmd = args.shift
-
-cmdabbrev = { 'ls' => 'list', 'in' => 'install', 'rm' => 'erase',
- 've' => 'verify', 'se' => 'search' }
-cmd = cmdabbrev[cmd] if cmdabbrev.has_key?(cmd)
-
-cmdactionmap = {
- 'install' => Solv::Job::SOLVER_INSTALL,
- 'erase' => Solv::Job::SOLVER_ERASE,
- 'up' => Solv::Job::SOLVER_UPDATE,
- 'dup' => Solv::Job::SOLVER_DISTUPGRADE,
- 'verify' => Solv::Job::SOLVER_VERIFY,
- 'list' => 0,
- 'info' => 0,
-}
-
-repos = []
-reposdirs = []
-if FileTest.directory?('/etc/zypp/repos.d')
- reposdirs = [ '/etc/zypp/repos.d' ]
-else
- reposdirs = [ '/etc/yum/repos.d' ]
-end
-for reposdir in reposdirs do
- next unless FileTest.directory?(reposdir)
- for reponame in Dir["#{reposdir}/*.repo"].sort do
- cfg = IniFile.load(reponame)
- cfg.each_section do |ali|
- repoattr = { 'alias' => ali, 'enabled' => 0, 'priority' => 99, 'autorefresh' => 1, 'type' => 'rpm-md', 'metadata_expire' => 900}
- repoattr.update(cfg[ali])
- if repoattr['type'] == 'rpm-md'
- repo = Repo_rpmmd.new(ali, 'repomd', repoattr)
- elsif repoattr['type'] == 'yast2'
- repo = Repo_susetags.new(ali, 'susetags', repoattr)
- else
- repo = Repo_unknown.new(ali, 'unknown', repoattr)
- end
- repos.push(repo)
- end
- end
-end
-
-pool = Solv::Pool.new()
-pool.setarch()
-
-pool.set_loadcallback { |repodata|
- repo = repodata.repo.appdata
- repo ? repo.load_ext(repodata) : false
-}
-
-sysrepo = Repo_system.new('@System', 'system')
-sysrepo.load(pool)
-for repo in repos
- repo.load(pool) if repo.enabled?
-end
-
-if cmd == 'search'
- pool.createwhatprovides()
- sel = pool.Selection
- for di in pool.Dataiterator(Solv::SOLVABLE_NAME, args[0], Solv::Dataiterator::SEARCH_SUBSTRING | Solv::Dataiterator::SEARCH_NOCASE)
- sel.add_raw(Solv::Job::SOLVER_SOLVABLE, di.solvid)
- end
- for s in sel.solvables
- puts "- #{s.str} [#{s.repo.name}]: #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
- end
- exit
-end
-
-abort("unknown command '#{cmd}'\n") unless cmdactionmap.has_key?(cmd)
-
-addedprovides = pool.addfileprovides_queue()
-if !addedprovides.empty?
- sysrepo.updateaddedprovides(addedprovides)
- for repo in repos
- repo.updateaddedprovides(addedprovides)
- end
-end
-pool.createwhatprovides()
-
-jobs = []
-for arg in args
- flags = Solv::Selection::SELECTION_NAME | Solv::Selection::SELECTION_PROVIDES | Solv::Selection::SELECTION_GLOB
- flags |= Solv::Selection::SELECTION_CANON | Solv::Selection::SELECTION_DOTARCH | Solv::Selection::SELECTION_REL
- if arg =~ /^\//
- flags |= Solv::Selection::SELECTION_FILELIST
- flags |= Solv::Selection::SELECTION_INSTALLED_ONLY if cmd == 'erase'
- end
- sel = pool.select(arg, flags)
- if sel.isempty?
- sel = pool.select(arg, flags | Solv::Selection::SELECTION_NOCASE)
- puts "[ignoring case for '#{arg}']" unless sel.isempty?
- end
- puts "[using file list match for '#{arg}']" if sel.flags & Solv::Selection::SELECTION_FILELIST != 0
- puts "[using capability match for '#{arg}']" if sel.flags & Solv::Selection::SELECTION_PROVIDES != 0
- jobs += sel.jobs(cmdactionmap[cmd])
-end
-
-if jobs.empty? && (cmd == 'up' || cmd == 'dup' || cmd == 'verify')
- sel = pool.Selection_all()
- jobs += sel.jobs(cmdactionmap[cmd])
-end
-
-abort("no package matched.") if jobs.empty?
-
-if cmd == 'list' || cmd == 'info'
- for job in jobs
- for s in job.solvables()
- if cmd == 'info'
- puts "Name: #{s.str}"
- puts "Repo: #{s.repo.name}"
- puts "Summary: #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
- str = s.lookup_str(Solv::SOLVABLE_URL)
- puts "Url: #{str}" if str
- str = s.lookup_str(Solv::SOLVABLE_LICENSE)
- puts "License: #{str}" if str
- puts "Description:\n#{s.lookup_str(Solv::SOLVABLE_DESCRIPTION)}"
- puts
- else
- puts " - #{s.str} [#{s.repo.name}]"
- puts " #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
- end
- end
- end
- exit
-end
-
-for job in jobs
- job.how ^= Solv::Job::SOLVER_UPDATE ^ Solv::Job::SOLVER_INSTALL if cmd == 'up' and job.isemptyupdate?
-end
-
-solver = pool.Solver
-solver.set_flag(Solv::Solver::SOLVER_FLAG_SPLITPROVIDES, 1)
-solver.set_flag(Solv::Solver::SOLVER_FLAG_ALLOW_UNINSTALL, 1) if cmd == 'erase'
-#pool.set_debuglevel(1)
-
-while true
- problems = solver.solve(jobs)
- break if problems.empty?
- for problem in problems
- puts "Problem #{problem.id}/#{problems.count}:"
- puts problem
- solutions = problem.solutions
- for solution in solutions
- puts " Solution #{solution.id}:"
- elements = solution.elements(true)
- for element in elements
- puts " - #{element.str}"
- end
- puts
- end
- sol = nil
- while true
- print "Please choose a solution: "
- STDOUT.flush
- sol = STDIN.gets.strip
- break if sol == 's' || sol == 'q'
- break if sol =~ /^\d+$/ && sol.to_i >= 1 && sol.to_i <= solutions.length
- end
- next if sol == 's'
- abort if sol == 'q'
- solution = solutions[sol.to_i - 1]
- for element in solution.elements
- newjob = element.Job()
- if element.type == Solv::Solver::SOLVER_SOLUTION_JOB
- jobs[element.jobidx] = newjob
- else
- jobs.push(newjob) if newjob && !jobs.include?(newjob)
- end
- end
- end
-end
-
-trans = solver.transaction
-solver = nil
-if trans.isempty?
- puts "Nothing to do."
- exit
-end
-
-puts "\nTransaction summary:\n"
-for cl in trans.classify(Solv::Transaction::SOLVER_TRANSACTION_SHOW_OBSOLETES | Solv::Transaction::SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE)
- if cl.type == Solv::Transaction::SOLVER_TRANSACTION_ERASE
- puts "#{cl.count} erased packages:"
- elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_INSTALL
- puts "#{cl.count} installed packages:"
- elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_REINSTALLED
- puts "#{cl.count} reinstalled packages:"
- elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED
- puts "#{cl.count} downgraded packages:"
- elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_CHANGED
- puts "#{cl.count} changed packages:"
- elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_UPGRADED
- puts "#{cl.count} upgraded packages:"
- elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_VENDORCHANGE
- puts "#{cl.count} vendor changes from '#{cl.fromstr}' to '#{cl.tostr}':"
- elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_ARCHCHANGE
- puts "#{cl.count} arch changes from '#{cl.fromstr}' to '#{cl.tostr}':"
- else
- next
- end
- for p in cl.solvables
- if cl.type == Solv::Transaction::SOLVER_TRANSACTION_UPGRADED || cl.type == Solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED
- puts " - #{p.str} -> #{trans.othersolvable(p).str}"
- else
- puts " - #{p.str}"
- end
- end
- puts
-end
-puts "install size change: #{trans.calc_installsizechange()} K\n\n"
-
-while true
- print("OK to continue (y/n)? ")
- STDOUT.flush
- yn = STDIN.gets.strip
- break if yn == 'y'
- abort if yn == 'n' || yn == 'q'
-end
-
-newpkgs = trans.newsolvables()
-newpkgsfp = {}
-if !newpkgs.empty?
- downloadsize = 0
- for p in newpkgs
- downloadsize += p.lookup_num(Solv::SOLVABLE_DOWNLOADSIZE)
- end
- puts "Downloading #{newpkgs.length} packages, #{downloadsize / 1024} K"
- for p in newpkgs
- repo = p.repo.appdata
- location, medianr = p.lookup_location()
- next unless location
- location = repo.packagespath + location
- chksum = p.lookup_checksum(Solv::SOLVABLE_CHECKSUM)
- f = repo.download(location, false, chksum)
- abort("\n#{@name}: #{location} not found in repository\n") unless f
- newpkgsfp[p.id] = f
- print "."
- STDOUT.flush()
- end
- puts
-end
-
-puts "Committing transaction:"
-puts
-trans.order()
-for p in trans.steps
- steptype = trans.steptype(p, Solv::Transaction::SOLVER_TRANSACTION_RPM_ONLY)
- if steptype == Solv::Transaction::SOLVER_TRANSACTION_ERASE
- puts "erase #{p.str}"
- next unless p.lookup_num(Solv::RPM_RPMDBID)
- evr = p.evr.sub(/^[0-9]+:/, '')
- system('rpm', '-e', '--nodeps', '--nodigest', '--nosignature', "#{p.name}-#{evr}.#{p.arch}") || abort("rpm failed: #{$? >> 8}")
- elsif (steptype == Solv::Transaction::SOLVER_TRANSACTION_INSTALL || steptype == Solv::Transaction::SOLVER_TRANSACTION_MULTIINSTALL)
- puts "install #{p.str}"
- f = newpkgsfp.delete(p.id)
- next unless f
- mode = steptype == Solv::Transaction::SOLVER_TRANSACTION_INSTALL ? '-U' : '-i'
- f.cloexec(0)
- system('rpm', mode, '--force', '--nodeps', '--nodigest', '--nosignature', "/dev/fd/#{f.fileno().to_s}") || abort("rpm failed: #{$? >> 8}")
- f.close
- end
-end
+++ /dev/null
-
-ADD_EXECUTABLE (solv solv.c
-checksig.c
-deltarpm.c
-fastestmirror.c
-fileconflicts.c
-fileprovides.c
-mirror.c
-patchjobs.c
-repoinfo.c
-repoinfo_cache.c
-repoinfo_config_debian.c
-repoinfo_config_yum.c
-repoinfo_config_urpmi.c
-repoinfo_download.c
-repoinfo_system_debian.c
-repoinfo_system_rpm.c
-repoinfo_type_debian.c
-repoinfo_type_mdk.c
-repoinfo_type_rpmmd.c
-repoinfo_type_susetags.c
-)
-
-TARGET_LINK_LIBRARIES (solv libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-INSTALL(TARGETS
- solv
- DESTINATION ${BIN_INSTALL_DIR})
-
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "pool.h"
-#include "repo.h"
-#ifdef ENABLE_PUBKEY
-#include "repo_pubkey.h"
-#endif
-
-#include "checksig.h"
-
-#ifndef DEBIAN
-
-static void
-cleanupgpg(char *gpgdir)
-{
- char cmd[256];
- snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
- unlink(cmd);
- snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
- unlink(cmd);
- snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
- unlink(cmd);
- snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
- unlink(cmd);
- snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
- unlink(cmd);
- rmdir(gpgdir);
-}
-
-int
-checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
-{
- char *gpgdir;
- char *keysfile;
- const char *pubkey;
- char cmd[256];
- FILE *kfp;
- Solvable *s;
- Id p;
- off_t posfp, possigfp;
- int r, nkeys;
-
- gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
- if (!gpgdir)
- return 0;
- keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
- if (!(kfp = fopen(keysfile, "w")) )
- {
- cleanupgpg(gpgdir);
- return 0;
- }
- nkeys = 0;
- for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
- {
- if (!s->repo)
- continue;
- pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
- if (!pubkey || !*pubkey)
- continue;
- if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
- break;
- if (fputc('\n', kfp) == EOF) /* Just in case... */
- break;
- nkeys++;
- }
- if (fclose(kfp) || !nkeys || p < sigpool->nsolvables)
- {
- cleanupgpg(gpgdir);
- return 0;
- }
- snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
- if (system(cmd))
- {
- fprintf(stderr, "key import error\n");
- cleanupgpg(gpgdir);
- return 0;
- }
- unlink(keysfile);
- posfp = lseek(fileno(fp), 0, SEEK_CUR);
- lseek(fileno(fp), 0, SEEK_SET);
- possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
- lseek(fileno(sigfp), 0, SEEK_SET);
- snprintf(cmd, sizeof(cmd), "gpgv -q --homedir %s --keyring %s/pubring.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, gpgdir, fileno(sigfp), fileno(fp));
- fcntl(fileno(fp), F_SETFD, 0); /* clear CLOEXEC */
- fcntl(fileno(sigfp), F_SETFD, 0); /* clear CLOEXEC */
- r = system(cmd);
- lseek(fileno(sigfp), possigfp, SEEK_SET);
- lseek(fileno(fp), posfp, SEEK_SET);
- fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
- fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
- cleanupgpg(gpgdir);
- return r == 0 ? 1 : 0;
-}
-
-#else
-
-int
-checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
-{
- char cmd[256];
- int r;
-
- snprintf(cmd, sizeof(cmd), "gpgv -q --keyring /etc/apt/trusted.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", fileno(sigfp), fileno(fp));
- fcntl(fileno(fp), F_SETFD, 0); /* clear CLOEXEC */
- fcntl(fileno(sigfp), F_SETFD, 0); /* clear CLOEXEC */
- r = system(cmd);
- fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
- fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
- return r == 0 ? 1 : 0;
-}
-
-#endif
-
-Pool *
-read_sigs()
-{
- Pool *sigpool = pool_create();
-#if defined(ENABLE_PUBKEY) && defined(ENABLE_RPMDB)
- Repo *repo = repo_create(sigpool, "pubkeys");
- repo_add_rpmdb_pubkeys(repo, 0);
-#endif
- return sigpool;
-}
+++ /dev/null
-extern int checksig(Pool *sigpool, FILE *fp, FILE *sigfp);
-extern Pool *read_sigs();
-
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repoinfo.h"
-#include "repoinfo_download.h"
-
-#include "deltarpm.h"
-
-static inline int
-opentmpfile()
-{
- char tmpl[100];
- int fd;
-
- strcpy(tmpl, "/var/tmp/solvXXXXXX");
- fd = mkstemp(tmpl);
- if (fd < 0)
- {
- perror("mkstemp");
- exit(1);
- }
- unlink(tmpl);
- return fd;
-}
-
-FILE *
-trydeltadownload(Solvable *s, const char *loc)
-{
- Repo *repo = s->repo;
- Pool *pool = repo->pool;
- struct repoinfo *cinfo = repo->appdata;
- Dataiterator di;
- Id pp;
- const unsigned char *chksum;
- Id chksumtype;
- FILE *retfp = 0;
- char *matchname = strdup(pool_id2str(pool, s->name));
-
- dataiterator_init(&di, pool, repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
- dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
- while (dataiterator_step(&di))
- {
- Id baseevr, op;
-
- dataiterator_setpos_parent(&di);
- if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
- pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
- continue;
- baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
- FOR_PROVIDES(op, pp, s->name)
- {
- Solvable *os = pool->solvables + op;
- if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
- break;
- }
- if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
- {
- /* base is installed, run sequence check */
- const char *seq;
- const char *dloc;
- const char *archstr;
- FILE *fp;
- char cmd[128];
- int newfd;
-
- archstr = pool_id2str(pool, s->arch);
- if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
- continue;
-
- seq = pool_tmpjoin(pool, pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME), "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
- seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
- if (strchr(seq, '\'') != 0)
- continue;
-#ifdef FEDORA
- sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
-#else
- sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
-#endif
- if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
- continue; /* didn't match */
- /* looks good, download delta */
- chksumtype = 0;
- chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
- if (!chksumtype)
- continue; /* no way! */
- dloc = pool_lookup_deltalocation(pool, SOLVID_POS, 0);
- if (!dloc)
- continue;
-#ifdef ENABLE_SUSEREPO
- if (cinfo->type == TYPE_SUSETAGS)
- {
- const char *datadir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DATADIR);
- dloc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", dloc);
- }
-#endif
- if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
- continue;
- /* got it, now reconstruct */
- newfd = opentmpfile();
-#ifdef FEDORA
- sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
-#else
- sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
-#endif
- fcntl(fileno(fp), F_SETFD, 0);
- if (system(cmd))
- {
- close(newfd);
- fclose(fp);
- continue;
- }
- lseek(newfd, 0, SEEK_SET);
- chksumtype = 0;
- chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
- if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
- {
- close(newfd);
- fclose(fp);
- continue;
- }
- retfp = fdopen(newfd, "r");
- fclose(fp);
- break;
- }
- }
- dataiterator_free(&di);
- solv_free(matchname);
- return retfp;
-}
+++ /dev/null
-extern FILE *trydeltadownload(Solvable *s, const char *loc);
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <poll.h>
-#include <errno.h>
-
-#include "util.h"
-
-#include "fastestmirror.h"
-
-void
-findfastest(char **urls, int nurls)
-{
- int i, j, port;
- int *socks, qc;
- struct pollfd *fds;
- char *p, *p2, *q;
- char portstr[16];
- struct addrinfo hints, *result;;
-
- fds = solv_calloc(nurls, sizeof(*fds));
- socks = solv_calloc(nurls, sizeof(*socks));
- for (i = 0; i < nurls; i++)
- {
- socks[i] = -1;
- p = strchr(urls[i], '/');
- if (!p)
- continue;
- if (p[1] != '/')
- continue;
- p += 2;
- q = strchr(p, '/');
- qc = 0;
- if (q)
- {
- qc = *q;
- *q = 0;
- }
- if ((p2 = strchr(p, '@')) != 0)
- p = p2 + 1;
- port = 80;
- if (!strncmp("https:", urls[i], 6))
- port = 443;
- else if (!strncmp("ftp:", urls[i], 4))
- port = 21;
- if ((p2 = strrchr(p, ':')) != 0)
- {
- port = atoi(p2 + 1);
- if (q)
- *q = qc;
- q = p2;
- qc = *q;
- *q = 0;
- }
- sprintf(portstr, "%d", port);
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_NUMERICSERV;
- result = 0;
- if (!getaddrinfo(p, portstr, &hints, &result))
- {
- socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
- if (socks[i] >= 0)
- {
- fcntl(socks[i], F_SETFL, O_NONBLOCK);
- if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
- {
- if (errno != EINPROGRESS)
- {
- close(socks[i]);
- socks[i] = -1;
- }
- }
- }
- freeaddrinfo(result);
- }
- if (q)
- *q = qc;
- }
- for (;;)
- {
- for (i = j = 0; i < nurls; i++)
- {
- if (socks[i] < 0)
- continue;
- fds[j].fd = socks[i];
- fds[j].events = POLLOUT;
- j++;
- }
- if (j < 2)
- {
- i = j - 1;
- break;
- }
- if (poll(fds, j, 10000) <= 0)
- {
- i = -1; /* something is wrong */
- break;
- }
- for (i = 0; i < j; i++)
- if ((fds[i].revents & POLLOUT) != 0)
- {
- int soe = 0;
- socklen_t soel = sizeof(int);
- if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
- {
- /* connect failed, kill socket */
- for (j = 0; j < nurls; j++)
- if (socks[j] == fds[i].fd)
- {
- close(socks[j]);
- socks[j] = -1;
- }
- i = j + 1;
- break;
- }
- break; /* horray! */
- }
- if (i == j + 1)
- continue;
- if (i == j)
- i = -1; /* something is wrong, no bit was set */
- break;
- }
- /* now i contains the fastest fd index */
- if (i >= 0)
- {
- for (j = 0; j < nurls; j++)
- if (socks[j] == fds[i].fd)
- break;
- if (j != 0)
- {
- char *url0 = urls[0];
- urls[0] = urls[j];
- urls[j] = url0;
- }
- }
- for (i = j = 0; i < nurls; i++)
- if (socks[i] >= 0)
- close(socks[i]);
- free(socks);
- free(fds);
-}
+++ /dev/null
-extern void findfastest(char **urls, int nurls);
-
+++ /dev/null
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_rpmdb.h"
-#include "pool_fileconflicts.h"
-
-#include "fileconflicts.h"
-
-struct fcstate {
- FILE **newpkgsfps;
- Queue *checkq;
- int newpkgscnt;
- void *rpmstate;
-};
-
-static void *
-fileconflict_cb(Pool *pool, Id p, void *cbdata)
-{
- struct fcstate *fcstate = cbdata;
- Solvable *s;
- Id rpmdbid;
- int i;
- FILE *fp;
-
- s = pool_id2solvable(pool, p);
- if (pool->installed && s->repo == pool->installed)
- {
- if (!s->repo->rpmdbid)
- return 0;
- rpmdbid = s->repo->rpmdbid[p - s->repo->start];
- if (!rpmdbid)
- return 0;
- return rpm_byrpmdbid(fcstate->rpmstate, rpmdbid);
- }
- for (i = 0; i < fcstate->newpkgscnt; i++)
- if (fcstate->checkq->elements[i] == p)
- break;
- if (i == fcstate->newpkgscnt)
- return 0;
- fp = fcstate->newpkgsfps[i];
- if (!fp)
- return 0;
- rewind(fp);
- return rpm_byfp(fcstate->rpmstate, fp, pool_solvable2str(pool, s));
-}
-
-int
-checkfileconflicts(Pool *pool, Queue *checkq, int newpkgs, FILE **newpkgsfps, Queue *conflicts)
-{
- struct fcstate fcstate;
- int i;
-
- printf("Searching for file conflicts\n");
- queue_init(conflicts);
- fcstate.rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
- fcstate.newpkgscnt = newpkgs;
- fcstate.checkq = checkq;
- fcstate.newpkgsfps = newpkgsfps;
- pool_findfileconflicts(pool, checkq, newpkgs, conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &fileconflict_cb, &fcstate);
- fcstate.rpmstate = rpm_state_free(fcstate.rpmstate);
- if (conflicts->count)
- {
- printf("\n");
- for (i = 0; i < conflicts->count; i += 6)
- printf("file %s of package %s conflicts with package %s\n", pool_id2str(pool, conflicts->elements[i]), pool_solvid2str(pool, conflicts->elements[i + 1]), pool_solvid2str(pool, conflicts->elements[i + 4]));
- printf("\n");
- }
- return conflicts->count;
-}
-
-#endif
+++ /dev/null
-extern int checkfileconflicts(Pool *pool, Queue *checkq, int newpkgs, FILE **newpkgsfps, Queue *conflicts);
-
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-
-#include "fileprovides.h"
-
-static void
-rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
-{
- Repo *repo;
- Repodata *data;
- Map providedids;
- Queue fileprovidesq;
- int i, j, n;
- struct repoinfo *cinfo;
-
- map_init(&providedids, pool->ss.nstrings);
- queue_init(&fileprovidesq);
- for (i = 0; i < addedfileprovides->count; i++)
- MAPSET(&providedids, addedfileprovides->elements[i]);
- FOR_REPOS(i, repo)
- {
- /* make sure all repodatas but the first are extensions */
- if (repo->nrepodata < 2)
- continue;
- cinfo = repo->appdata;
- if (!cinfo)
- continue; /* cmdline */
- if (cinfo->incomplete)
- continue;
- data = repo_id2repodata(repo, 1);
- if (data->loadcallback)
- continue;
- for (j = 2; j < repo->nrepodata; j++)
- {
- Repodata *edata = repo_id2repodata(repo, j);
- if (!edata->loadcallback)
- break;
- }
- if (j < repo->nrepodata)
- continue; /* found a non-extension repodata, can't rewrite */
- if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
- {
- if (repo == pool->installed && addedfileprovides_inst)
- {
- for (j = 0; j < addedfileprovides->count; j++)
- MAPCLR(&providedids, addedfileprovides->elements[j]);
- for (j = 0; j < addedfileprovides_inst->count; j++)
- MAPSET(&providedids, addedfileprovides_inst->elements[j]);
- }
- n = 0;
- for (j = 0; j < fileprovidesq.count; j++)
- if (MAPTST(&providedids, fileprovidesq.elements[j]))
- n++;
- if (repo == pool->installed && addedfileprovides_inst)
- {
- for (j = 0; j < addedfileprovides_inst->count; j++)
- MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
- for (j = 0; j < addedfileprovides->count; j++)
- MAPSET(&providedids, addedfileprovides->elements[j]);
- if (n == addedfileprovides_inst->count)
- continue; /* nothing new added */
- }
- else if (n == addedfileprovides->count)
- continue; /* nothing new added */
- }
- repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
- repodata_internalize(data);
- writecachedrepo(cinfo, 0, data);
- }
- queue_free(&fileprovidesq);
- map_free(&providedids);
-}
-
-void
-addfileprovides(Pool *pool)
-{
- Queue addedfileprovides;
- Queue addedfileprovides_inst;
-
- queue_init(&addedfileprovides);
- queue_init(&addedfileprovides_inst);
- pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
- if (addedfileprovides.count || addedfileprovides_inst.count)
- rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
- queue_free(&addedfileprovides);
- queue_free(&addedfileprovides_inst);
-}
+++ /dev/null
-void addfileprovides(Pool *pool);
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "pool.h"
-#include "util.h"
-#include "fastestmirror.h"
-
-#include "mirror.h"
-
-char *
-findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
-{
- char buf[4096], *bp, *ep;
- char **urls = 0;
- int nurls = 0;
- int i;
-
- if (chksumtypep)
- *chksumtypep = 0;
- while((bp = fgets(buf, sizeof(buf), fp)) != 0)
- {
- while (*bp == ' ' || *bp == '\t')
- bp++;
- if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
- {
- bp += 20;
- if (solv_hex2bin((const char **)&bp, chksump, 32) == 32)
- *chksumtypep = REPOKEY_TYPE_SHA256;
- continue;
- }
- if (strncmp(bp, "<url", 4))
- continue;
- bp = strchr(bp, '>');
- if (!bp)
- continue;
- bp++;
- ep = strstr(bp, "repodata/repomd.xml</url>");
- if (!ep)
- continue;
- *ep = 0;
- if (strncmp(bp, "http", 4))
- continue;
- urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
- urls[nurls++] = strdup(bp);
- }
- if (nurls)
- {
- if (nurls > 1)
- findfastest(urls, nurls > 5 ? 5 : nurls);
- bp = urls[0];
- urls[0] = 0;
- for (i = 0; i < nurls; i++)
- solv_free(urls[i]);
- solv_free(urls);
- ep = strchr(bp, '/');
- if ((ep = strchr(ep + 2, '/')) != 0)
- {
- *ep = 0;
- printf("[using mirror %s]\n", bp);
- *ep = '/';
- }
- return bp;
- }
- return 0;
-}
-
-char *
-findmirrorlisturl(FILE *fp)
-{
- char buf[4096], *bp, *ep;
- int i, l;
- char **urls = 0;
- int nurls = 0;
-
- while((bp = fgets(buf, sizeof(buf), fp)) != 0)
- {
- while (*bp == ' ' || *bp == '\t')
- bp++;
- if (!*bp || *bp == '#')
- continue;
- l = strlen(bp);
- while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
- bp[--l] = 0;
- if ((ep = strstr(bp, "url=")) != 0)
- bp = ep + 4;
- urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
- urls[nurls++] = strdup(bp);
- }
- if (nurls)
- {
- if (nurls > 1)
- findfastest(urls, nurls > 5 ? 5 : nurls);
- bp = urls[0];
- urls[0] = 0;
- for (i = 0; i < nurls; i++)
- solv_free(urls[i]);
- solv_free(urls);
- ep = strchr(bp, '/');
- if ((ep = strchr(ep + 2, '/')) != 0)
- {
- *ep = 0;
- printf("[using mirror %s]\n", bp);
- *ep = '/';
- }
- return bp;
- }
- return 0;
-}
+++ /dev/null
-char *findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep);
-char *findmirrorlisturl(FILE *fp);
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "evr.h"
-#include "solver.h"
-
-void
-add_patchjobs(Pool *pool, Queue *job)
-{
- Id p, pp;
- int pruneyou = 0;
- Map installedmap, multiversionmap;
- Solvable *s;
-
- map_init(&multiversionmap, 0);
- map_init(&installedmap, pool->nsolvables);
- solver_calculate_multiversionmap(pool, job, &multiversionmap);
- if (pool->installed)
- FOR_REPO_SOLVABLES(pool->installed, p, s)
- MAPSET(&installedmap, p);
-
- /* install all patches */
- for (p = 1; p < pool->nsolvables; p++)
- {
- const char *type;
- int r;
- Id p2;
-
- s = pool->solvables + p;
- if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
- continue;
- FOR_PROVIDES(p2, pp, s->name)
- {
- Solvable *s2 = pool->solvables + p2;
- if (s2->name != s->name)
- continue;
- r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
- if (r < 0 || (r == 0 && p > p2))
- break;
- }
- if (p2)
- continue;
- type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
- if (type && !strcmp(type, "optional"))
- continue;
- r = solvable_trivial_installable_map(s, &installedmap, 0, &multiversionmap);
- if (r == -1)
- continue;
- if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
- {
- if (!pruneyou++)
- queue_empty(job);
- }
- else if (pruneyou)
- continue;
- queue_push2(job, SOLVER_SOLVABLE, p);
- }
- map_free(&installedmap);
- map_free(&multiversionmap);
-}
+++ /dev/null
-extern void add_patchjobs(Pool *pool, Queue *job);
-
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
-#include "repo_rpmdb.h"
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-#include "repo_deb.h"
-#endif
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-
-#if defined(SUSE) || defined(FEDORA)
-#include "repoinfo_config_yum.h"
-#endif
-#if defined(DEBIAN)
-#include "repoinfo_config_debian.h"
-#endif
-#if defined(MANDRIVA) || defined(MAGEIA)
-#include "repoinfo_config_urpmi.h"
-#endif
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
-#include "repoinfo_system_rpm.h"
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-#include "repoinfo_system_debian.h"
-#endif
-
-#ifdef ENABLE_RPMMD
-#include "repoinfo_type_rpmmd.h"
-#endif
-#ifdef ENABLE_SUSEREPO
-#include "repoinfo_type_susetags.h"
-#endif
-#ifdef ENABLE_DEBIAN
-#include "repoinfo_type_debian.h"
-#endif
-#ifdef ENABLE_MDKREPO
-#include "repoinfo_type_mdk.h"
-#endif
-
-static int
-repoinfos_sort_cmp(const void *ap, const void *bp)
-{
- const struct repoinfo *a = ap;
- const struct repoinfo *b = bp;
- return strcmp(a->alias, b->alias);
-}
-
-void
-sort_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
-{
- qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), repoinfos_sort_cmp);
-}
-
-void
-free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
-{
- int i, j;
- for (i = 0; i < nrepoinfos; i++)
- {
- struct repoinfo *cinfo = repoinfos + i;
- solv_free(cinfo->name);
- solv_free(cinfo->alias);
- solv_free(cinfo->path);
- solv_free(cinfo->metalink);
- solv_free(cinfo->mirrorlist);
- solv_free(cinfo->baseurl);
- for (j = 0; j < cinfo->ncomponents; j++)
- solv_free(cinfo->components[j]);
- solv_free(cinfo->components);
- }
- solv_free(repoinfos);
-#if defined(SUSE) || defined(FEDORA)
- yum_substitute((Pool *)0, 0); /* free data */
-#endif
-}
-
-struct repoinfo *
-read_repoinfos(Pool *pool, int *nrepoinfosp)
-{
- struct repoinfo *repoinfos = 0;
-#if defined(SUSE) || defined(FEDORA)
- repoinfos = read_repoinfos_yum(pool, nrepoinfosp);
-#endif
-#if defined(MANDRIVA) || defined(MAGEIA)
- repoinfos = read_repoinfos_urpmi(pool, nrepoinfosp);
-#endif
-#if defined(DEBIAN)
- repoinfos = read_repoinfos_debian(pool, nrepoinfosp);
-#endif
- return repoinfos;
-}
-
-int
-read_installed_repo(struct repoinfo *cinfo, Pool *pool)
-{
- int r = 1;
- cinfo->type = TYPE_INSTALLED;
- cinfo->repo = repo_create(pool, "@System");
- cinfo->repo->appdata = cinfo;
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
- r = read_installed_rpm(cinfo);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- r = read_installed_debian(cinfo);
-#endif
- pool_set_installed(pool, cinfo->repo);
- return r;
-}
-
-int
-is_cmdline_package(const char *filename)
-{
- int l = strlen(filename);
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
- if (l > 4 && !strcmp(filename + l - 4, ".rpm"))
- return 1;
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- if (l > 4 && !strcmp(filename + l - 4, ".deb"))
- return 1;
-#endif
- return 0;
-}
-
-Id
-add_cmdline_package(Repo *repo, const char *filename)
-{
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
- return repo_add_rpm(repo, filename, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- return repo_add_deb(repo, filename, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
-#endif
- return 0;
-}
-
-void
-commit_transactionelement(Pool *pool, Id type, Id p, FILE *fp)
-{
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
- commit_transactionelement_rpm(pool, type, p, fp);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- commit_transactionelement_debian(pool, type, p, fp);
-#endif
-}
-
-void
-add_ext_keys(Repodata *data, Id handle, const char *ext)
-{
- static Id langtags[] = {
- SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
- SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
- SOLVABLE_EULA, REPOKEY_TYPE_STR,
- SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
- SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
- SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
- 0, 0
- };
- if (!strcmp(ext, "DL"))
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
- }
- else if (!strcmp(ext, "FL"))
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
- }
- else if (!strcmp(ext, "DU"))
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
- }
- else
- {
- Pool *pool = data->repo->pool;
- int i;
- for (i = 0; langtags[i]; i += 2)
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, langtags[i], ext, 1));
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, langtags[i + 1]);
- }
- }
-}
-
-int
-load_stub(Pool *pool, Repodata *data, void *dp)
-{
- struct repoinfo *cinfo = data->repo->appdata;
- switch (cinfo->type)
- {
-#ifdef ENABLE_SUSEREPO
- case TYPE_SUSETAGS:
- return susetags_load_ext(data->repo, data);
-#endif
-#ifdef ENABLE_RPMMD
- case TYPE_RPMMD:
- return repomd_load_ext(data->repo, data);
-#endif
-#ifdef ENABLE_MDKREPO
- case TYPE_MDK:
- return mdk_load_ext(data->repo, data);
-#endif
- default:
- /* debian does not have any ext data yet */
- return 0;
- }
-}
-
-void
-read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
-{
- Repo *repo;
- int i;
- Pool *sigpool = 0;
-
- for (i = 0; i < nrepoinfos; i++)
- {
- struct repoinfo *cinfo = repoinfos + i;
- if (!cinfo->enabled)
- continue;
-
- repo = repo_create(pool, cinfo->alias);
- cinfo->repo = repo;
- repo->appdata = cinfo;
- repo->priority = 99 - cinfo->priority;
-
- if ((!cinfo->autorefresh || cinfo->metadata_expire) && usecachedrepo(cinfo, 0, 0))
- {
- printf("repo '%s':", cinfo->alias);
- printf(" cached\n");
- continue;
- }
-
- switch (cinfo->type)
- {
-#ifdef ENABLE_RPMMD
- case TYPE_RPMMD:
- repomd_load(cinfo, &sigpool);
- break;
-#endif
-#ifdef ENABLE_SUSEREPO
- case TYPE_SUSETAGS:
- susetags_load(cinfo, &sigpool);
- break;
-#endif
-#ifdef ENABLE_DEBIAN
- case TYPE_DEBIAN:
- debian_load(cinfo, &sigpool);
- break;
-#endif
-#ifdef ENABLE_MDKREPO
- case TYPE_MDK:
- mdk_load(cinfo, &sigpool);
- break;
-#endif
- default:
- printf("unsupported repo '%s': skipped\n", cinfo->alias);
- repo_free(repo, 1);
- cinfo->repo = 0;
- break;
- }
- }
- if (sigpool)
- pool_free(sigpool);
-}
-
+++ /dev/null
-struct repoinfo {
- Repo *repo;
-
- int type;
- char *alias;
- char *name;
- int enabled;
- int autorefresh;
- char *baseurl;
- char *metalink;
- char *mirrorlist;
- char *path;
- int pkgs_gpgcheck;
- int repo_gpgcheck;
- int priority;
- int keeppackages;
- int metadata_expire;
- char **components;
- int ncomponents;
- int cookieset;
- unsigned char cookie[32];
- int extcookieset;
- unsigned char extcookie[32];
- int incomplete;
-};
-
-#define TYPE_UNKNOWN 0
-#define TYPE_SUSETAGS 1
-#define TYPE_RPMMD 2
-#define TYPE_PLAINDIR 3
-#define TYPE_DEBIAN 4
-#define TYPE_MDK 5
-
-#define TYPE_INSTALLED 16
-#define TYPE_CMDLINE 17
-
-#define METADATA_EXPIRE (60 * 15)
-
-extern void sort_repoinfos(struct repoinfo *repoinfos, int nrepoinfos);
-extern void free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos);
-extern void read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos);
-extern struct repoinfo *read_repoinfos(Pool *pool, int *nrepoinfosp);
-
-extern int read_installed_repo(struct repoinfo *cinfo, Pool *pool);
-
-extern int is_cmdline_package(const char *filename);
-extern Id add_cmdline_package(Repo *repo, const char *filename);
-
-extern void commit_transactionelement(Pool *pool, Id type, Id p, FILE *fp);
-
-extern void add_ext_keys(Repodata *data, Id handle, const char *ext);
-extern int load_stub(Pool *pool, Repodata *data, void *dp);
-
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <time.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "repo_solv.h"
-#include "repo_write.h"
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-
-#define COOKIE_IDENT "1.1"
-
-#define SOLVCACHE_PATH "/var/cache/solv"
-
-static char *userhome;
-
-void
-set_userhome()
-{
- userhome = getenv("HOME");
- if (userhome && userhome[0] != '/')
- userhome = 0;
-}
-
-void
-calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out)
-{
- char buf[4096];
- Chksum *h = solv_chksum_create(chktype);
- int l;
-
- solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
- while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
- solv_chksum_add(h, buf, l);
- rewind(fp);
- solv_chksum_free(h, out);
-}
-
-void
-calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out)
-{
- Chksum *h = solv_chksum_create(chktype);
- solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
- if (cookie)
- solv_chksum_add(h, cookie, 32);
- solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
- solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
- solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
- solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
- solv_chksum_free(h, out);
-}
-
-char *
-calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc)
-{
- char *q, *p;
- int l;
- if (!forcesystemloc && userhome && getuid())
- p = pool_tmpjoin(repo->pool, userhome, "/.solvcache/", 0);
- else
- p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", 0);
- l = strlen(p);
- p = pool_tmpappend(repo->pool, p, repo->name, 0);
- if (repoext)
- {
- p = pool_tmpappend(repo->pool, p, "_", repoext);
- p = pool_tmpappend(repo->pool, p, ".solvx", 0);
- }
- else
- p = pool_tmpappend(repo->pool, p, ".solv", 0);
- q = p + l;
- if (*q == '.')
- *q = '_';
- for (; *q; q++)
- if (*q == '/')
- *q = '_';
- return p;
-}
-
-int
-usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark)
-{
- Repo *repo = cinfo->repo;
- FILE *fp;
- unsigned char *cookie = repoext ? cinfo->extcookie : (cinfo->cookieset ? cinfo->cookie : 0);
- unsigned char mycookie[32];
- unsigned char myextcookie[32];
- int flags;
- int forcesystemloc;
-
- if (repoext && !cinfo->extcookieset)
- return 0; /* huh? */
- forcesystemloc = mark & 2 ? 0 : 1;
- if (mark < 2 && userhome && getuid())
- {
- /* first try home location */
- int res = usecachedrepo(cinfo, repoext, mark | 2);
- if (res)
- return res;
- }
- mark &= 1;
- if (!(fp = fopen(calc_cachepath(repo, repoext, forcesystemloc), "r")))
- return 0;
- if (!repoext && !cinfo->cookieset && cinfo->autorefresh && cinfo->metadata_expire != -1)
- {
- struct stat stb; /* no cookie set yet, check cache expiry time */
- if (fstat(fileno(fp), &stb) || time(0) - stb.st_mtime >= cinfo->metadata_expire)
- {
- fclose(fp);
- return 0;
- }
- }
- if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
- {
- fclose(fp);
- return 0;
- }
- if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)) != 0)
- {
- fclose(fp);
- return 0;
- }
- if (cinfo->type != TYPE_INSTALLED && !repoext)
- {
- if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
- {
- fclose(fp);
- return 0;
- }
- }
- rewind(fp);
-
- flags = 0;
- if (repoext)
- {
- flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
- if (strcmp(repoext, "DL") != 0)
- flags |= REPO_LOCALPOOL; /* no local pool for DL so that we can compare IDs */
- }
- if (repo_add_solv(repo, fp, flags))
- {
- fclose(fp);
- return 0;
- }
- if (cinfo->type != TYPE_INSTALLED && !repoext)
- {
- memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
- cinfo->cookieset = 1;
- memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
- cinfo->extcookieset = 1;
- }
- if (mark)
- futimens(fileno(fp), 0); /* try to set modification time */
- fclose(fp);
- return 1;
-}
-
-static void
-switchtowritten(struct repoinfo *cinfo, const char *repoext, Repodata *repodata, char *tmpl)
-{
- Repo *repo = cinfo->repo;
- FILE *fp;
- int i;
-
- if (!repoext && repodata)
- return; /* rewrite case, don't bother for the added fileprovides */
- for (i = repo->start; i < repo->end; i++)
- if (repo->pool->solvables[i].repo != repo)
- break;
- if (i < repo->end)
- return; /* not a simple block */
- /* switch to just saved repo to activate paging and save memory */
- fp = fopen(tmpl, "r");
- if (!fp)
- return;
- if (!repoext)
- {
- /* main repo */
- repo_empty(repo, 1);
- if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
- {
- /* oops, no way to recover from here */
- fprintf(stderr, "internal error\n");
- exit(1);
- }
- }
- else
- {
- int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
- /* make sure repodata contains complete repo */
- /* (this is how repodata_write saves it) */
- repodata_extend_block(repodata, repo->start, repo->end - repo->start);
- repodata->state = REPODATA_LOADING;
- if (strcmp(repoext, "DL") != 0)
- flags |= REPO_LOCALPOOL;
- repo_add_solv(repo, fp, flags);
- repodata->state = REPODATA_AVAILABLE; /* in case the load failed */
- }
- fclose(fp);
-}
-
-void
-writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata)
-{
- Repo *repo = cinfo->repo;
- FILE *fp;
- int fd;
- char *tmpl, *cachedir;
-
- if (cinfo->incomplete || (repoext && !cinfo->extcookieset) || (!repoext && !cinfo->cookieset))
- return;
- cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
- if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
- printf("[created %s]\n", cachedir);
- /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
- tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
- fd = mkstemp(tmpl);
- if (fd < 0)
- {
- free(tmpl);
- return;
- }
- fchmod(fd, 0444);
- if (!(fp = fdopen(fd, "w")))
- {
- close(fd);
- unlink(tmpl);
- free(tmpl);
- return;
- }
-
- if (!repodata)
- repo_write(repo, fp);
- else if (repoext)
- repodata_write(repodata, fp);
- else
- {
- int oldnrepodata = repo->nrepodata;
- repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata; /* XXX: do this right */
- repo_write(repo, fp);
- repo->nrepodata = oldnrepodata;
- }
-
- if (!repoext && cinfo->type != TYPE_INSTALLED)
- {
- if (!cinfo->extcookieset)
- {
- /* create the ext cookie and append it */
- /* we just need some unique ID */
- struct stat stb;
- if (fstat(fileno(fp), &stb))
- memset(&stb, 0, sizeof(stb));
- calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->cookie, cinfo->extcookie);
- cinfo->extcookieset = 1;
- }
- if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
- {
- fclose(fp);
- unlink(tmpl);
- free(tmpl);
- return;
- }
- }
- /* append our cookie describing the metadata state */
- if (fwrite(repoext ? cinfo->extcookie : cinfo->cookie, 32, 1, fp) != 1)
- {
- fclose(fp);
- unlink(tmpl);
- free(tmpl);
- return;
- }
- if (fclose(fp))
- {
- unlink(tmpl);
- free(tmpl);
- return;
- }
-
- switchtowritten(cinfo, repoext, repodata, tmpl);
-
- if (!rename(tmpl, calc_cachepath(repo, repoext, 0)))
- unlink(tmpl);
- free(tmpl);
-}
-
+++ /dev/null
-
-struct repoinfo;
-struct stat;
-
-extern void set_userhome(void);
-extern char *calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc);
-extern void calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out);
-extern void calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out);
-
-extern int usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark);
-extern void writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata);
-
+++ /dev/null
-#ifdef DEBIAN
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <dirent.h>
-
-#include "pool.h"
-#include "repo.h"
-
-#include "repoinfo.h"
-#include "repoinfo_config_debian.h"
-
-
-
-struct repoinfo *
-read_repoinfos_debian(Pool *pool, int *nrepoinfosp)
-{
- FILE *fp;
- char buf[4096];
- char buf2[4096];
- int l;
- char *kp, *url, *distro;
- struct repoinfo *repoinfos = 0, *cinfo;
- int nrepoinfos = 0;
- DIR *dir = 0;
- struct dirent *ent;
-
- fp = fopen("/etc/apt/sources.list", "r");
- while (1)
- {
- if (!fp)
- {
- if (!dir)
- {
- dir = opendir("/etc/apt/sources.list.d");
- if (!dir)
- break;
- }
- if ((ent = readdir(dir)) == 0)
- {
- closedir(dir);
- break;
- }
- if (ent->d_name[0] == '.')
- continue;
- l = strlen(ent->d_name);
- if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
- continue;
- snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
- if (!(fp = fopen(buf, "r")))
- continue;
- }
- while(fgets(buf2, sizeof(buf2), fp))
- {
- l = strlen(buf2);
- if (l == 0)
- continue;
- while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
- buf2[--l] = 0;
- kp = buf2;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp || *kp == '#')
- continue;
- if (strncmp(kp, "deb", 3) != 0)
- continue;
- kp += 3;
- if (*kp != ' ' && *kp != '\t')
- continue;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp)
- continue;
- url = kp;
- while (*kp && *kp != ' ' && *kp != '\t')
- kp++;
- if (*kp)
- *kp++ = 0;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp)
- continue;
- distro = kp;
- while (*kp && *kp != ' ' && *kp != '\t')
- kp++;
- if (*kp)
- *kp++ = 0;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp)
- continue;
- repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
- cinfo = repoinfos + nrepoinfos++;
- memset(cinfo, 0, sizeof(*cinfo));
- cinfo->baseurl = strdup(url);
- cinfo->alias = solv_dupjoin(url, "/", distro);
- cinfo->name = strdup(distro);
- cinfo->type = TYPE_DEBIAN;
- cinfo->enabled = 1;
- cinfo->autorefresh = 1;
- cinfo->repo_gpgcheck = 1;
- cinfo->metadata_expire = METADATA_EXPIRE;
- while (*kp)
- {
- char *compo;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp)
- break;
- compo = kp;
- while (*kp && *kp != ' ' && *kp != '\t')
- kp++;
- if (*kp)
- *kp++ = 0;
- cinfo->components = solv_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
- cinfo->components[cinfo->ncomponents++] = strdup(compo);
- }
- }
- fclose(fp);
- fp = 0;
- }
- *nrepoinfosp = nrepoinfos;
- return repoinfos;
-}
-
-#endif
+++ /dev/null
-extern struct repoinfo *read_repoinfos_debian(Pool *pool, int *nrepoinfosp);
+++ /dev/null
-#if defined(MANDRIVA) || defined(MAGEIA)
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <dirent.h>
-
-#include "pool.h"
-#include "repo.h"
-
-#include "repoinfo.h"
-#include "repoinfo_config_urpmi.h"
-
-
-#define URPMI_CFG "/etc/urpmi/urpmi.cfg"
-
-
-struct repoinfo *
-read_repoinfos_urpmi(Pool *pool, int *nrepoinfosp)
-{
- char buf[4096], *bp, *arg;
- FILE *fp;
- int l, insect = 0;
- struct repoinfo *cinfo = 0;
- struct repoinfo *repoinfos = 0;
- int nrepoinfos = 0;
-
- if ((fp = fopen(URPMI_CFG, "r")) == 0)
- {
- *nrepoinfosp = 0;
- return 0;
- }
- while (fgets(buf, sizeof(buf), fp))
- {
- l = strlen(buf);
- while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
- buf[--l] = 0;
- bp = buf;
- while (l && (*bp == ' ' || *bp == '\t'))
- {
- l--;
- bp++;
- }
- if (!l || *bp == '#')
- continue;
- if (!insect && bp[l - 1] == '{')
- {
- insect++;
- bp[--l] = 0;
- if (l > 0)
- {
- while (l && (bp[l - 1] == ' ' || bp[l - 1] == '\t'))
- bp[--l] = 0;
- }
- if (l)
- {
- char *bbp = bp, *bbp2 = bp;
- /* unescape */
- while (*bbp)
- {
- if (*bbp == '\\' && bbp[1])
- bbp++;
- *bbp2++ = *bbp++;
- }
- *bbp2 = 0;
- repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
- cinfo = repoinfos + nrepoinfos++;
- memset(cinfo, 0, sizeof(*cinfo));
- cinfo->alias = strdup(bp);
- cinfo->type = TYPE_MDK;
- cinfo->autorefresh = 1;
- cinfo->priority = 99;
- cinfo->enabled = 1;
- cinfo->metadata_expire = METADATA_EXPIRE;
- }
- continue;
- }
- if (insect && *bp == '}')
- {
- insect--;
- cinfo = 0;
- continue;
- }
- if (!insect || !cinfo)
- continue;
- if ((arg = strchr(bp, ':')) != 0)
- {
- *arg++ = 0;
- while (*arg == ' ' || *arg == '\t')
- arg++;
- if (!*arg)
- arg = 0;
- }
- if (strcmp(bp, "ignore") == 0)
- cinfo->enabled = 0;
- if (strcmp(bp, "mirrorlist") == 0)
- cinfo->mirrorlist = solv_strdup(arg);
- if (strcmp(bp, "with-dir") == 0)
- cinfo->path = solv_strdup(arg);
- }
- fclose(fp);
- *nrepoinfosp = nrepoinfos;
- return repoinfos;
-}
-
-#endif
+++ /dev/null
-extern struct repoinfo *read_repoinfos_urpmi(Pool *pool, int *nrepoinfosp);
-
+++ /dev/null
-#if defined(SUSE) || defined(FEDORA)
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/utsname.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_rpmdb.h"
-
-#include "repoinfo.h"
-#include "repoinfo_config_yum.h"
-
-
-#ifdef FEDORA
-# define REPOINFO_PATH "/etc/yum.repos.d"
-#endif
-#ifdef SUSE
-# define REPOINFO_PATH "/etc/zypp/repos.d"
-#endif
-
-char *
-yum_substitute(Pool *pool, char *line)
-{
- char *p, *p2;
- static char *releaseevr;
- static char *basearch;
-
- if (!line)
- {
- solv_free(releaseevr);
- releaseevr = 0;
- solv_free(basearch);
- basearch = 0;
- return 0;
- }
- p = line;
- while ((p2 = strchr(p, '$')) != 0)
- {
- if (!strncmp(p2, "$releasever", 11))
- {
- if (!releaseevr)
- {
- void *rpmstate;
- Queue q;
-
- queue_init(&q);
- rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
- rpm_installedrpmdbids(rpmstate, "Providename", "redhat-release", &q);
- if (q.count)
- {
- void *handle;
- char *p;
- handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
- releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
- if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
- *p = 0;
- }
- rpm_state_free(rpmstate);
- queue_free(&q);
- if (!releaseevr)
- {
- fprintf(stderr, "no installed package provides 'redhat-release', cannot determine $releasever\n");
- exit(1);
- }
- }
- *p2 = 0;
- p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
- p2 = p + (p2 - line);
- line = p;
- p = p2 + strlen(releaseevr);
- continue;
- }
- if (!strncmp(p2, "$basearch", 9))
- {
- if (!basearch)
- {
- struct utsname un;
- if (uname(&un))
- {
- perror("uname");
- exit(1);
- }
- basearch = strdup(un.machine);
- if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
- basearch[1] = '3';
- }
- *p2 = 0;
- p = pool_tmpjoin(pool, line, basearch, p2 + 9);
- p2 = p + (p2 - line);
- line = p;
- p = p2 + strlen(basearch);
- continue;
- }
- p = p2 + 1;
- }
- return line;
-}
-
-struct repoinfo *
-read_repoinfos_yum(Pool *pool, int *nrepoinfosp)
-{
- const char *reposdir = REPOINFO_PATH;
- char buf[4096];
- char buf2[4096], *kp, *vp, *kpe;
- DIR *dir;
- FILE *fp;
- struct dirent *ent;
- int l, rdlen;
- struct repoinfo *repoinfos = 0, *cinfo;
- int nrepoinfos = 0;
-
- rdlen = strlen(reposdir);
- dir = opendir(reposdir);
- if (!dir)
- {
- *nrepoinfosp = 0;
- return 0;
- }
- while ((ent = readdir(dir)) != 0)
- {
- if (ent->d_name[0] == '.')
- continue;
- l = strlen(ent->d_name);
- if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
- continue;
- snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
- if ((fp = fopen(buf, "r")) == 0)
- {
- perror(buf);
- continue;
- }
- cinfo = 0;
- while(fgets(buf2, sizeof(buf2), fp))
- {
- l = strlen(buf2);
- if (l == 0)
- continue;
- while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
- buf2[--l] = 0;
- kp = buf2;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp || *kp == '#')
- continue;
- if (strchr(kp, '$'))
- kp = yum_substitute(pool, kp);
- if (*kp == '[')
- {
- vp = strrchr(kp, ']');
- if (!vp)
- continue;
- *vp = 0;
- repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
- cinfo = repoinfos + nrepoinfos++;
- memset(cinfo, 0, sizeof(*cinfo));
- cinfo->alias = strdup(kp + 1);
- cinfo->type = TYPE_RPMMD;
- cinfo->autorefresh = 1;
- cinfo->priority = 99;
-#ifndef FEDORA
- cinfo->repo_gpgcheck = 1;
-#endif
- cinfo->metadata_expire = METADATA_EXPIRE;
- continue;
- }
- if (!cinfo)
- continue;
- vp = strchr(kp, '=');
- if (!vp)
- continue;
- for (kpe = vp - 1; kpe >= kp; kpe--)
- if (*kpe != ' ' && *kpe != '\t')
- break;
- if (kpe == kp)
- continue;
- vp++;
- while (*vp == ' ' || *vp == '\t')
- vp++;
- kpe[1] = 0;
- if (!strcmp(kp, "name"))
- cinfo->name = strdup(vp);
- else if (!strcmp(kp, "enabled"))
- cinfo->enabled = *vp == '0' ? 0 : 1;
- else if (!strcmp(kp, "autorefresh"))
- cinfo->autorefresh = *vp == '0' ? 0 : 1;
- else if (!strcmp(kp, "gpgcheck"))
- cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
- else if (!strcmp(kp, "repo_gpgcheck"))
- cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
- else if (!strcmp(kp, "baseurl"))
- cinfo->baseurl = strdup(vp);
- else if (!strcmp(kp, "mirrorlist"))
- {
- if (strstr(vp, "metalink"))
- cinfo->metalink = strdup(vp);
- else
- cinfo->mirrorlist = strdup(vp);
- }
- else if (!strcmp(kp, "path"))
- {
- if (vp && strcmp(vp, "/") != 0)
- cinfo->path = strdup(vp);
- }
- else if (!strcmp(kp, "type"))
- {
- if (!strcmp(vp, "yast2"))
- cinfo->type = TYPE_SUSETAGS;
- else if (!strcmp(vp, "rpm-md"))
- cinfo->type = TYPE_RPMMD;
- else if (!strcmp(vp, "plaindir"))
- cinfo->type = TYPE_PLAINDIR;
- else if (!strcmp(vp, "mdk"))
- cinfo->type = TYPE_MDK;
- else
- cinfo->type = TYPE_UNKNOWN;
- }
- else if (!strcmp(kp, "priority"))
- cinfo->priority = atoi(vp);
- else if (!strcmp(kp, "keeppackages"))
- cinfo->keeppackages = *vp == '0' ? 0 : 1;
- }
- fclose(fp);
- cinfo = 0;
- }
- closedir(dir);
- *nrepoinfosp = nrepoinfos;
- return repoinfos;
-}
-
-#endif
+++ /dev/null
-extern char *yum_substitute(Pool *pool, char *line);
-extern struct repoinfo *read_repoinfos_yum(Pool *pool, int *nrepoinfosp);
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "solv_xfopen.h"
-
-#include "repoinfo.h"
-#include "mirror.h"
-#include "checksig.h"
-#include "repoinfo_download.h"
-
-static inline int
-opentmpfile()
-{
- char tmpl[100];
- int fd;
-
- strcpy(tmpl, "/var/tmp/solvXXXXXX");
- fd = mkstemp(tmpl);
- if (fd < 0)
- {
- perror("mkstemp");
- exit(1);
- }
- unlink(tmpl);
- return fd;
-}
-
-int
-verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
-{
- char buf[1024];
- const unsigned char *sum;
- Chksum *h;
- int l;
-
- h = solv_chksum_create(chksumtype);
- if (!h)
- {
- printf("%s: unknown checksum type\n", file);
- return 0;
- }
- while ((l = read(fd, buf, sizeof(buf))) > 0)
- solv_chksum_add(h, buf, l);
- lseek(fd, 0, SEEK_SET);
- l = 0;
- sum = solv_chksum_get(h, &l);
- if (memcmp(sum, chksum, l))
- {
- printf("%s: checksum mismatch\n", file);
- solv_chksum_free(h, 0);
- return 0;
- }
- solv_chksum_free(h, 0);
- return 1;
-}
-
-FILE *
-curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete)
-{
- FILE *fp;
- pid_t pid;
- int fd;
- int status;
- char url[4096];
- const char *baseurl = cinfo->baseurl;
-
- if (!baseurl)
- {
- if (!cinfo->metalink && !cinfo->mirrorlist)
- return 0;
- if (file != cinfo->metalink && file != cinfo->mirrorlist)
- {
- unsigned char mlchksum[32];
- Id mlchksumtype = 0;
- fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
- if (!fp)
- return 0;
- if (cinfo->metalink)
- cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
- else
- cinfo->baseurl = findmirrorlisturl(fp);
- fclose(fp);
- if (!cinfo->baseurl)
- return 0;
-#ifdef FEDORA
- if (strchr(cinfo->baseurl, '$'))
- {
- char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
- free(cinfo->baseurl);
- cinfo->baseurl = strdup(b);
- }
-#endif
- if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
- {
- chksumtype = mlchksumtype;
- chksum = mlchksum;
- }
- return curlfopen(cinfo, file, uncompress, chksum, chksumtype, markincomplete);
- }
- snprintf(url, sizeof(url), "%s", file);
- }
- else
- {
- const char *path = cinfo->path && strcmp(cinfo->path, "/") != 0 ? cinfo->path : "";
- int l = strlen(baseurl);
- int pl = strlen(path);
- const char *sep = l && baseurl[l - 1] == '/' ? "" : "/";
- const char *psep = pl && cinfo->path[pl - 1] == '/' ? "" : "/";
- snprintf(url, sizeof(url), "%s%s%s%s%s", baseurl, sep, path, psep, file);
- }
- fd = opentmpfile();
- // printf("url: %s\n", url);
- if ((pid = fork()) == (pid_t)-1)
- {
- perror("fork");
- exit(1);
- }
- if (pid == 0)
- {
- if (fd != 1)
- {
- dup2(fd, 1);
- close(fd);
- }
- execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
- perror("curl");
- _exit(0);
- }
- status = 0;
- while (waitpid(pid, &status, 0) != pid)
- ;
- if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
- {
- /* empty file */
- close(fd);
- return 0;
- }
- lseek(fd, 0, SEEK_SET);
- if (status)
- {
- printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
- if (markincomplete)
- cinfo->incomplete = 1;
- close(fd);
- return 0;
- }
- if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
- {
- if (markincomplete)
- cinfo->incomplete = 1;
- close(fd);
- return 0;
- }
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (uncompress)
- {
- if (solv_xfopen_iscompressed(file) < 0)
- {
- printf("%s: unsupported compression\n", file);
- if (markincomplete)
- cinfo->incomplete = 1;
- close(fd);
- return 0;
- }
- fp = solv_xfopen_fd(file, fd, "r");
- }
- else
- fp = fdopen(fd, "r");
- if (!fp)
- close(fd);
- return fp;
-}
-
-FILE *
-downloadpackage(Solvable *s, const char *loc)
-{
- const unsigned char *chksum;
- Id chksumtype;
- struct repoinfo *cinfo = s->repo->appdata;
-
-#ifdef ENABLE_SUSEREPO
- if (cinfo->type == TYPE_SUSETAGS)
- {
- const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
- loc = pool_tmpjoin(s->repo->pool, datadir ? datadir : "suse", "/", loc);
- }
-#endif
- chksumtype = 0;
- chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
- return curlfopen(cinfo, loc, 0, chksum, chksumtype, 0);
-}
-
-int
-downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool)
-{
- FILE *sigfp;
- sigfp = curlfopen(cinfo, sigurl, 0, 0, 0, 0);
- if (!sigfp)
- {
- printf(" unsigned, skipped\n");
- return 0;
- }
- if (!*sigpool)
- *sigpool = read_sigs();
- if (!checksig(*sigpool, fp, sigfp))
- {
- printf(" checksig failed, skipped\n");
- fclose(sigfp);
- return 0;
- }
- fclose(sigfp);
- return 1;
-}
-
+++ /dev/null
-int verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype);
-
-FILE *curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete);
-
-FILE *downloadpackage(Solvable *s, const char *loc);
-int downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool);
-
+++ /dev/null
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_deb.h"
-#include "transaction.h"
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-#include "repoinfo_system_debian.h"
-
-static void
-rundpkg(const char *arg, const char *name, int dupfd3, const char *rootdir)
-{
- pid_t pid;
- int status;
-
- if ((pid = fork()) == (pid_t)-1)
- {
- perror("fork");
- exit(1);
- }
- if (pid == 0)
- {
- if (!rootdir)
- rootdir = "/";
- if (dupfd3 != -1 && dupfd3 != 3)
- {
- dup2(dupfd3, 3);
- close(dupfd3);
- }
- if (dupfd3 != -1)
- fcntl(3, F_SETFD, 0); /* clear CLOEXEC */
- if (strcmp(arg, "--install") == 0)
- execlp("dpkg", "dpkg", "--install", "--root", rootdir, "--force", "all", name, (char *)0);
- else
- execlp("dpkg", "dpkg", "--remove", "--root", rootdir, "--force", "all", name, (char *)0);
- perror("dpkg");
- _exit(0);
- }
- while (waitpid(pid, &status, 0) != pid)
- ;
- if (status)
- {
- printf("dpkg failed\n");
- exit(1);
- }
-}
-
-int
-read_installed_debian(struct repoinfo *cinfo)
-{
- struct stat stb;
- Repo *repo = cinfo->repo;
- Pool *pool = repo->pool;
-
- memset(&stb, 0, sizeof(stb));
- printf("dpgk database:");
- if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/dpkg/status"), &stb))
- memset(&stb, 0, sizeof(stb));
- calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, 0, cinfo->cookie);
- cinfo->cookieset = 1;
- if (usecachedrepo(cinfo, 0, 0))
- {
- printf(" cached\n");
- return 1;
- }
- if (repo_add_debdb(repo, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
- return 0;
- }
- repo_internalize(repo);
- writecachedrepo(cinfo, 0, 0);
- return 1;
-}
-
-void
-commit_transactionelement_debian(Pool *pool, Id type, Id p, FILE *fp)
-{
- Solvable *s = pool_id2solvable(pool, p);
- const char *rootdir = pool_get_rootdir(pool);
-
- switch(type)
- {
- case SOLVER_TRANSACTION_ERASE:
- rundpkg("--remove", pool_id2str(pool, s->name), 0, rootdir);
- break;
- case SOLVER_TRANSACTION_INSTALL:
- case SOLVER_TRANSACTION_MULTIINSTALL:
- rewind(fp);
- lseek(fileno(fp), 0, SEEK_SET);
- rundpkg("--install", "/dev/fd/3", fileno(fp), rootdir);
- break;
- default:
- break;
- }
-}
-
-#endif
+++ /dev/null
-int read_installed_debian(struct repoinfo *cinfo);
-void commit_transactionelement_debian(Pool *pool, Id type, Id p, FILE *fp);
+++ /dev/null
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_rpmdb.h"
-#if defined(ENABLE_SUSEREPO) && defined(SUSE)
-#include "repo_products.h"
-#endif
-#if defined(ENABLE_APPDATA)
-#include "repo_appdata.h"
-#endif
-#include "transaction.h"
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-#include "repoinfo_system_rpm.h"
-
-#ifdef SUSE
-# define PRODUCTS_PATH "/etc/products.d"
-#endif
-#ifdef ENABLE_APPDATA
-# define APPDATA_PATH "/usr/share/appdata"
-#endif
-
-static void
-runrpm(const char *arg, const char *name, int dupfd3, const char *rootdir)
-{
- pid_t pid;
- int status;
-
- if ((pid = fork()) == (pid_t)-1)
- {
- perror("fork");
- exit(1);
- }
- if (pid == 0)
- {
- if (!rootdir)
- rootdir = "/";
- if (dupfd3 != -1 && dupfd3 != 3)
- {
- dup2(dupfd3, 3);
- close(dupfd3);
- }
- if (dupfd3 != -1)
- fcntl(3, F_SETFD, 0); /* clear CLOEXEC */
- if (strcmp(arg, "-e") == 0)
- execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
- else
- execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
- perror("rpm");
- _exit(0);
- }
- while (waitpid(pid, &status, 0) != pid)
- ;
- if (status)
- {
- printf("rpm failed\n");
- exit(1);
- }
-}
-
-int
-read_installed_rpm(struct repoinfo *cinfo)
-{
- Repo *repo = cinfo->repo;
- Pool *pool = repo->pool;
- FILE *ofp = 0;
- struct stat stb;
-
- memset(&stb, 0, sizeof(stb));
- printf("rpm database:");
- if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/rpm/Packages"), &stb))
- memset(&stb, 0, sizeof(stb));
- calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, 0, cinfo->cookie);
- cinfo->cookieset = 1;
- if (usecachedrepo(cinfo, 0, 0))
- {
- printf(" cached\n");
- return 1;
- }
- printf(" reading\n");
-#if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
- if (repo_add_products(repo, PRODUCTS_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "product reading failed: %s\n", pool_errstr(pool));
- return 0;
- }
-#endif
-#if defined(ENABLE_APPDATA) && defined(APPDATA_PATH)
- if (repo_add_appdata_dir(repo, APPDATA_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "appdata reading failed: %s\n", pool_errstr(pool));
- return 0;
- }
-#endif
- ofp = fopen(calc_cachepath(repo, 0, 0), "r");
- if (repo_add_rpmdb_reffp(repo, ofp, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
- return 0;
- }
- if (ofp)
- fclose(ofp);
- repo_internalize(repo);
- writecachedrepo(cinfo, 0, 0);
- return 1;
-}
-
-void
-commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp)
-{
- Solvable *s = pool_id2solvable(pool, p);
- const char *rootdir = pool_get_rootdir(pool);
- const char *evr, *evrp, *nvra;
-
- switch(type)
- {
- case SOLVER_TRANSACTION_ERASE:
- if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
- break;
- /* strip epoch from evr */
- evr = evrp = pool_id2str(pool, s->evr);
- while (*evrp >= '0' && *evrp <= '9')
- evrp++;
- if (evrp > evr && evrp[0] == ':' && evrp[1])
- evr = evrp + 1;
- nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
- nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
- runrpm("-e", nvra, -1, rootdir); /* too bad that --querybynumber doesn't work */
- break;
- case SOLVER_TRANSACTION_INSTALL:
- case SOLVER_TRANSACTION_MULTIINSTALL:
- rewind(fp);
- lseek(fileno(fp), 0, SEEK_SET);
- runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp), rootdir);
- break;
- default:
- break;
- }
-}
-
-#endif
+++ /dev/null
-int read_installed_rpm(struct repoinfo *cinfo);
-void commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp);
+++ /dev/null
-#ifdef ENABLE_DEBIAN
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/utsname.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "repo_deb.h"
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-#include "repoinfo_download.h"
-#include "repoinfo_type_debian.h"
-
-static const char *
-debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
-{
- char buf[4096];
- Id chksumtype;
- unsigned char *chksum;
- Id curchksumtype;
- int l, compl;
- char *ch, *fn, *bp;
- char *filename;
- static char *basearch;
- char *binarydir;
- int lbinarydir;
-
- if (!basearch)
- {
- struct utsname un;
- if (uname(&un))
- {
- perror("uname");
- exit(1);
- }
- basearch = strdup(un.machine);
- if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
- basearch[1] = '3';
- }
- binarydir = solv_dupjoin("binary-", basearch, "/");
- lbinarydir = strlen(binarydir);
- compl = strlen(comp);
- rewind(fp);
- curchksumtype = 0;
- filename = 0;
- chksum = solv_malloc(32);
- chksumtype = 0;
- while(fgets(buf, sizeof(buf), fp))
- {
- l = strlen(buf);
- if (l == 0)
- continue;
- while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
- buf[--l] = 0;
- if (!strncasecmp(buf, "MD5Sum:", 7))
- {
- curchksumtype = REPOKEY_TYPE_MD5;
- continue;
- }
- if (!strncasecmp(buf, "SHA1:", 5))
- {
- curchksumtype = REPOKEY_TYPE_SHA1;
- continue;
- }
- if (!strncasecmp(buf, "SHA256:", 7))
- {
- curchksumtype = REPOKEY_TYPE_SHA256;
- continue;
- }
- if (!curchksumtype)
- continue;
- bp = buf;
- if (*bp++ != ' ')
- {
- curchksumtype = 0;
- continue;
- }
- ch = bp;
- while (*bp && *bp != ' ' && *bp != '\t')
- bp++;
- if (!*bp)
- continue;
- *bp++ = 0;
- while (*bp == ' ' || *bp == '\t')
- bp++;
- while (*bp && *bp != ' ' && *bp != '\t')
- bp++;
- if (!*bp)
- continue;
- while (*bp == ' ' || *bp == '\t')
- bp++;
- fn = bp;
- if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
- continue;
- bp += compl + 1;
- if (strncmp(bp, binarydir, lbinarydir))
- continue;
- bp += lbinarydir;
- if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
- {
- unsigned char curchksum[32];
- int curl;
- if (filename && !strcmp(bp, "Packages"))
- continue;
- curl = solv_chksum_len(curchksumtype);
- if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
- continue;
- if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
- continue;
- solv_free(filename);
- filename = strdup(fn);
- chksumtype = curchksumtype;
- memcpy(chksum, curchksum, curl);
- }
- }
- free(binarydir);
- if (filename)
- {
- fn = solv_dupjoin("/", filename, 0);
- solv_free(filename);
- filename = solv_dupjoin("dists/", cinfo->name, fn);
- solv_free(fn);
- }
- if (!chksumtype)
- chksum = solv_free(chksum);
- *chksump = chksum;
- *chksumtypep = chksumtype;
- return filename;
-}
-
-int
-debian_load(struct repoinfo *cinfo, Pool **sigpoolp)
-{
- Repo *repo = cinfo->repo;
- Pool *pool = repo->pool;
- const char *filename;
- const unsigned char *filechksum;
- Id filechksumtype;
- FILE *fp, *fpr;
- int j;
-
- printf("debian repo '%s':", cinfo->alias);
- fflush(stdout);
- filename = solv_dupjoin("dists/", cinfo->name, "/Release");
- if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
- {
- printf(" no Release file\n");
- free((char *)filename);
- cinfo->incomplete = 1;
- return 0;
- }
- solv_free((char *)filename);
- if (cinfo->repo_gpgcheck)
- {
- filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
- if (!downloadchecksig(cinfo, fpr, filename, sigpoolp))
- {
- fclose(fpr);
- solv_free((char *)filename);
- cinfo->incomplete = 1;
- return 0;
- }
- solv_free((char *)filename);
- }
- calc_cookie_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
- cinfo->cookieset = 1;
- if (usecachedrepo(cinfo, 0, 1))
- {
- printf(" cached\n");
- fclose(fpr);
- return 1;
- }
- printf(" fetching\n");
- for (j = 0; j < cinfo->ncomponents; j++)
- {
- if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
- {
- printf("[component %s not found]\n", cinfo->components[j]);
- continue;
- }
- if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_debpackages(repo, fp, 0))
- {
- printf("component %s: %s\n", cinfo->components[j], pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
- solv_free((char *)filechksum);
- solv_free((char *)filename);
- }
- fclose(fpr);
- writecachedrepo(cinfo, 0, 0);
- return 1;
-}
-
-#endif
+++ /dev/null
-extern int debian_load(struct repoinfo *cinfo, Pool **sigpoolp);
-
+++ /dev/null
-#ifdef ENABLE_MDKREPO
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "repo_mdk.h"
-#include "solv_xfopen.h"
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-#include "repoinfo_download.h"
-#include "repoinfo_type_mdk.h"
-
-static int
-mdk_find(const char *md5sums, const char *what, unsigned char *chksum)
-{
- const char *sp, *ep;
- int wl = strlen(what);
- for (sp = md5sums; (ep = strchr(sp, '\n')) != 0; sp = ep + 1)
- {
- int l = ep - sp;
- if (l <= 34)
- continue;
- if (sp[32] != ' ' || sp[33] != ' ')
- continue;
- if (wl != l - 34 || strncmp(what, sp + 34, wl) != 0)
- continue;
- if (solv_hex2bin(&sp, chksum, 16) != 16)
- continue;
- return 1;
- }
- return 0;
-}
-
-static char *
-slurp(FILE *fp)
-{
- int l, ll;
- char *buf = 0;
- int bufl = 0;
-
- for (l = 0; ; l += ll)
- {
- if (bufl - l < 4096)
- {
- bufl += 4096;
- buf = solv_realloc(buf, bufl);
- }
- ll = fread(buf + l, 1, bufl - l, fp);
- if (ll < 0)
- {
- buf = solv_free(buf);
- l = 0;
- break;
- }
- if (ll == 0)
- {
- buf[l] = 0;
- break;
- }
- }
- return buf;
-}
-
-int
-mdk_load_ext(Repo *repo, Repodata *data)
-{
- struct repoinfo *cinfo = repo->appdata;
- const char *type, *ext, *filename;
- const unsigned char *filechksum;
- Id filechksumtype;
- int r = 0;
- FILE *fp;
-
- type = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
- if (strcmp(type, "filelists") != 0)
- return 0;
- ext = "FL";
- printf("[%s:%s", repo->name, ext);
- if (usecachedrepo(cinfo, ext, 0))
- {
- printf(" cached]\n"); fflush(stdout);
- return 1;
- }
- printf(" fetching]\n"); fflush(stdout);
- filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
- filechksumtype = 0;
- filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
- if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 0)) == 0)
- return 0;
- r = repo_add_mdk_info(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
- fclose(fp);
- if (r)
- {
- printf("%s\n", pool_errstr(repo->pool));
- return 0;
- }
- writecachedrepo(cinfo, ext, data);
- return 1;
-}
-
-int
-mdk_load(struct repoinfo *cinfo, Pool **sigpoolp)
-{
- Repo *repo = cinfo->repo;
- Pool *pool = repo->pool;
- Repodata *data;
- const char *compression;
- FILE *fp, *cfp;
- char *md5sums;
- unsigned char probe[5];
- unsigned char md5[16];
-
- printf("mdk repo '%s':", cinfo->alias);
- fflush(stdout);
- if ((fp = curlfopen(cinfo, "media_info/MD5SUM", 0, 0, 0, 0)) == 0)
- {
- printf(" no media_info/MD5SUM file\n");
- cinfo->incomplete = 1;
- return 0;
- }
- calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
- cinfo->cookieset = 1;
- if (usecachedrepo(cinfo, 0, 1))
- {
- printf(" cached\n");
- fclose(fp);
- return 1;
- }
- md5sums = slurp(fp);
- fclose(fp);
- printf(" fetching\n");
- if (!mdk_find(md5sums, "synthesis.hdlist.cz", md5))
- {
- solv_free(md5sums);
- cinfo->incomplete = 1;
- return 0; /* hopeless */
- }
- if ((fp = curlfopen(cinfo, "media_info/synthesis.hdlist.cz", 0, md5, REPOKEY_TYPE_MD5, 1)) == 0)
- {
- solv_free(md5sums);
- cinfo->incomplete = 1;
- return 0; /* hopeless */
- }
- /* probe compression */
- if (fread(probe, 5, 1, fp) != 1)
- {
- fclose(fp);
- solv_free(md5sums);
- cinfo->incomplete = 1;
- return 0; /* hopeless */
- }
- if (probe[0] == 0xfd && memcmp(probe + 1, "7zXZ", 4) == 0)
- compression = "synthesis.hdlist.xz";
- else
- compression = "synthesis.hdlist.gz";
- lseek(fileno(fp), 0, SEEK_SET);
- cfp = solv_xfopen_fd(compression, dup(fileno(fp)), "r");
- fclose(fp);
- fp = cfp;
- if (!fp)
- {
- solv_free(md5sums);
- cinfo->incomplete = 1;
- return 0; /* hopeless */
- }
- if (repo_add_mdk(repo, fp, REPO_NO_INTERNALIZE))
- {
- printf("synthesis.hdlist.cz: %s\n", pool_errstr(pool));
- fclose(fp);
- solv_free(md5sums);
- cinfo->incomplete = 1;
- return 0; /* hopeless */
- }
- fclose(fp);
- /* add info, could do this on demand, but always having the summary is nice */
- if (mdk_find(md5sums, "info.xml.lzma", md5))
- {
- if ((fp = curlfopen(cinfo, "media_info/info.xml.lzma", 1, md5, REPOKEY_TYPE_MD5, 1)) != 0)
- {
- if (repo_add_mdk_info(repo, fp, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
- {
- printf("info.xml.lzma: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
- }
- repo_internalize(repo);
- data = repo_add_repodata(repo, 0);
- /* setup on-demand loading of filelist data */
- if (mdk_find(md5sums, "files.xml.lzma", md5))
- {
- Id handle = repodata_new_handle(data);
- /* we mis-use the repomd ids here... need something generic in the future */
- repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, "filelists");
- repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, "media_info/files.xml.lzma");
- repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, REPOKEY_TYPE_MD5, md5);
- add_ext_keys(data, handle, "FL");
- repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
- }
- solv_free(md5sums);
- repodata_internalize(data);
- writecachedrepo(cinfo, 0, 0);
- repodata_create_stubs(repo_last_repodata(repo));
- return 1;
-}
-
-#endif
+++ /dev/null
-extern int mdk_load(struct repoinfo *cinfo, Pool **sigpoolp);
-extern int mdk_load_ext(Repo *repo, Repodata *data);
-
+++ /dev/null
-#ifdef ENABLE_RPMMD
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "repo_rpmmd.h"
-#include "repo_deltainfoxml.h"
-#include "repo_updateinfoxml.h"
-#include "repo_repomdxml.h"
-#ifdef ENABLE_APPDATA
-#include "repo_appdata.h"
-#endif
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-#include "repoinfo_download.h"
-#include "repoinfo_type_rpmmd.h"
-
-
-
-static const char *
-repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
-{
- Pool *pool = repo->pool;
- Dataiterator di;
- const char *filename;
-
- filename = 0;
- *chksump = 0;
- *chksumtypep = 0;
- dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
- dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
- if (dataiterator_step(&di))
- {
- dataiterator_setpos_parent(&di);
- filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
- *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
- }
- dataiterator_free(&di);
- if (filename && !*chksumtypep)
- {
- printf("no %s file checksum!\n", what);
- filename = 0;
- }
- return filename;
-}
-
-static void
-repomd_add_ext(Repo *repo, Repodata *data, const char *what, const char *ext)
-{
- Id chksumtype, handle;
- const unsigned char *chksum;
- const char *filename;
-
- filename = repomd_find(repo, what, &chksum, &chksumtype);
- if (!filename && !strcmp(what, "deltainfo"))
- filename = repomd_find(repo, "prestodelta", &chksum, &chksumtype);
- if (!filename)
- return;
- handle = repodata_new_handle(data);
- repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
- repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
- repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
- add_ext_keys(data, handle, ext);
- repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
-}
-
-int
-repomd_load_ext(Repo *repo, Repodata *data)
-{
- const char *filename, *repomdtype;
- char ext[3];
- FILE *fp;
- struct repoinfo *cinfo;
- const unsigned char *filechksum;
- Id filechksumtype;
- int r = 0;
-
- cinfo = repo->appdata;
- repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
- if (!repomdtype)
- return 0;
- if (!strcmp(repomdtype, "filelists"))
- strcpy(ext, "FL");
- else if (!strcmp(repomdtype, "deltainfo"))
- strcpy(ext, "DL");
- else
- return 0;
- printf("[%s:%s", repo->name, ext);
- if (usecachedrepo(cinfo, ext, 0))
- {
- printf(" cached]\n"); fflush(stdout);
- return 1;
- }
- printf(" fetching]\n"); fflush(stdout);
- filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
- filechksumtype = 0;
- filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
- if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 0)) == 0)
- return 0;
- if (!strcmp(ext, "FL"))
- r = repo_add_rpmmd(repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
- else if (!strcmp(ext, "DL"))
- r = repo_add_deltainfoxml(repo, fp, REPO_USE_LOADING);
- fclose(fp);
- if (r)
- {
- printf("%s\n", pool_errstr(repo->pool));
- return 0;
- }
- if (cinfo->extcookieset)
- writecachedrepo(cinfo, ext, data);
- return 1;
-}
-
-int
-repomd_load(struct repoinfo *cinfo, Pool **sigpoolp)
-{
- Repo *repo = cinfo->repo;
- Pool *pool = repo->pool;
- Repodata *data;
- const char *filename;
- const unsigned char *filechksum;
- Id filechksumtype;
- FILE *fp;
-
- printf("rpmmd repo '%s':", cinfo->alias);
- fflush(stdout);
- if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
- {
- printf(" no repomd.xml file\n");
- cinfo->incomplete = 1;
- return 0;
- }
- calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
- cinfo->cookieset = 1;
- if (usecachedrepo(cinfo, 0, 1))
- {
- printf(" cached\n");
- fclose(fp);
- return 1;
- }
- if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "repodata/repomd.xml.asc", sigpoolp))
- {
- fclose(fp);
- cinfo->incomplete = 1;
- return 0;
- }
- if (repo_add_repomdxml(repo, fp, 0))
- {
- printf("repomd.xml: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- fclose(fp);
- return 0;
- }
- fclose(fp);
- printf(" fetching\n");
- filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
- if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_rpmmd(repo, fp, 0, 0))
- {
- printf("primary: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
- if (cinfo->incomplete)
- return 0; /* hopeless */
-
- filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
- if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_updateinfoxml(repo, fp, 0))
- {
- printf("updateinfo: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
-
-#ifdef ENABLE_APPDATA
- filename = repomd_find(repo, "appdata", &filechksum, &filechksumtype);
- if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_appdata(repo, fp, 0))
- {
- printf("appdata: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
-#endif
- data = repo_add_repodata(repo, 0);
- repomd_add_ext(repo, data, "deltainfo", "DL");
- repomd_add_ext(repo, data, "filelists", "FL");
- repodata_internalize(data);
- writecachedrepo(cinfo, 0, 0);
- repodata_create_stubs(repo_last_repodata(repo));
- return 1;
-}
-
-#endif
+++ /dev/null
-extern int repomd_load_ext(Repo *repo, Repodata *data);
-extern int repomd_load(struct repoinfo *cinfo, Pool **sigpoolp);
+++ /dev/null
-#ifdef ENABLE_SUSEREPO
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "repo_content.h"
-#include "repo_susetags.h"
-#ifdef ENABLE_APPDATA
-#include "repo_appdata.h"
-#endif
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-#include "repoinfo_download.h"
-#include "repoinfo_type_susetags.h"
-
-/* susetags helpers */
-
-static const char *
-susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
-{
- Pool *pool = repo->pool;
- Dataiterator di;
- const char *filename;
-
- filename = 0;
- *chksump = 0;
- *chksumtypep = 0;
- dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
- dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
- if (dataiterator_step(&di))
- {
- dataiterator_setpos_parent(&di);
- *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
- filename = what;
- }
- dataiterator_free(&di);
- if (filename && !*chksumtypep)
- {
- printf("no %s file checksum!\n", what);
- filename = 0;
- }
- return filename;
-}
-
-void
-susetags_add_ext(Repo *repo, Repodata *data)
-{
- Pool *pool = repo->pool;
- Dataiterator di;
- char ext[3];
- Id handle, filechksumtype;
- const unsigned char *filechksum;
-
- dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
- dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
- while (dataiterator_step(&di))
- {
- if (strncmp(di.kv.str, "packages.", 9) != 0)
- continue;
- if (!strcmp(di.kv.str + 9, "gz"))
- continue;
- if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
- continue;
- ext[0] = di.kv.str[9];
- ext[1] = di.kv.str[10];
- ext[2] = 0;
- if (!strcmp(ext, "en"))
- continue;
- if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
- continue;
- handle = repodata_new_handle(data);
- repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
- if (filechksumtype)
- repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
- add_ext_keys(data, handle, ext);
- repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
- }
- dataiterator_free(&di);
-}
-
-int
-susetags_load_ext(Repo *repo, Repodata *data)
-{
- const char *filename, *descrdir;
- Id defvendor;
- char ext[3];
- FILE *fp;
- struct repoinfo *cinfo;
- const unsigned char *filechksum;
- Id filechksumtype;
- int flags;
-
- cinfo = repo->appdata;
- filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
- if (!filename)
- return 0;
- /* susetags load */
- ext[0] = filename[9];
- ext[1] = filename[10];
- ext[2] = 0;
- printf("[%s:%s", repo->name, ext);
- if (usecachedrepo(cinfo, ext, 0))
- {
- printf(" cached]\n"); fflush(stdout);
- return 1;
- }
- printf(" fetching]\n"); fflush(stdout);
- defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
- descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
- if (!descrdir)
- descrdir = "suse/setup/descr";
- filechksumtype = 0;
- filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
- if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 0)) == 0)
- return 0;
- flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
- if (strcmp(ext, "DL") != 0)
- flags |= REPO_LOCALPOOL;
- if (repo_add_susetags(repo, fp, defvendor, ext, flags))
- {
- fclose(fp);
- printf("%s\n", pool_errstr(repo->pool));
- return 0;
- }
- fclose(fp);
- writecachedrepo(cinfo, ext, data);
- return 1;
-}
-
-int
-susetags_load(struct repoinfo *cinfo, Pool **sigpoolp)
-{
- Repo *repo = cinfo->repo;
- Pool *pool = repo->pool;
- Repodata *data;
- const char *filename;
- const unsigned char *filechksum;
- Id filechksumtype;
- FILE *fp;
- const char *descrdir;
- int defvendor;
-
- printf("susetags repo '%s':", cinfo->alias);
- fflush(stdout);
- descrdir = 0;
- defvendor = 0;
- if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
- {
- printf(" no content file\n");
- cinfo->incomplete = 1;
- return 0;
- }
- calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
- cinfo->cookieset = 1;
- if (usecachedrepo(cinfo, 0, 1))
- {
- printf(" cached\n");
- fclose(fp);
- return 1;
- }
- if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "content.asc", sigpoolp))
- {
- fclose(fp);
- cinfo->incomplete = 1;
- return 0;
- }
- if (repo_add_content(repo, fp, 0))
- {
- printf("content: %s\n", pool_errstr(pool));
- fclose(fp);
- cinfo->incomplete = 1;
- return 0;
- }
- fclose(fp);
- defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
- descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
- if (!descrdir)
- descrdir = "suse/setup/descr";
- filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
- if (!filename)
- filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
- if (!filename)
- {
- printf(" no packages file entry, skipped\n");
- cinfo->incomplete = 1;
- return 0;
- }
- printf(" fetching\n");
- if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) == 0)
- {
- cinfo->incomplete = 1;
- return 0; /* hopeless */
- }
- if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES))
- {
- printf("packages: %s\n", pool_errstr(pool));
- fclose(fp);
- cinfo->incomplete = 1;
- return 0; /* hopeless */
- }
- fclose(fp);
- /* add default language */
- filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
- if (!filename)
- filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
- if (filename)
- {
- if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
- {
- printf("packages.en: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
- }
- filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
- if (filename)
- {
- if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
- {
- char pbuf[256];
- while (fgets(pbuf, sizeof(pbuf), fp))
- {
- int l = strlen(pbuf);
- FILE *fp2;
- if (l && pbuf[l - 1] == '\n')
- pbuf[--l] = 0;
- if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
- continue;
- filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
- if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE))
- {
- printf("%s: %s\n", pbuf, pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp2);
- }
- }
- fclose(fp);
- }
- }
-#ifdef ENABLE_APPDATA
- filename = susetags_find(repo, "appdata.xml.gz", &filechksum, &filechksumtype);
- if (!filename)
- filename = susetags_find(repo, "appdata.xml", &filechksum, &filechksumtype);
- if (filename && (fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_appdata(repo, fp, 0))
- {
- printf("appdata: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
-#endif
- repo_internalize(repo);
- data = repo_add_repodata(repo, 0);
- susetags_add_ext(repo, data);
- repodata_internalize(data);
- writecachedrepo(cinfo, 0, 0);
- repodata_create_stubs(repo_last_repodata(repo));
- return 1;
-}
-
-#endif
+++ /dev/null
-extern int susetags_load_ext(Repo *repo, Repodata *data);
-extern int susetags_load(struct repoinfo *cinfo, Pool **sigpoolp);
+++ /dev/null
-/*
- * Copyright (c) 2009-2015, SUSE LLC.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/* solv, a little software installer demoing the sat solver library */
-
-/* things it does:
- * - understands globs for package names / dependencies
- * - understands .arch suffix
- * - installation of commandline packages
- * - repository data caching
- * - on demand loading of secondary repository data
- * - gpg and checksum verification
- * - file conflicts
- * - deltarpm support
- * - fastestmirror implementation
- *
- * things available in the library but missing from solv:
- * - vendor policy loading
- * - multi version handling
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/utsname.h>
-
-#include "pool.h"
-#include "poolarch.h"
-#include "evr.h"
-#include "selection.h"
-#include "repo.h"
-#include "solver.h"
-#include "solverdebug.h"
-#include "transaction.h"
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-
-#include "repoinfo.h"
-#include "repoinfo_cache.h"
-#include "repoinfo_download.h"
-
-#if defined(ENABLE_RPMDB)
-#include "fileprovides.h"
-#include "fileconflicts.h"
-#include "deltarpm.h"
-#endif
-#if defined(SUSE) || defined(FEDORA)
-#include "patchjobs.h"
-#endif
-
-void
-setarch(Pool *pool)
-{
- struct utsname un;
- if (uname(&un))
- {
- perror("uname");
- exit(1);
- }
- pool_setarch(pool, un.machine);
-}
-
-
-int
-yesno(const char *str)
-{
- char inbuf[128], *ip;
-
- for (;;)
- {
- printf("%s", str);
- fflush(stdout);
- *inbuf = 0;
- if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
- {
- printf("Abort.\n");
- exit(1);
- }
- while (*ip == ' ' || *ip == '\t')
- ip++;
- if (*ip == 'q')
- {
- printf("Abort.\n");
- exit(1);
- }
- if (*ip == 'y' || *ip == 'n')
- return *ip == 'y' ? 1 : 0;
- }
-}
-
-#ifdef SUSE
-static Id
-nscallback(Pool *pool, void *data, Id name, Id evr)
-{
-#if 0
- if (name == NAMESPACE_LANGUAGE)
- {
- if (!strcmp(pool_id2str(pool, evr), "ja"))
- return 1;
- if (!strcmp(pool_id2str(pool, evr), "de"))
- return 1;
- if (!strcmp(pool_id2str(pool, evr), "en"))
- return 1;
- if (!strcmp(pool_id2str(pool, evr), "en_US"))
- return 1;
- }
-#endif
- return 0;
-}
-#endif
-
-#ifdef SUSE
-static void
-add_autopackages(Pool *pool)
-{
- int i;
- Repo *repo;
- FOR_REPOS(i, repo)
- repo_add_autopattern(repo, 0);
-}
-#endif
-
-#ifdef SUSE
-static void
-showdiskusagechanges(Transaction *trans)
-{
- DUChanges duc[4];
- int i;
-
- /* XXX: use mountpoints here */
- memset(duc, 0, sizeof(duc));
- duc[0].path = "/";
- duc[1].path = "/usr/share/man";
- duc[2].path = "/sbin";
- duc[3].path = "/etc";
- transaction_calc_duchanges(trans, duc, 4);
- for (i = 0; i < 4; i++)
- printf("duchanges %s: %d K %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
-}
-#endif
-
-static Id
-find_repo(const char *name, Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
-{
- const char *rp;
- int i;
-
- for (rp = name; *rp; rp++)
- if (*rp <= '0' || *rp >= '9')
- break;
- if (!*rp)
- {
- /* repo specified by number */
- int rnum = atoi(name);
- for (i = 0; i < nrepoinfos; i++)
- {
- struct repoinfo *cinfo = repoinfos + i;
- if (!cinfo->enabled || !cinfo->repo)
- continue;
- if (--rnum == 0)
- return cinfo->repo->repoid;
- }
- }
- else
- {
- /* repo specified by alias */
- Repo *repo;
- FOR_REPOS(i, repo)
- {
- if (!strcasecmp(name, repo->name))
- return repo->repoid;
- }
- }
- return 0;
-}
-
-
-#define MODE_LIST 0
-#define MODE_INSTALL 1
-#define MODE_ERASE 2
-#define MODE_UPDATE 3
-#define MODE_DISTUPGRADE 4
-#define MODE_VERIFY 5
-#define MODE_PATCH 6
-#define MODE_INFO 7
-#define MODE_REPOLIST 8
-#define MODE_SEARCH 9
-
-void
-usage(int r)
-{
- fprintf(stderr, "Usage: solv COMMAND <select>\n");
- fprintf(stderr, "\n");
- fprintf(stderr, " dist-upgrade: replace installed packages with\n");
- fprintf(stderr, " versions from the repositories\n");
- fprintf(stderr, " erase: erase installed packages\n");
- fprintf(stderr, " info: display package information\n");
- fprintf(stderr, " install: install packages\n");
- fprintf(stderr, " list: list packages\n");
- fprintf(stderr, " repos: list enabled repositories\n");
- fprintf(stderr, " search: search name/summary/description\n");
- fprintf(stderr, " update: update installed packages\n");
- fprintf(stderr, " verify: check dependencies of installed packages\n");
-#if defined(SUSE) || defined(FEDORA)
- fprintf(stderr, " patch: install newest maintenance updates\n");
-#endif
- fprintf(stderr, "\n");
- exit(r);
-}
-
-int
-main(int argc, char **argv)
-{
- Pool *pool;
- Repo *commandlinerepo = 0;
- Id *commandlinepkgs = 0;
- Id p;
- struct repoinfo *repoinfos, installedrepoinfo;
- int nrepoinfos = 0;
- int mainmode = 0, mode = 0;
- int i, newpkgs;
- Queue job, checkq;
- Solver *solv = 0;
- Transaction *trans;
- FILE **newpkgsfps;
- Queue repofilter;
- Queue kindfilter;
- Queue archfilter;
- int archfilter_src = 0;
- int cleandeps = 0;
- int forcebest = 0;
- char *rootdir = 0;
- char *keyname = 0;
- int debuglevel = 0;
-
- argc--;
- argv++;
- while (argc && !strcmp(argv[0], "-d"))
- {
- debuglevel++;
- argc--;
- argv++;
- }
- if (!argv[0])
- usage(1);
- if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
- {
- mainmode = MODE_INSTALL;
- mode = SOLVER_INSTALL;
- }
-#if defined(SUSE) || defined(FEDORA)
- else if (!strcmp(argv[0], "patch"))
- {
- mainmode = MODE_PATCH;
- mode = SOLVER_INSTALL;
- }
-#endif
- else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
- {
- mainmode = MODE_ERASE;
- mode = SOLVER_ERASE;
- }
- else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
- {
- mainmode = MODE_LIST;
- mode = 0;
- }
- else if (!strcmp(argv[0], "info"))
- {
- mainmode = MODE_INFO;
- mode = 0;
- }
- else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
- {
- mainmode = MODE_SEARCH;
- mode = 0;
- }
- else if (!strcmp(argv[0], "verify"))
- {
- mainmode = MODE_VERIFY;
- mode = SOLVER_VERIFY;
- }
- else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
- {
- mainmode = MODE_UPDATE;
- mode = SOLVER_UPDATE;
- }
- else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
- {
- mainmode = MODE_DISTUPGRADE;
- mode = SOLVER_DISTUPGRADE;
- }
- else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
- {
- mainmode = MODE_REPOLIST;
- mode = 0;
- }
- else
- usage(1);
-
- for (;;)
- {
- if (argc > 2 && !strcmp(argv[1], "--root"))
- {
- rootdir = argv[2];
- argc -= 2;
- argv += 2;
- }
- else if (argc > 1 && !strcmp(argv[1], "--clean"))
- {
- cleandeps = 1;
- argc--;
- argv++;
- }
- else if (argc > 1 && !strcmp(argv[1], "--best"))
- {
- forcebest = 1;
- argc--;
- argv++;
- }
- if (argc > 2 && !strcmp(argv[1], "--keyname"))
- {
- keyname = argv[2];
- argc -= 2;
- argv += 2;
- }
- else
- break;
- }
-
- set_userhome();
- pool = pool_create();
- pool_set_rootdir(pool, rootdir);
-
-#if 0
- {
- const char *langs[] = {"de_DE", "de", "en"};
- pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
- }
-#endif
-
- pool_setloadcallback(pool, load_stub, 0);
-#ifdef SUSE
- pool->nscallback = nscallback;
-#endif
- if (debuglevel)
- pool_setdebuglevel(pool, debuglevel);
- setarch(pool);
- pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
- repoinfos = read_repoinfos(pool, &nrepoinfos);
- sort_repoinfos(repoinfos, nrepoinfos);
-
- if (mainmode == MODE_REPOLIST)
- {
- int j = 1;
- for (i = 0; i < nrepoinfos; i++)
- {
- struct repoinfo *cinfo = repoinfos + i;
- if (!cinfo->enabled)
- continue;
- printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
- }
- exit(0);
- }
- memset(&installedrepoinfo, 0, sizeof(installedrepoinfo));
- if (!read_installed_repo(&installedrepoinfo, pool))
- exit(1);
- read_repos(pool, repoinfos, nrepoinfos);
-
- /* setup filters */
- queue_init(&repofilter);
- queue_init(&kindfilter);
- queue_init(&archfilter);
- while (argc > 1)
- {
- if (!strcmp(argv[1], "-i"))
- {
- queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
- argc--;
- argv++;
- }
- else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
- {
- Id repoid = find_repo(argv[2], pool, repoinfos, nrepoinfos);
- if (!repoid)
- {
- fprintf(stderr, "%s: no such repo\n", argv[2]);
- exit(1);
- }
- /* SETVENDOR is actually wrong but useful */
- queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
- argc -= 2;
- argv += 2;
- }
- else if (argc > 2 && !strcmp(argv[1], "--arch"))
- {
- if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
- archfilter_src = 1;
- queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
- argc -= 2;
- argv += 2;
- }
- else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
- {
- const char *kind = argv[2];
- if (!strcmp(kind, "srcpackage"))
- {
- /* hey! should use --arch! */
- queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
- archfilter_src = 1;
- argc -= 2;
- argv += 2;
- continue;
- }
- if (!strcmp(kind, "package"))
- kind = "";
- if (!strcmp(kind, "all"))
- queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
- else
- queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
- argc -= 2;
- argv += 2;
- }
- else
- break;
- }
-
- if (mainmode == MODE_SEARCH)
- {
- Queue sel, q;
- Dataiterator di;
- if (argc != 2)
- usage(1);
- pool_createwhatprovides(pool);
- queue_init(&sel);
- dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
- dataiterator_set_keyname(&di, SOLVABLE_NAME);
- dataiterator_set_search(&di, 0, 0);
- while (dataiterator_step(&di))
- queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
- dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
- dataiterator_set_search(&di, 0, 0);
- while (dataiterator_step(&di))
- queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
- dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
- dataiterator_set_search(&di, 0, 0);
- while (dataiterator_step(&di))
- queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
- dataiterator_free(&di);
- if (repofilter.count)
- selection_filter(pool, &sel, &repofilter);
-
- queue_init(&q);
- selection_solvables(pool, &sel, &q);
- queue_free(&sel);
- for (i = 0; i < q.count; i++)
- {
- Solvable *s = pool_id2solvable(pool, q.elements[i]);
- printf(" - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
- }
- queue_free(&q);
- exit(0);
- }
-
- /* process command line packages */
- if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
- {
- for (i = 1; i < argc; i++)
- {
- if (!is_cmdline_package((const char *)argv[i]))
- continue;
- if (access(argv[i], R_OK))
- {
- perror(argv[i]);
- exit(1);
- }
- if (!commandlinepkgs)
- commandlinepkgs = solv_calloc(argc, sizeof(Id));
- if (!commandlinerepo)
- commandlinerepo = repo_create(pool, "@commandline");
- p = add_cmdline_package(commandlinerepo, (const char *)argv[i]);
- if (!p)
- {
- fprintf(stderr, "could not add '%s'\n", argv[i]);
- exit(1);
- }
- commandlinepkgs[i] = p;
- }
- if (commandlinerepo)
- repo_internalize(commandlinerepo);
- }
-
-#if defined(ENABLE_RPMDB)
- if (pool->disttype == DISTTYPE_RPM)
- addfileprovides(pool);
-#endif
-#ifdef SUSE
- add_autopackages(pool);
-#endif
- pool_createwhatprovides(pool);
-
- if (keyname)
- keyname = solv_dupjoin("solvable:", keyname, 0);
- queue_init(&job);
- for (i = 1; i < argc; i++)
- {
- Queue job2;
- int flags, rflags;
-
- if (commandlinepkgs && commandlinepkgs[i])
- {
- queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
- continue;
- }
- queue_init(&job2);
- flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
- flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
- if (kindfilter.count)
- flags |= SELECTION_SKIP_KIND;
- if (mode == MODE_LIST || archfilter_src)
- flags |= SELECTION_WITH_SOURCE;
- if (argv[i][0] == '/')
- flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
- if (!keyname)
- rflags = selection_make(pool, &job2, argv[i], flags);
- else
- rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
- if (repofilter.count)
- selection_filter(pool, &job2, &repofilter);
- if (archfilter.count)
- selection_filter(pool, &job2, &archfilter);
- if (kindfilter.count)
- selection_filter(pool, &job2, &kindfilter);
- if (!job2.count)
- {
- flags |= SELECTION_NOCASE;
- if (!keyname)
- rflags = selection_make(pool, &job2, argv[i], flags);
- else
- rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
- if (repofilter.count)
- selection_filter(pool, &job2, &repofilter);
- if (archfilter.count)
- selection_filter(pool, &job2, &archfilter);
- if (kindfilter.count)
- selection_filter(pool, &job2, &kindfilter);
- if (job2.count)
- printf("[ignoring case for '%s']\n", argv[i]);
- }
- if (!job2.count)
- {
- fprintf(stderr, "nothing matches '%s'\n", argv[i]);
- exit(1);
- }
- if (rflags & SELECTION_FILELIST)
- printf("[using file list match for '%s']\n", argv[i]);
- if (rflags & SELECTION_PROVIDES)
- printf("[using capability match for '%s']\n", argv[i]);
- queue_insertn(&job, job.count, job2.count, job2.elements);
- queue_free(&job2);
- }
- keyname = solv_free(keyname);
-
- if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
- {
- queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
- if (repofilter.count)
- selection_filter(pool, &job, &repofilter);
- if (archfilter.count)
- selection_filter(pool, &job, &archfilter);
- if (kindfilter.count)
- selection_filter(pool, &job, &kindfilter);
- }
- queue_free(&repofilter);
- queue_free(&archfilter);
- queue_free(&kindfilter);
-
- if (!job.count && mainmode != MODE_PATCH)
- {
- printf("no package matched\n");
- exit(1);
- }
-
- if (mainmode == MODE_LIST || mainmode == MODE_INFO)
- {
- /* list mode, no solver needed */
- Queue q;
- queue_init(&q);
- for (i = 0; i < job.count; i += 2)
- {
- int j;
- queue_empty(&q);
- pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
- for (j = 0; j < q.count; j++)
- {
- Solvable *s = pool_id2solvable(pool, q.elements[j]);
- if (mainmode == MODE_INFO)
- {
- const char *str;
- printf("Name: %s\n", pool_solvable2str(pool, s));
- printf("Repo: %s\n", s->repo->name);
- printf("Summary: %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
- str = solvable_lookup_str(s, SOLVABLE_URL);
- if (str)
- printf("Url: %s\n", str);
- str = solvable_lookup_str(s, SOLVABLE_LICENSE);
- if (str)
- printf("License: %s\n", str);
- printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
- printf("\n");
- }
- else
- {
-#if 1
- const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
-#else
- const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
-#endif
- printf(" - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
- if (sum)
- printf(" %s\n", sum);
- }
- }
- }
- queue_free(&q);
- queue_free(&job);
- pool_free(pool);
- free_repoinfos(repoinfos, nrepoinfos);
- solv_free(commandlinepkgs);
- exit(0);
- }
-
-#if defined(SUSE) || defined(FEDORA)
- if (mainmode == MODE_PATCH)
- add_patchjobs(pool, &job);
-#endif
-
- // add mode
- for (i = 0; i < job.count; i += 2)
- {
- job.elements[i] |= mode;
- if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
- job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
- if (cleandeps)
- job.elements[i] |= SOLVER_CLEANDEPS;
- if (forcebest)
- job.elements[i] |= SOLVER_FORCEBEST;
- }
-
- // multiversion test
- // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
- // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
- // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
-#if 0
- queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
- queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
-#endif
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
-rerunsolver:
-#endif
- solv = solver_create(pool);
- solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
-#ifdef FEDORA
- solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
-#endif
- if (mainmode == MODE_ERASE)
- solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1); /* don't nag */
- solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
-
- for (;;)
- {
- Id problem, solution;
- int pcnt, scnt;
-
- if (!solver_solve(solv, &job))
- break;
- pcnt = solver_problem_count(solv);
- printf("Found %d problems:\n", pcnt);
- for (problem = 1; problem <= pcnt; problem++)
- {
- int take = 0;
- printf("Problem %d/%d:\n", problem, pcnt);
- solver_printprobleminfo(solv, problem);
- printf("\n");
- scnt = solver_solution_count(solv, problem);
- for (solution = 1; solution <= scnt; solution++)
- {
- printf("Solution %d:\n", solution);
- solver_printsolution(solv, problem, solution);
- printf("\n");
- }
- for (;;)
- {
- char inbuf[128], *ip;
- printf("Please choose a solution: ");
- fflush(stdout);
- *inbuf = 0;
- if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
- {
- printf("Abort.\n");
- exit(1);
- }
- while (*ip == ' ' || *ip == '\t')
- ip++;
- if (*ip >= '0' && *ip <= '9')
- {
- take = atoi(ip);
- if (take >= 1 && take <= scnt)
- break;
- }
- if (*ip == 's')
- {
- take = 0;
- break;
- }
- if (*ip == 'q')
- {
- printf("Abort.\n");
- exit(1);
- }
- }
- if (!take)
- continue;
- solver_take_solution(solv, problem, take, &job);
- }
- }
-
- trans = solver_create_transaction(solv);
- if (!trans->steps.count)
- {
- printf("Nothing to do.\n");
- transaction_free(trans);
- solver_free(solv);
- queue_free(&job);
- pool_free(pool);
- free_repoinfos(repoinfos, nrepoinfos);
- solv_free(commandlinepkgs);
- exit(1);
- }
-
- /* display transaction to the user and ask for confirmation */
- printf("\n");
- printf("Transaction summary:\n\n");
- transaction_print(trans);
-#if defined(SUSE)
- showdiskusagechanges(trans);
-#endif
- printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
- printf("\n");
-
- if (!yesno("OK to continue (y/n)? "))
- {
- printf("Abort.\n");
- transaction_free(trans);
- solver_free(solv);
- queue_free(&job);
- pool_free(pool);
- free_repoinfos(repoinfos, nrepoinfos);
- solv_free(commandlinepkgs);
- exit(1);
- }
-
- /* download all new packages */
- queue_init(&checkq);
- newpkgs = transaction_installedresult(trans, &checkq);
- newpkgsfps = 0;
- if (newpkgs)
- {
- int downloadsize = 0;
- for (i = 0; i < newpkgs; i++)
- {
- Solvable *s;
-
- p = checkq.elements[i];
- s = pool_id2solvable(pool, p);
- downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
- }
- printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
- newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
- for (i = 0; i < newpkgs; i++)
- {
- const char *loc;
- Solvable *s;
- struct repoinfo *cinfo;
-
- p = checkq.elements[i];
- s = pool_id2solvable(pool, p);
- if (s->repo == commandlinerepo)
- {
- loc = solvable_lookup_location(s, 0);
- if (!loc)
- continue;
- if (!(newpkgsfps[i] = fopen(loc, "r")))
- {
- perror(loc);
- exit(1);
- }
- putchar('.');
- continue;
- }
- cinfo = s->repo->appdata;
- if (!cinfo || cinfo->type == TYPE_INSTALLED)
- {
- printf("%s: no repository information\n", s->repo->name);
- exit(1);
- }
- loc = solvable_lookup_location(s, 0);
- if (!loc)
- continue; /* pseudo package? */
-#if defined(ENABLE_RPMDB)
- if (pool->installed && pool->installed->nsolvables)
- {
- if ((newpkgsfps[i] = trydeltadownload(s, loc)) != 0)
- {
- putchar('d');
- fflush(stdout);
- continue; /* delta worked! */
- }
- }
-#endif
- if ((newpkgsfps[i] = downloadpackage(s, loc)) == 0)
- {
- printf("\n%s: %s not found in repository\n", s->repo->name, loc);
- exit(1);
- }
- putchar('.');
- fflush(stdout);
- }
- putchar('\n');
- }
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
- /* check for file conflicts */
- if (newpkgs)
- {
- Queue conflicts;
- queue_init(&conflicts);
- if (checkfileconflicts(pool, &checkq, newpkgs, newpkgsfps, &conflicts))
- {
- if (yesno("Re-run solver (y/n/q)? "))
- {
- for (i = 0; i < newpkgs; i++)
- if (newpkgsfps[i])
- fclose(newpkgsfps[i]);
- newpkgsfps = solv_free(newpkgsfps);
- solver_free(solv);
- solv = 0;
- pool_add_fileconflicts_deps(pool, &conflicts);
- queue_free(&conflicts);
- goto rerunsolver;
- }
- }
- queue_free(&conflicts);
- }
-#endif
-
- /* and finally commit the transaction */
- printf("Committing transaction:\n\n");
- transaction_order(trans, 0);
- for (i = 0; i < trans->steps.count; i++)
- {
- int j;
- FILE *fp;
- Id type;
-
- p = trans->steps.elements[i];
- type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
- switch(type)
- {
- case SOLVER_TRANSACTION_ERASE:
- printf("erase %s\n", pool_solvid2str(pool, p));
- commit_transactionelement(pool, type, p, 0);
- break;
- case SOLVER_TRANSACTION_INSTALL:
- case SOLVER_TRANSACTION_MULTIINSTALL:
- printf("install %s\n", pool_solvid2str(pool, p));
- for (j = 0; j < newpkgs; j++)
- if (checkq.elements[j] == p)
- break;
- fp = j < newpkgs ? newpkgsfps[j] : 0;
- if (!fp)
- continue;
- commit_transactionelement(pool, type, p, fp);
- fclose(fp);
- newpkgsfps[j] = 0;
- break;
- default:
- break;
- }
- }
-
- for (i = 0; i < newpkgs; i++)
- if (newpkgsfps[i])
- fclose(newpkgsfps[i]);
- solv_free(newpkgsfps);
- queue_free(&checkq);
- transaction_free(trans);
- solver_free(solv);
- queue_free(&job);
- pool_free(pool);
- free_repoinfos(repoinfos, nrepoinfos);
- solv_free(commandlinepkgs);
- exit(0);
-}
+++ /dev/null
-#!/usr/bin/tclsh
-
-package require solv
-package require inifile
-package require fileutil
-
-set reposdir /etc/zypp/repos.d
-
-### some helpers
-
-proc fileno {file} {
- if [regexp -- {^file(\d+)$} $file match fd] {
- return $fd
- }
- error "file not open"
-}
-
-set ::globalarray_cnt 0
-
-proc globalarray {} {
- set name "::globalarray_[incr ::globalarray_cnt]"
- array set $name [list varName $name]
- return $name
-}
-
-### generic repo handling (cache reading/writing)
-
-proc repo_calc_cookie_file {selfName filename} {
- upvar $selfName self
- set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
- $chksum add "1.1"
- $chksum add_stat $filename
- return [$chksum raw]
-}
-
-proc repo_calc_cookie_fp {selfName fp} {
- upvar $selfName self
- set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
- $chksum add "1.1"
- $chksum add_fp $fp
- return [$chksum raw]
-}
-
-proc repo_calc_cookie_ext {selfName f cookie} {
- upvar $selfName self
- set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
- $chksum add "1.1"
- $chksum add $cookie
- $chksum add_fstat [$f fileno]
- return [$chksum raw]
-}
-
-proc repo_cachepath {selfName {ext "-"}} {
- upvar $selfName self
- regsub {^\.} $self(name) _ path
- if {$ext ne "-"} {
- set path "${path}_$ext.solvx"
- } else {
- set path "${path}.solv"
- }
- regsub -all / $path _ path
- return "/var/cache/solv/$path"
-}
-
-proc repo_generic_load {selfName pool} {
- upvar $selfName self
- set handle [ $pool add_repo $self(name) ]
- set self(handle) $handle
- $handle configure -priority [expr 99 - $self(priority)] -appdata $self(varName)
- set dorefresh $self(autorefresh)
- set metadata_expire $self(metadata_expire)
- catch {
- if {$metadata_expire == -1 || [clock seconds] - [file mtime [repo_cachepath self]] < $metadata_expire} {
- set dorefresh 0
- }
- }
- set self(cookie) {}
- set self(extcookie) {}
- if { !$dorefresh && [repo_usecachedrepo self] } {
- puts "repo $self(name): cached"
- return 1
- }
- return 0
-}
-
-proc repo_free_handle {selfName} {
- upvar $selfName self
- set handle $self(handle)
- unset self(handle)
- $handle free 1
-}
-
-proc repo_usecachedrepo {selfName {ext "-"} {mark 0}} {
- upvar $selfName self
- set repopath [repo_cachepath self $ext]
- set code [catch {
- set f [open $repopath "rb"]
- seek $f -32 end
- set fcookie [read $f 32]
- set cookie [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
- if {$cookie ne {} && $cookie ne $fcookie} {
- close $f
- return 0
- }
- set fextcookie {}
- if {$ext eq "-" && $self(type) ne "system"} {
- seek $f -64 end
- set fextcookie [read $f 32]
- }
- seek $f 0 start
- set ff [solv::xfopen_fd {} [fileno $f]]
- close $f
- set flags 0
- if {$ext ne "-"} {
- set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
- if {$ext ne "DL"} {
- set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
- }
- }
- if {! [$self(handle) add_solv $ff $flags]} {
- $ff close
- return 0
- }
- $ff close
- if {$self(type) ne "system" && $ext eq "-"} {
- set self(cookie) $fcookie
- set self(extcookie) $fextcookie
- }
- if {$mark} {
- catch {
- ::fileutil::touch -c -m -t [clock seconds] $repopath
- }
- }
- return 1
- } res]
- return [expr {$code == 2 ? $res : 0}]
-}
-
-proc repo_writecachedrepo {selfName {ext "-"} {repodata "NULL"}} {
- upvar $selfName self
- if [info exists self(incomplete)] {
- return
- }
- file mkdir "/var/cache/solv"
- ::fileutil::tempdir "/var/cache/solv"
- set tempfilename [::fileutil::tempfile ".newsolv-"]
- ::fileutil::tempdirReset
- set f [solv::xfopen $tempfilename "w+"]
- file attributes $tempfilename -permissions 0444
- if {$repodata eq {NULL}} {
- $self(handle) write $f
- } else {
- $repodata write $f
- }
- $f flush
- if {$self(type) ne "system" && $ext eq "-"} {
- if {$self(extcookie) eq {}} {
- set self(extcookie) [repo_calc_cookie_ext self $f $self(cookie)]
- }
- $f write $self(extcookie)
- }
- $f write [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
- $f close
- file rename -force -- $tempfilename [repo_cachepath self $ext]
-}
-
-proc repo_download {selfName file uncompress chksum {markincomplete 0}} {
- upvar $selfName self
- regsub {/$} $self(baseurl) {} url
- set url "$url/$file"
- set tempfilename [::fileutil::tempfile]
- set f [open $tempfilename rb+]
- file delete -- $tempfilename
- if [catch {
- exec -ignorestderr -- curl -f -s -L $url ">@$f"
- }] {
- seek $f 0 end
- if {($chksum ne "" && $chksum ne "NULL") || [tell $f] != 0} {
- puts "$file: download error"
- }
- close $f
- return {NULL}
- }
- seek $f 0 start
- if {$chksum ne "" && $chksum ne "NULL"} {
- set fchksum [solv::new_Chksum [$chksum cget -type]]
- if {$fchksum eq "" || $fchksum eq "NULL"} {
- puts "$file: unknown checksum type"
- if {$markincomplete} {
- set self(incomplete) 1
- }
- close $f
- return {NULL}
- }
- $fchksum add_fd [fileno $f]
- if {[$fchksum != $chksum]} {
- puts "$file: checksum mismatch"
- if {$markincomplete} {
- set self(incomplete) 1
- }
- close $f
- return {NULL}
- }
- }
- set ff [solv::xfopen_fd [expr {$uncompress ? $file : ""}] [fileno $f]]
- close $f
- return $ff
-}
-
-proc repo_generic_add_ext_keys {selfName ext repodata h} {
- upvar $selfName self
- if {$ext eq "DL"} {
- $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOSITORY_DELTAINFO
- $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_FLEXARRAY
- } elseif {$ext eq "DU"} {
- $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_DISKUSAGE
- $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRNUMNUMARRAY
- } elseif {$ext eq "FL"} {
- $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_FILELIST
- $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRSTRARRAY
- }
-}
-
-### system
-
-proc repo_system_load {selfName pool} {
- upvar $selfName self
- set handle [ $pool add_repo $self(name) ]
- set self(handle) $handle
- $handle configure -appdata $self(varName)
- $pool configure -installed $handle
- puts -nonewline "rpm database: "
- set self(cookie) [repo_calc_cookie_file self "/var/lib/rpm/Packages"]
- if [repo_usecachedrepo self] {
- puts "cached"
- return 1
- }
- puts "reading"
- set f [solv::xfopen [repo_cachepath self]]
- $handle add_rpmdb_reffp $f $solv::Repo_REPO_REUSE_REPODATA
- repo_writecachedrepo self
-}
-
-### repomd
-
-proc repo_repomd_add_ext {selfName repodata what ext} {
- upvar $selfName self
- set where [repo_repomd_find self $what]
- if {$where eq {}} {
- return
- }
- set h [$repodata new_handle]
- $repodata set_poolstr $h $solv::REPOSITORY_REPOMD_TYPE $what
- $repodata set_str $h $solv::REPOSITORY_REPOMD_LOCATION [lindex $where 0]
- $repodata set_checksum $h $solv::REPOSITORY_REPOMD_CHECKSUM [lindex $where 1]
- repo_generic_add_ext_keys self $ext $repodata $h
- $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
-}
-
-proc repo_repomd_add_exts {selfName} {
- upvar $selfName self
- set repodata [$self(handle) add_repodata 0]
- repo_repomd_add_ext self $repodata "filelists" "FL"
- $repodata internalize
-}
-
-proc repo_repomd_find {selfName what} {
- upvar $selfName self
- set di [$self(handle) Dataiterator_meta $solv::REPOSITORY_REPOMD_TYPE $what $solv::Dataiterator_SEARCH_STRING]
- $di prepend_keyname $solv::REPOSITORY_REPOMD
- solv::iter d $di {
- set dp [$d parentpos]
- set filename [$dp lookup_str $solv::REPOSITORY_REPOMD_LOCATION]
- set checksum [$dp lookup_checksum $solv::REPOSITORY_REPOMD_CHECKSUM]
- if {$filename ne "" && $checksum eq "NULL"} {
- puts "no $filename file checksum"
- } elseif {$filename ne ""} {
- return [list $filename $checksum]
- }
- }
- return {}
-}
-
-proc repo_repomd_load {selfName pool} {
- upvar $selfName self
- if [repo_generic_load self $pool] {
- return 1
- }
- puts -nonewline "rpmmd repo '$self(name)': "
- set f [repo_download self {repodata/repomd.xml} 0 {}]
- if {$f eq {NULL}} {
- puts "no repomd.xml file, skipped"
- repo_free_handle self
- return 0
- }
- set self(cookie) [repo_calc_cookie_fp self $f]
- if [repo_usecachedrepo self "-" 1] {
- puts "cached"
- return 1
- }
- set handle $self(handle)
- $handle add_repomdxml $f
- puts "fetching"
- set primary [repo_repomd_find self primary]
- if {$primary ne {}} {
- set f [repo_download self [lindex $primary 0] 1 [lindex $primary 1] 1]
- if {$f ne {NULL}} {
- $handle add_rpmmd $f {}
- $f close
- }
- if [info exists self(incomplete)] {
- return 0
- }
- }
- set updateinfo [repo_repomd_find self primary]
- if {$updateinfo ne {}} {
- set f [repo_download self [lindex $updateinfo 0] 1 [lindex $updateinfo 1] 1]
- if {$f ne {NULL}} {
- $handle add_updateinfoxml $f
- $f close
- }
- }
- repo_repomd_add_exts self
- repo_writecachedrepo self
- $self(handle) create_stubs
- return 1
-}
-
-proc repo_repomd_packagespath {selfName} {
- return ""
-}
-
-proc repo_repomd_load_ext {selfName repodata} {
- upvar $selfName self
- switch -- [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_TYPE] {
- "deltainfo" {
- set ext DL
- }
- "filelists" {
- set ext FL
- }
- default {
- return 0
- }
- }
- puts -nonewline "\[$self(name):$ext: "
- flush stdout
- if [repo_usecachedrepo self $ext] {
- puts "cached]"
- return 1
- }
- puts "fetching]"
- set handle $self(handle)
- set filename [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_LOCATION]
- set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::REPOSITORY_REPOMD_CHECKSUM]
- set f [repo_download self $filename 1 $filechecksum]
- if {$f eq {NULL}} {
- return 0
- }
- if {$ext eq "FL"} {
- $handle add_rpmmd $f "FL" [ expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES | $solv::Repo_REPO_LOCALPOOL]
- }
- $f close
- repo_writecachedrepo self $ext $repodata
- return 1
-}
-
-### susetags
-
-proc repo_susetags_add_ext {selfName repodata what ext} {
- upvar $selfName self
- set where [repo_susetags_find self $what]
- if {$where eq {}} {
- return
- }
- set h [$repodata new_handle]
- $repodata set_str $h $solv::SUSETAGS_FILE_NAME [lindex $where 0]
- $repodata set_checksum $h $solv::SUSETAGS_FILE_CHECKSUM [lindex $where 1]
- repo_generic_add_ext_keys self $ext $repodata $h
- $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
-}
-
-proc repo_susetags_add_exts {selfName} {
- upvar $selfName self
- set repodata [$self(handle) add_repodata 0]
- repo_susetags_add_ext self $repodata "packages.FL" "FL"
- repo_susetags_add_ext self $repodata "packages.FL.gz" "FL"
- $repodata internalize
-}
-
-proc repo_susetags_find {selfName what} {
- upvar $selfName self
- set di [$self(handle) Dataiterator_meta $solv::SUSETAGS_FILE_NAME $what $solv::Dataiterator_SEARCH_STRING]
- $di prepend_keyname $solv::SUSETAGS_FILE
- solv::iter d $di {
- set dp [$d parentpos]
- set checksum [$dp lookup_checksum $solv::SUSETAGS_FILE_CHECKSUM]
- return [list $what $checksum]
- }
- return {}
-}
-
-proc repo_susetags_load {selfName pool} {
- upvar $selfName self
- if [repo_generic_load self $pool] {
- return 1
- }
- puts -nonewline "susetags repo '$self(name)': "
- set f [repo_download self {content} 0 {}]
- if {$f eq {NULL}} {
- puts "no content file, skipped"
- repo_free_handle self
- return 0
- }
- set self(cookie) [repo_calc_cookie_fp self $f]
- if [repo_usecachedrepo self "-" 1] {
- puts "cached"
- return 1
- }
- set handle $self(handle)
- $handle add_content $f
- puts "fetching"
- set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
- set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
- if {$descrdir eq {NULL}} {
- set descrdir "suse/setup/descr"
- }
- set packages [repo_susetags_find self "packages.gz"]
- if {$packages eq {}} {
- set packages [repo_susetags_find self "packages"]
- }
- if {$packages ne {}} {
- set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
- if {$f ne {NULL}} {
- $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_SUSETAGS_RECORD_SHARES]
- $f close
- set packages [repo_susetags_find self "packages.en.gz"]
- if {$packages eq {}} {
- set packages [repo_susetags_find self "packages.en"]
- }
- if {$packages ne {}} {
- set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
- if {$f ne {NULL}} {
- $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_REPO_REUSE_REPODATA | $solv::Repo_REPO_EXTEND_SOLVABLES ]
- $f close
- }
- }
- $handle internalize
- }
- }
- repo_susetags_add_exts self
- repo_writecachedrepo self
- $self(handle) create_stubs
- return 1
-}
-
-proc repo_susetags_packagespath {selfName} {
- upvar $selfName self
- set datadir [[$self(handle) cget -meta] lookup_str $solv::SUSETAGS_DATADIR]
- return [expr {$datadir ne {} ? "$datadir/" : "suse/"}]
-}
-
-proc repo_susetags_load_ext {selfName repodata} {
- upvar $selfName self
- set filename [$repodata lookup_str $solv::SOLVID_META $solv::SUSETAGS_FILE_NAME]
- set ext [string range $filename 9 10]
- puts -nonewline "\[$self(name):$ext: "
- flush stdout
- if [repo_usecachedrepo self $ext] {
- puts "cached]"
- return 1
- }
- puts "fetching]"
- set handle $self(handle)
- set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
- set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
- if {$descrdir eq {NULL}} {
- set descrdir "suse/setup/descr"
- }
- set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::SUSETAGS_FILE_CHECKSUM]
- set f [repo_download self "$descrdir/$filename" 1 $filechecksum]
- if {$f eq {NULL}} {
- return 0
- }
- set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
- if {$ext ne "DL"} {
- set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
- }
- $handle add_susetags $f $defvendorid $ext $flags
- $f close
- repo_writecachedrepo self $ext $repodata
- return 1
-}
-
-### unknown
-
-proc repo_unknown_load {selfName pool} {
- upvar $selfName self
- puts "unsupported repo '$self(name)': skipped"
- return 0
-}
-
-### poor man's OO
-
-proc repo_load {selfName pool} {
- upvar $selfName self
- "repo_$self(type)_load" self $pool
-}
-
-proc repo_packagespath {selfName} {
- upvar $selfName self
- "repo_$self(type)_packagespath" self
-}
-
-proc repo_load_ext {selfName repodata} {
- upvar $selfName self
- "repo_$self(type)_load_ext" self $repodata
-}
-
-###
-
-proc load_stub {repodata} {
- set code [catch {
- upvar #0 [[$repodata cget -repo] cget -appdata] repo
- if [info exists repo(handle)] {
- return [repo_load_ext repo $repodata]
- }
- return 0
- } res]
- if {$code == 2} {
- return $res
- }
- puts stderr $res
- return 0
-}
-
-###
-
-set repoNames {}
-foreach reponame [lsort [glob -nocomplain -directory $reposdir *.repo]] {
- set ini [::ini::open $reponame r]
- foreach alias [::ini::sections $ini] {
- upvar #0 [globalarray] repo
- array set repo {enabled 0 priority 99 autorefresh 1 type rpm-md metadata_expire 900}
- array set repo [::ini::get $ini $alias]
- set repo(name) $alias
- switch -exact -- $repo(type) {
- rpm-md { set repo(type) repomd }
- yast2 { set repo(type) susetags }
- default { set repo(type) unknown }
- }
- lappend repoNames $repo(varName)
- }
- ::ini::close $ini
-}
-
-set pool [solv::new_Pool]
-$pool setarch
-$pool set_loadcallback load_stub
-
-upvar #0 [globalarray] sysrepo
-array set sysrepo [list name {@System} type system]
-repo_load sysrepo $pool
-
-foreach repoName $repoNames {
- upvar 0 $repoName repo
- if {$repo(enabled)} {
- repo_load repo $pool
- }
-}
-
-
-set cmd [lindex $::argv 0]
-set ::argv [lreplace $::argv 0 0]
-
-array set cmdabbrev [ list \
- in install \
- rm erase \
- ls list \
- ve verify \
- se search \
-]
-if [info exists cmdabbrev($cmd)] {
- set cmd $cmdabbrev($cmd)
-}
-
-if {$cmd eq "search"} {
- set arg [lindex $::argv 0]
- $pool createwhatprovides
- set sel [$pool Selection]
- set di [$pool Dataiterator $solv::SOLVABLE_NAME $arg [ expr $solv::Dataiterator_SEARCH_SUBSTRING | $solv::Dataiterator_SEARCH_NOCASE ]]
- solv::iter d $di {
- $sel add_raw $solv::Job_SOLVER_SOLVABLE [$d cget -solvid]
- }
- foreach s [$sel solvables] {
- puts [format { - %s [%s]: %s} [$s str] [[$s cget -repo] cget -name] [$s lookup_str $solv::SOLVABLE_SUMMARY]]
- }
- exit
-}
-
-$pool addfileprovides
-$pool createwhatprovides
-
-array set cmdactionmap [ list \
- install $solv::Job_SOLVER_INSTALL \
- erase $solv::Job_SOLVER_ERASE \
- up $solv::Job_SOLVER_UPDATE \
- dup $solv::Job_SOLVER_DISTUPGRADE \
- verify $solv::Job_SOLVER_VERIFY \
- list 0 \
- info 0 \
-]
-
-set jobs {}
-foreach arg $::argv {
- set flags [expr $solv::Selection_SELECTION_NAME | $solv::Selection_SELECTION_PROVIDES | $solv::Selection_SELECTION_GLOB | \
- $solv::Selection_SELECTION_CANON | $solv::Selection_SELECTION_DOTARCH | $solv::Selection_SELECTION_REL ]
- switch -glob -- $arg {
- "/*" {
- set flags [expr $flags | $solv::Selection_SELECTION_FILELIST ]
- if {$cmd eq "erase"} {
- set flags [expr $flags | $solv::Selection_SELECTION_INSTALLED_ONLY ]
- }
- }
- }
- set sel [$pool select $arg $flags]
- if [$sel isempty] {
- set sel [$pool select $arg [expr $flags | $solv::Selection_SELECTION_NOCASE]]
- if {![$sel isempty]} {
- puts "\[ignoring case for '$arg']"
- }
- }
- if [$sel isempty] {
- puts "nothing matches '$arg'"
- exit 1
- }
- if {[$sel flags] & $solv::Selection_SELECTION_FILELIST} {
- puts "\[using file list match for '$arg']"
- }
- if {[$sel flags] & $solv::Selection_SELECTION_PROVIDES} {
- puts "\[using capability match for '$arg']"
- }
- lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
-}
-
-if {$jobs eq {} && ($cmd eq "up" || $cmd eq "dup" || $cmd eq "verify") } {
- set sel [$pool Selection_all]
- lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
-}
-
-if {$jobs eq {}} {
- puts "no package matched."
- exit 1
-}
-
-if {$cmd eq "list" || $cmd eq "info"} {
- foreach job $jobs {
- foreach s [$job solvables] {
- if {$cmd eq "info"} {
- puts [format {Name: %s} [$s str]]
- puts [format {Repo: %s} [[$s cget -repo] cget -name]]
- puts [format {Summary: %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
- set str [$s lookup_str $solv::SOLVABLE_URL]
- if {$str ne {}} {
- puts [format {Url: %s} $str]
- }
- set str [$s lookup_str $solv::SOLVABLE_LICENSE]
- if {$str ne {}} {
- puts [format {License %s} $str]
- }
- puts [format {Description: %s} [$s lookup_str $solv::SOLVABLE_DESCRIPTION]]
- puts {}
- } else {
- puts [format { - %s [%s]} [$s str] [[$s cget -repo] cget -name]]
- puts [format { %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
- }
- }
- }
- exit
-}
-
-#$pool set_debuglevel 1
-set solver [$pool Solver]
-$solver set_flag $solv::Solver_SOLVER_FLAG_SPLITPROVIDES 1
-if {$cmd eq "erase"} {
- $solver set_flag $solv::Solver_SOLVER_FLAG_ALLOW_UNINSTALL 1
-}
-
-set problems [$solver solve $jobs]
-if {$problems ne {}} {
- set pcnt 1
- foreach problem $problems {
- puts [format {Problem %d/%d:} $pcnt [llength $problems]]
- puts [$problem str]
- incr pcnt
- }
- exit 1
-}
-
-set trans [$solver transaction]
-
-if [$trans isempty] {
- puts "Nothing to do."
- exit
-}
-
-puts {}
-puts "Transaction summary:"
-puts {}
-foreach cl [$trans classify [expr $solv::Transaction_SOLVER_TRANSACTION_SHOW_OBSOLETES | $solv::Transaction_SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE]] {
- switch -- [$cl cget -type] \
- $solv::Transaction_SOLVER_TRANSACTION_ERASE {
- puts [format {%d erased packages:} [$cl cget -count]]
- } \
- $solv::Transaction_SOLVER_TRANSACTION_INSTALL {
- puts [format {%d installed packages:} [$cl cget -count]]
- } \
- $solv::Transaction_SOLVER_TRANSACTION_REINSTALLED {
- puts [format {%d reinstalled packages:} [$cl cget -count]]
- } \
- $solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED {
- puts [format {%d downgraded packages:} [$cl cget -count]]
- } \
- $solv::Transaction_SOLVER_TRANSACTION_CHANGED {
- puts [format {%d changed packages:} [$cl cget -count]]
- } \
- $solv::Transaction_SOLVER_TRANSACTION_UPGRADED {
- puts [format {%d upgraded packages:} [$cl cget -count]]
- } \
- $solv::Transaction_SOLVER_TRANSACTION_VENDORCHANGE {
- puts [format {%d vendor changes from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
- } \
- $solv::Transaction_SOLVER_TRANSACTION_ARCHCHANGE {
- puts [format {%d archchanges from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
- } \
- default continue
- foreach p [$cl solvables] {
- set cltype [$cl cget -type]
- if {$cltype == $solv::Transaction_SOLVER_TRANSACTION_UPGRADED || $cltype ==$solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED} {
- set op [$trans othersolvable $p]
- puts [format { - %s -> %s} [$p str] [$op str]]
- } else {
- puts [format { - %s} [$p str]]
- }
- }
- puts {}
-}
-puts [format {install size change: %d K} [$trans calc_installsizechange]]
-puts {}
-
-while 1 {
- puts -nonewline "OK to continue (y/n)? "
- flush stdout
- set yn [gets stdin]
- if {$yn eq "y"} {
- break
- }
- if {$yn eq "n" || $yn eq "q"} {
- exit
- }
-}
-
-set newpkgs [$trans newsolvables]
-array set newpkgs_f {}
-if {$newpkgs ne {}} {
- set downloadsize 0
- foreach p $newpkgs {
- set downloadsize [expr $downloadsize + [$p lookup_num $solv::SOLVABLE_DOWNLOADSIZE]]
- }
- puts [format {Downloading %d packages, %d K} [llength $newpkgs] [expr $downloadsize / 1024]]
- foreach p $newpkgs {
- upvar #0 [[$p cget -repo] cget -appdata] repo
- set location [$p lookup_location]
- if {$location eq {}} {
- continue
- }
- set location "[repo_packagespath repo][lindex $location 0]"
- set checksum [$p lookup_checksum $solv::SOLVABLE_CHECKSUM]
- set f [repo_download repo $location 0 $checksum]
- set newpkgs_f([$p cget -id]) $f
- puts -nonewline "."
- flush stdout
- }
- puts {}
-}
-
-puts "Committing transaction:"
-$trans order
-foreach p [$trans steps] {
- set steptype [$trans steptype $p $solv::Transaction_SOLVER_TRANSACTION_RPM_ONLY]
- if {$steptype == $solv::Transaction_SOLVER_TRANSACTION_ERASE} {
- puts "erase [$p str]"
- regsub {^[0-9]+:} [$p cget -evr] {} nvr
- set nvr "[$p cget -name]-$nvr.[$p cget -arch]"
- exec -ignorestderr -- rpm -e --nodeps --nodigest --nosignature $nvr
- } elseif {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL || $steptype == $solv::Transaction_SOLVER_TRANSACTION_MULTIINSTALL} {
- puts "install [$p str]"
- set f $newpkgs_f([$p cget -id])
- set mode [expr {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL ? "-U" : "-i"}]
- $f cloexec 0
- exec -ignorestderr -- rpm $mode --force --nodeps --nodigest --nosignature "/dev/fd/[$f fileno]"
- }
-}
+++ /dev/null
-SET (libsolvext_SRCS
- solv_xfopen.c testcase.c)
-
-SET (libsolvext_HEADERS
- tools_util.h solv_xfopen.h testcase.h)
-
-IF (ENABLE_RPMDB)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- pool_fileconflicts.c repo_rpmdb.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- pool_fileconflicts.h repo_rpmdb.h)
-ENDIF (ENABLE_RPMDB)
-
-IF (ENABLE_PUBKEY)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_pubkey.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_pubkey.h)
-ENDIF (ENABLE_PUBKEY)
-
-IF (ENABLE_PGPVRFY)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- solv_pgpvrfy.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- solv_pgpvrfy.h)
-ENDIF (ENABLE_PGPVRFY)
-
-IF (ENABLE_RPMMD)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_repomdxml.c repo_rpmmd.c
- repo_deltainfoxml.c repo_updateinfoxml.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_repomdxml.h repo_rpmmd.h
- repo_deltainfoxml.h repo_updateinfoxml.h)
-ENDIF (ENABLE_RPMMD)
-
-IF (ENABLE_SUSEREPO)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_content.c repo_products.c repo_releasefile_products.c
- repo_susetags.c repo_zyppdb.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_content.h repo_products.h repo_releasefile_products.h
- repo_susetags.h repo_zyppdb.h)
-ENDIF (ENABLE_SUSEREPO)
-
-# old cmake does not support parenthetical expressions...
-IF (ENABLE_COMPLEX_DEPS)
-IF (ENABLE_SUSEREPO OR ENABLE_RPMMD OR ENABLE_RPMDB)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- pool_parserpmrichdep.c)
-ENDIF (ENABLE_SUSEREPO OR ENABLE_RPMMD OR ENABLE_RPMDB)
-ENDIF (ENABLE_COMPLEX_DEPS)
-
-IF (SUSE)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_autopattern.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_autopattern.h)
-ENDIF (SUSE)
-
-IF (ENABLE_COMPS)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_comps.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_comps.h)
-ENDIF (ENABLE_COMPS)
-
-IF (ENABLE_DEBIAN)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_deb.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_deb.h)
-ENDIF (ENABLE_DEBIAN)
-
-IF (ENABLE_HELIXREPO)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_helix.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_helix.h)
-ENDIF (ENABLE_HELIXREPO)
-
-IF (ENABLE_MDKREPO)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_mdk.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_mdk.h)
-ENDIF (ENABLE_MDKREPO)
-
-IF (ENABLE_ARCHREPO)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_arch.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_arch.h)
-ENDIF (ENABLE_ARCHREPO)
-
-IF (ENABLE_CUDFREPO)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_cudf.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_cudf.h)
-ENDIF (ENABLE_CUDFREPO)
-
-IF (ENABLE_HAIKU)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_haiku.cpp)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_haiku.h)
-ENDIF (ENABLE_HAIKU)
-
-IF (ENABLE_APPDATA)
- SET (libsolvext_SRCS ${libsolvext_SRCS}
- repo_appdata.c)
- SET (libsolvext_HEADERS ${libsolvext_HEADERS}
- repo_appdata.h)
-ENDIF (ENABLE_APPDATA)
-
-SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
-IF (HAVE_LINKER_VERSION_SCRIPT)
-SET (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_FLAGS} -Wl,--version-script=${CMAKE_SOURCE_DIR}/ext/libsolvext.ver")
-ENDIF (HAVE_LINKER_VERSION_SCRIPT)
-
-IF (DISABLE_SHARED)
-ADD_LIBRARY (libsolvext STATIC ${libsolvext_SRCS})
-ELSE (DISABLE_SHARED)
-ADD_LIBRARY (libsolvext SHARED ${libsolvext_SRCS})
-TARGET_LINK_LIBRARIES(libsolvext libsolv ${SYSTEM_LIBRARIES})
-ENDIF (DISABLE_SHARED)
-
-SET_TARGET_PROPERTIES(libsolvext PROPERTIES OUTPUT_NAME "solvext")
-SET_TARGET_PROPERTIES(libsolvext PROPERTIES SOVERSION ${LIBSOLVEXT_SOVERSION})
-SET_TARGET_PROPERTIES(libsolvext PROPERTIES INSTALL_NAME_DIR ${LIB_INSTALL_DIR})
-
-INSTALL (FILES ${libsolvext_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/solv")
-INSTALL (TARGETS libsolvext LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
-
-IF (ENABLE_STATIC AND NOT DISABLE_SHARED)
-ADD_LIBRARY (libsolvext_static STATIC ${libsolvext_SRCS})
-SET_TARGET_PROPERTIES(libsolvext_static PROPERTIES OUTPUT_NAME "solvext")
-SET_TARGET_PROPERTIES(libsolvext_static PROPERTIES SOVERSION ${LIBSOLVEXT_SOVERSION})
-INSTALL (TARGETS libsolvext_static LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
-ENDIF (ENABLE_STATIC AND NOT DISABLE_SHARED)
+++ /dev/null
-SOLV_1.0 {
- global:
- pool_deb_get_autoinstalled;
- pool_findfileconflicts;
- repo_add_appdata;
- repo_add_appdata_dir;
- repo_add_arch_local;
- repo_add_arch_pkg;
- repo_add_arch_repo;
- repo_add_autopattern;
- repo_add_code11_products;
- repo_add_content;
- repo_add_comps;
- repo_add_cudf;
- repo_add_deb;
- repo_add_debdb;
- repo_add_debpackages;
- repo_add_deltainfoxml;
- repo_add_haiku_installed_packages;
- repo_add_haiku_package;
- repo_add_haiku_package_info;
- repo_add_haiku_packages;
- repo_add_helix;
- repo_add_keydir;
- repo_add_keyring;
- repo_add_mdk;
- repo_add_mdk_info;
- repo_add_products;
- repo_add_pubkey;
- repo_add_releasefile_products;
- repo_add_repomdxml;
- repo_add_rpm;
- repo_add_rpm_handle;
- repo_add_rpmdb;
- repo_add_rpmdb_pubkeys;
- repo_add_rpmdb_reffp;
- repo_add_rpmmd;
- repo_add_susetags;
- repo_add_updateinfoxml;
- repo_add_zyppdb_products;
- repo_find_all_pubkeys;
- repo_find_pubkey;
- repo_verify_sigdata;
- rpm_byfp;
- rpm_byrpmdbid;
- rpm_byrpmh;
- rpm_installedrpmdbids;
- rpm_iterate_filelist;
- rpm_query;
- rpm_query_num;
- rpm_state_create;
- rpm_state_free;
- solv_verify_sig;
- solv_xfopen;
- solv_xfopen_buf;
- solv_xfopen_fd;
- solv_xfopen_iscompressed;
- solvsig_create;
- solvsig_free;
- solvsig_verify;
- testcase_add_testtags;
- testcase_dep2str;
- testcase_job2str;
- testcase_solvid2str;
- testcase_str2dep;
- testcase_str2job;
- testcase_str2repo;
- testcase_read;
- testcase_resultdiff;
- testcase_solverresult;
- testcase_write;
- testcase_write_testtags;
- local:
- *;
-};
+++ /dev/null
-/*
- * Copyright (c) 2009-2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "hash.h"
-#include "repo_rpmdb.h"
-#include "pool_fileconflicts.h"
-
-struct cbdata {
- Pool *pool;
- int create;
- int aliases;
-
- Queue lookat; /* conflict candidates */
- Queue lookat_dir; /* not yet conflicting directories */
-
- Hashtable cflmap;
- Hashval cflmapn;
- unsigned int cflmapused;
-
- Hashtable dirmap;
- Hashval dirmapn;
- unsigned int dirmapused;
- int dirconflicts;
-
- Map idxmap;
-
- unsigned int lastdiridx; /* last diridx we have seen */
- unsigned int lastdirhash; /* strhash of last dir we have seen */
-
- Id idx; /* index of package we're looking at */
- Id hx; /* used in findfileconflicts2_cb, limit to files matching hx */
-
- Id dirid; /* used in findfileconflicts2_cb, limit to dirs matching dirid */
- Id dirhash; /* used in findfileconflicts2_cb, limit to dirs matching dirhash */
-
- Queue files;
- unsigned char *filesspace;
- unsigned int filesspacen;
-
- Hashtable normap;
- Hashval normapn;
- unsigned int normapused;
- Queue norq;
-
- Hashtable statmap;
- Hashval statmapn;
- unsigned int statmapused;
-
- int usestat;
- int statsmade;
-
- const char *rootdir;
- int rootdirl;
-
- char *canonspace;
- int canonspacen;
-};
-
-#define FILESSPACE_BLOCK 255
-
-static Hashtable
-growhash(Hashtable map, Hashval *mapnp)
-{
- Hashval mapn = *mapnp;
- Hashval newn = (mapn + 1) * 2 - 1;
- Hashval i, h, hh;
- Hashtable m;
- Id hx, qx;
-
- m = solv_calloc(newn + 1, 2 * sizeof(Id));
- for (i = 0; i <= mapn; i++)
- {
- hx = map[2 * i];
- if (!hx)
- continue;
- h = hx & newn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = m[2 * h];
- if (!qx)
- break;
- h = HASHCHAIN_NEXT(h, hh, newn);
- }
- m[2 * h] = hx;
- m[2 * h + 1] = map[2 * i + 1];
- }
- solv_free(map);
- *mapnp = newn;
- return m;
-}
-
-static void
-finddirs_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
-{
- struct cbdata *cbdata = cbdatav;
- Hashval h, hh;
- Id hx, qx;
- Id oidx, idx = cbdata->idx;
-
- hx = strhash(fn);
- if (!hx)
- hx = strlen(fn) + 1;
- h = hx & cbdata->dirmapn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = cbdata->dirmap[2 * h];
- if (!qx)
- break;
- if (qx == hx)
- break;
- h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn);
- }
- if (!qx)
- {
- /* a miss */
- if (!cbdata->create)
- return;
- cbdata->dirmap[2 * h] = hx;
- cbdata->dirmap[2 * h + 1] = idx;
- if (++cbdata->dirmapused * 2 > cbdata->dirmapn)
- cbdata->dirmap = growhash(cbdata->dirmap, &cbdata->dirmapn);
- return;
- }
- oidx = cbdata->dirmap[2 * h + 1];
- if (oidx == idx)
- return;
- /* found a conflict, this dir may be used in multiple packages */
- if (oidx != -1)
- {
- MAPSET(&cbdata->idxmap, oidx);
- cbdata->dirmap[2 * h + 1] = -1;
- cbdata->dirconflicts++;
- }
- MAPSET(&cbdata->idxmap, idx);
-}
-
-static inline int
-isindirmap(struct cbdata *cbdata, Id hx)
-{
- Hashval h, hh;
- Id qx;
-
- h = hx & cbdata->dirmapn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = cbdata->dirmap[2 * h];
- if (!qx)
- return 0;
- if (qx == hx)
- return cbdata->dirmap[2 * h + 1] == -1 ? 1 : 0;
- h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn);
- }
-}
-
-static void
-findfileconflicts_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
-{
- struct cbdata *cbdata = cbdatav;
- int isdir = S_ISDIR(info->mode);
- const char *dp;
- Id idx, oidx;
- Id hx, qx;
- Hashval h, hh, dhx;
-
- idx = cbdata->idx;
-
- if (!info->dirlen)
- return;
- dp = fn + info->dirlen;
- if (info->diridx != cbdata->lastdiridx)
- {
- cbdata->lastdiridx = info->diridx;
- cbdata->lastdirhash = strnhash(fn, dp - fn);
- }
- dhx = cbdata->lastdirhash;
- /* this mirrors the "if (!hx) hx = strlen(fn) + 1" in finddirs_cb */
- if (!isindirmap(cbdata, dhx ? dhx : dp - fn + 1))
- return;
- hx = strhash_cont(dp, dhx);
- if (!hx)
- hx = strlen(fn) + 1;
-
- h = hx & cbdata->cflmapn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = cbdata->cflmap[2 * h];
- if (!qx)
- break;
- if (qx == hx)
- break;
- h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
- }
- if (!qx)
- {
- /* a miss */
- if (!cbdata->create)
- return;
- cbdata->cflmap[2 * h] = hx;
- cbdata->cflmap[2 * h + 1] = (isdir ? ~idx : idx);
- if (++cbdata->cflmapused * 2 > cbdata->cflmapn)
- cbdata->cflmap = growhash(cbdata->cflmap, &cbdata->cflmapn);
- return;
- }
- oidx = cbdata->cflmap[2 * h + 1];
- if (oidx < 0)
- {
- int i;
- if (isdir)
- {
- /* both are directories. delay the conflict, keep oidx in slot */
- queue_push2(&cbdata->lookat_dir, hx, idx);
- return;
- }
- oidx = ~oidx;
- /* now have file, had directories before. */
- cbdata->cflmap[2 * h + 1] = oidx; /* make it a file */
- /* dump all delayed directory hits for hx */
- for (i = 0; i < cbdata->lookat_dir.count; i += 2)
- if (cbdata->lookat_dir.elements[i] == hx)
- {
- queue_push2(&cbdata->lookat, hx, cbdata->lookat_dir.elements[i + 1]);
- queue_push2(&cbdata->lookat, 0, 0);
- }
- }
- else if (oidx == idx)
- return; /* no conflicts with ourself, please */
- queue_push2(&cbdata->lookat, hx, oidx);
- queue_push2(&cbdata->lookat, 0, 0);
- queue_push2(&cbdata->lookat, hx, idx);
- queue_push2(&cbdata->lookat, 0, 0);
-}
-
-/* same as findfileconflicts_cb, but
- * - hashes with just the basename
- * - sets idx in a map instead of pushing to lookat
- * - sets the hash element to -1 if there may be a conflict
- */
-static void
-findfileconflicts_basename_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
-{
- struct cbdata *cbdata = cbdatav;
- int isdir = S_ISDIR(info->mode);
- const char *dp;
- Id idx, oidx;
- Id hx, qx;
- Hashval h, hh;
-
- idx = cbdata->idx;
-
- if (!info->dirlen)
- return;
- dp = fn + info->dirlen;
- hx = strhash(dp);
- if (!hx)
- hx = strlen(fn) + 1;
-
- h = hx & cbdata->cflmapn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = cbdata->cflmap[2 * h];
- if (!qx)
- break;
- if (qx == hx)
- break;
- h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
- }
- if (!qx)
- {
- /* a miss */
- if (!cbdata->create)
- return;
- cbdata->cflmap[2 * h] = hx;
- cbdata->cflmap[2 * h + 1] = (isdir ? -idx - 2 : idx);
- if (++cbdata->cflmapused * 2 > cbdata->cflmapn)
- cbdata->cflmap = growhash(cbdata->cflmap, &cbdata->cflmapn);
- return;
- }
- oidx = cbdata->cflmap[2 * h + 1];
- if (oidx < -1)
- {
- int i;
- if (isdir)
- {
- /* both are directories. delay the conflict, keep oidx in slot */
- queue_push2(&cbdata->lookat_dir, hx, idx);
- return;
- }
- oidx = -idx - 2;
- /* now have file, had directories before. */
- cbdata->cflmap[2 * h + 1] = oidx; /* make it a file */
- /* dump all delayed directory hits for hx */
- for (i = 0; i < cbdata->lookat_dir.count; i += 2)
- if (cbdata->lookat_dir.elements[i] == hx)
- MAPSET(&cbdata->idxmap, cbdata->lookat_dir.elements[i + 1]);
- }
- else if (oidx == idx)
- return; /* no conflicts with ourself, please */
- if (oidx >= 0)
- MAPSET(&cbdata->idxmap, oidx);
- MAPSET(&cbdata->idxmap, idx);
- if (oidx != -1)
- cbdata->cflmap[2 * h + 1] = -1;
-}
-
-static inline Id
-addfilesspace(struct cbdata *cbdata, int len)
-{
- unsigned int off = cbdata->filesspacen;
- cbdata->filesspace = solv_extend(cbdata->filesspace, cbdata->filesspacen, len, 1, FILESSPACE_BLOCK);
- cbdata->filesspacen += len;
- return off;
-}
-
-static Id
-unifywithstat(struct cbdata *cbdata, Id diroff, int dirl)
-{
- struct stat stb;
- int i;
- Hashval h, hh;
- Id hx, qx;
- Id nspaceoff;
- unsigned char statdata[16 + sizeof(stb.st_dev) + sizeof(stb.st_ino)];
-
- if (dirl > 1 && cbdata->filesspace[diroff + dirl - 1] == '/')
- cbdata->filesspace[diroff + dirl - 1] = 0;
- cbdata->statsmade++;
- i = stat((char *)cbdata->filesspace + diroff, &stb);
- if (dirl > 1 && cbdata->filesspace[diroff + dirl - 1] == 0)
- cbdata->filesspace[diroff + dirl - 1] = '/';
- if (i)
- return diroff;
- memset(statdata, 0, 16);
- memcpy(statdata + 8, &stb.st_dev, sizeof(stb.st_dev));
- memcpy(statdata, &stb.st_ino, sizeof(stb.st_ino));
- hx = 0;
- for (i = 15; i >= 0; i--)
- hx = (unsigned int)hx * 13 + statdata[i];
- h = hx & cbdata->statmapn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = cbdata->statmap[2 * h];
- if (!qx)
- break;
- if (qx == hx)
- {
- Id off = cbdata->statmap[2 * h + 1];
- char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off];
- if (!memcmp(dp, statdata, 16))
- return cbdata->norq.elements[off + 1];
- }
- h = HASHCHAIN_NEXT(h, hh, cbdata->statmapn);
- }
- /* new stat result. work. */
- nspaceoff = addfilesspace(cbdata, 16);
- memcpy(cbdata->filesspace + nspaceoff, statdata, 16);
- queue_push2(&cbdata->norq, nspaceoff, nspaceoff);
- cbdata->statmap[2 * h] = hx;
- cbdata->statmap[2 * h + 1] = cbdata->norq.count - 2;
- if (++cbdata->statmapused * 2 > cbdata->statmapn)
- cbdata->statmap = growhash(cbdata->statmap, &cbdata->statmapn);
- return nspaceoff;
-}
-
-/* forward declaration */
-static Id normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create);
-
-static Id
-unifywithcanon(struct cbdata *cbdata, Id diroff, int dirl)
-{
- Id dirnameid;
- int i, l, ll, lo;
- struct stat stb;
-
-#if 0
- printf("UNIFY %.*s\n", dirl, (char *)cbdata->filesspace + diroff);
-#endif
- if (!dirl || cbdata->filesspace[diroff] != '/')
- return diroff;
- /* strip / at end*/
- while (dirl && cbdata->filesspace[diroff + dirl - 1] == '/')
- dirl--;
- if (!dirl)
- return diroff;
-
- /* find dirname */
- for (i = dirl - 1; i > 0; i--)
- if (cbdata->filesspace[diroff + i] == '/')
- break;
- i++; /* include trailing / */
-
- /* normalize dirname */
- dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + diroff, i, strnhash((char *)cbdata->filesspace + diroff, i), 1);
- if (dirnameid == -1)
- return diroff; /* hit "in progress" marker, some cyclic link */
-
- /* sanity check result */
- if (cbdata->filesspace[dirnameid] != '/')
- return diroff; /* hmm */
- l = strlen((char *)cbdata->filesspace + dirnameid);
- if (l && cbdata->filesspace[dirnameid + l - 1] != '/')
- return diroff; /* hmm */
-
- /* special handling for "." and ".." basename */
- if (cbdata->filesspace[diroff + i] == '.')
- {
- if (dirl - i == 1)
- return dirnameid;
- if (dirl - i == 2 && cbdata->filesspace[diroff + i + 1] == '.')
- {
- if (l <= 2)
- return dirnameid; /* we hit our root */
- for (i = l - 2; i > 0; i--)
- if (cbdata->filesspace[dirnameid + i] == '/')
- break;
- i++; /* include trailing / */
- dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + dirnameid, i, strnhash((char *)cbdata->filesspace + dirnameid, i), 1);
- return dirnameid == -1 ? diroff : dirnameid;
- }
- }
-
- /* append basename to normalized dirname */
- if (cbdata->rootdirl + l + dirl - i + 1 > cbdata->canonspacen)
- {
- cbdata->canonspacen = cbdata->rootdirl + l + dirl - i + 20;
- cbdata->canonspace = solv_realloc(cbdata->canonspace, cbdata->canonspacen);
- strcpy(cbdata->canonspace, cbdata->rootdir);
- }
- strcpy(cbdata->canonspace + cbdata->rootdirl, (char *)cbdata->filesspace + dirnameid);
- strncpy(cbdata->canonspace + cbdata->rootdirl + l, (char *)cbdata->filesspace + diroff + i, dirl - i);
- cbdata->canonspace[cbdata->rootdirl + l + dirl - i] = 0;
-
-#if 0
- printf("stat()ing %s\n", cbdata->canonspace);
-#endif
- cbdata->statsmade++;
- if (lstat(cbdata->canonspace, &stb) != 0 || !S_ISLNK(stb.st_mode))
- {
- /* not a symlink or stat failed, have new canon entry */
- diroff = addfilesspace(cbdata, l + dirl - i + 2);
- strcpy((char *)cbdata->filesspace + diroff, cbdata->canonspace + cbdata->rootdirl);
- l += dirl - i;
- /* add trailing / */
- if (cbdata->filesspace[diroff + l - 1] != '/')
- {
- cbdata->filesspace[diroff + l++] = '/';
- cbdata->filesspace[diroff + l] = 0;
- }
- /* call normalizedir on new entry for unification purposes */
- dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + diroff, l, strnhash((char *)cbdata->filesspace + diroff, l), 1);
- return dirnameid == -1 ? diroff : dirnameid;
- }
- /* oh no, a symlink! follow */
- lo = cbdata->rootdirl + l + dirl - i + 1;
- if (lo + stb.st_size + 2 > cbdata->canonspacen)
- {
- cbdata->canonspacen = lo + stb.st_size + 20;
- cbdata->canonspace = solv_realloc(cbdata->canonspace, cbdata->canonspacen);
- }
- ll = readlink(cbdata->canonspace, cbdata->canonspace + lo, stb.st_size);
- if (ll < 0 || ll > stb.st_size)
- return diroff; /* hmm */
- if (ll == 0)
- return dirnameid; /* empty means current dir */
- if (cbdata->canonspace[lo + ll - 1] != '/')
- cbdata->canonspace[lo + ll++] = '/'; /* add trailing / */
- cbdata->canonspace[lo + ll] = 0; /* zero terminate */
- if (cbdata->canonspace[lo] != '/')
- {
- /* relative link, concatenate to dirname */
- memmove(cbdata->canonspace + cbdata->rootdirl + l, cbdata->canonspace + lo, ll + 1);
- lo = cbdata->rootdirl;
- ll += l;
- }
- dirnameid = normalizedir(cbdata, cbdata->canonspace + lo, ll, strnhash(cbdata->canonspace + lo, ll), 1);
- return dirnameid == -1 ? diroff : dirnameid;
-}
-
-/*
- * map a directory (containing a trailing /) into a number.
- * for unifywithstat this is the offset to the 16 byte stat result.
- * for unifywithcanon this is the offset to the normailzed dir.
- */
-static Id
-normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create)
-{
- Hashval h, hh;
- Id qx;
- Id nspaceoff;
- int mycnt;
-
- if (!hx)
- hx = dirl + 1;
- h = hx & cbdata->normapn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = cbdata->normap[2 * h];
- if (!qx)
- break;
- if (qx == hx)
- {
- Id off = cbdata->normap[2 * h + 1];
- char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off];
- if (!strncmp(dp, dir, dirl) && dp[dirl] == 0)
- return cbdata->norq.elements[off + 1];
- }
- h = HASHCHAIN_NEXT(h, hh, cbdata->normapn);
- }
- if (!create)
- return 0;
- /* new dir. work. */
- if (dir >= (const char *)cbdata->filesspace && dir < (const char *)cbdata->filesspace + cbdata->filesspacen)
- {
- /* can happen when called from unifywithcanon */
- Id off = dir - (const char *)cbdata->filesspace;
- nspaceoff = addfilesspace(cbdata, dirl + 1);
- dir = (const char *)cbdata->filesspace + off;
- }
- else
- nspaceoff = addfilesspace(cbdata, dirl + 1);
- if (dirl)
- memcpy(cbdata->filesspace + nspaceoff, dir, dirl);
- cbdata->filesspace[nspaceoff + dirl] = 0;
- mycnt = cbdata->norq.count;
- queue_push2(&cbdata->norq, nspaceoff, -1); /* -1: in progress */
- cbdata->normap[2 * h] = hx;
- cbdata->normap[2 * h + 1] = mycnt;
- if (++cbdata->normapused * 2 > cbdata->normapn)
- cbdata->normap = growhash(cbdata->normap, &cbdata->normapn);
- /* unify */
- if (cbdata->usestat)
- nspaceoff = unifywithstat(cbdata, nspaceoff, dirl);
- else
- nspaceoff = unifywithcanon(cbdata, nspaceoff, dirl);
- cbdata->norq.elements[mycnt + 1] = nspaceoff; /* patch in result */
-#if 0
- if (!cbdata->usestat)
- printf("%s normalized to %d: %s\n", cbdata->filesspace + cbdata->norq.elements[mycnt], nspaceoff, cbdata->filesspace + nspaceoff);
-#endif
- return nspaceoff;
-}
-
-static void
-findfileconflicts_alias_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
-{
- int isdir = S_ISDIR(info->mode);
- struct cbdata *cbdata = cbdatav;
- const char *dp;
- Id idx, dirid;
- Id hx, qx;
- Hashval h, hh;
-
- idx = cbdata->idx;
-
- if (!info->dirlen)
- return;
- dp = fn + info->dirlen;
- if (info->diridx != cbdata->lastdiridx)
- {
- cbdata->lastdiridx = info->diridx;
- cbdata->lastdirhash = 0;
- }
- dp = fn + info->dirlen;
- hx = strhash(dp);
- if (!hx)
- hx = strlen(fn) + 1;
-
- h = hx & cbdata->cflmapn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = cbdata->cflmap[2 * h];
- if (!qx)
- break;
- if (qx == hx)
- break;
- h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
- }
- if (!qx || cbdata->cflmap[2 * h + 1] != -1)
- return;
- if (!cbdata->lastdirhash)
- cbdata->lastdirhash = strnhash(fn, dp - fn);
- dirid = normalizedir(cbdata, fn, dp - fn, cbdata->lastdirhash, 1);
- queue_push2(&cbdata->lookat, hx, idx);
- queue_push2(&cbdata->lookat, cbdata->lastdirhash, isdir ? -dirid : dirid);
-}
-
-static void
-findfileconflicts2_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
-{
- struct cbdata *cbdata = cbdatav;
- Hashval hx;
- const char *dp;
- char md5padded[34];
- Id off;
-
- if (!info->dirlen)
- return;
- dp = fn + info->dirlen;
- if (info->diridx != cbdata->lastdiridx)
- {
- cbdata->lastdiridx = info->diridx;
- cbdata->lastdirhash = strnhash(fn, dp - fn);
- }
- if (cbdata->aliases)
- {
- if (cbdata->lastdirhash != cbdata->dirhash)
- return;
- hx = strhash(dp);
- }
- else
- {
- hx = cbdata->lastdirhash;
- hx = strhash_cont(dp, hx);
- }
- if (!hx)
- hx = strlen(fn) + 1;
- if ((Id)hx != cbdata->hx)
- return;
- if (cbdata->dirid && cbdata->dirid != normalizedir(cbdata, fn, dp - fn, cbdata->dirhash, 0))
- return;
- strncpy(md5padded, info->digest, 32);
- md5padded[32] = 0;
- md5padded[33] = info->color;
- /* printf("%d, hx %x -> %s %d %s\n", cbdata->idx, hx, fn, info->mode, info->digest); */
- off = addfilesspace(cbdata, strlen(fn) + (34 + 1));
- memcpy(cbdata->filesspace + off, (unsigned char *)md5padded, 34);
- strcpy((char *)cbdata->filesspace + off + 34, fn);
- queue_push(&cbdata->files, off);
-}
-
-static int
-lookat_idx_cmp(const void *ap, const void *bp, void *dp)
-{
- const Id *a = ap, *b = bp;
- unsigned int ahx, bhx;
- if (a[1] - b[1] != 0) /* idx */
- return a[1] - b[1];
- if (a[3] - b[3] != 0) /* dirid */
- return a[3] - b[3];
- ahx = (unsigned int)a[0]; /* can be < 0 */
- bhx = (unsigned int)b[0];
- if (ahx != bhx)
- return ahx < bhx ? -1 : 1;
- ahx = (unsigned int)a[2]; /* dhx */
- bhx = (unsigned int)b[2];
- if (ahx != bhx)
- return ahx < bhx ? -1 : 1;
- return 0;
-}
-
-static int
-lookat_hx_cmp(const void *ap, const void *bp, void *dp)
-{
- const Id *a = ap, *b = bp;
- unsigned int ahx, bhx;
- Id adirid, bdirid;
- ahx = (unsigned int)a[0]; /* can be < 0 */
- bhx = (unsigned int)b[0];
- if (ahx != bhx)
- return ahx < bhx ? -1 : 1;
- adirid = a[3] < 0 ? -a[3] : a[3];
- bdirid = b[3] < 0 ? -b[3] : b[3];
- if (adirid - bdirid != 0) /* dirid */
- return adirid - bdirid;
- if (a[3] != b[3])
- return a[3] > 0 ? -1 : 1; /* bring positive dirids to front */
- if (a[1] - b[1] != 0) /* idx */
- return a[1] - b[1];
- ahx = (unsigned int)a[2]; /* dhx */
- bhx = (unsigned int)b[2];
- if (ahx != bhx)
- return ahx < bhx ? -1 : 1;
- return 0;
-}
-
-static int
-conflicts_cmp(const void *ap, const void *bp, void *dp)
-{
- Pool *pool = dp;
- const Id *a = ap;
- const Id *b = bp;
- if (a[0] != b[0]) /* filename1 */
- return strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
- if (a[3] != b[3]) /* filename2 */
- return strcmp(pool_id2str(pool, a[3]), pool_id2str(pool, b[3]));
- if (a[1] != b[1]) /* pkgid1 */
- return a[1] - b[1];
- if (a[4] != b[4]) /* pkgid2 */
- return a[4] - b[4];
- return 0;
-}
-
-static void
-iterate_solvable_dirs(Pool *pool, Id p, void (*cb)(void *, const char *, struct filelistinfo *), void *cbdata)
-{
- Repodata *lastdata = 0;
- Id lastdirid = -1;
- Dataiterator di;
-
- dataiterator_init(&di, pool, 0, p, SOLVABLE_FILELIST, 0, SEARCH_COMPLETE_FILELIST);
- while (dataiterator_step(&di))
- {
- if (di.data == lastdata && di.kv.id == lastdirid)
- continue;
- lastdata = di.data;
- lastdirid = di.kv.id;
- cb(cbdata, repodata_dir2str(di.data, di.kv.id, ""), 0);
- }
- dataiterator_free(&di);
-}
-
-/* before calling the expensive findfileconflicts_cb we check if any of
- * the files match. This only makes sense when cbdata->create is off.
- */
-static int
-precheck_solvable_files(struct cbdata *cbdata, Pool *pool, Id p)
-{
- Dataiterator di;
- Id hx, qx;
- Hashval h, hh;
- int found = 0;
- int aliases = cbdata->aliases;
- unsigned int lastdirid = -1;
- Hashval lastdirhash = 0;
- int lastdirlen = 0;
- int checkthisdir = 0;
- Repodata *lastrepodata = 0;
-
- dataiterator_init(&di, pool, 0, p, SOLVABLE_FILELIST, 0, SEARCH_COMPLETE_FILELIST);
- while (dataiterator_step(&di))
- {
- if (aliases)
- {
- /* hash just the basename */
- hx = strhash(di.kv.str);
- if (!hx)
- hx = strlen(di.kv.str) + 1;
- }
- else
- {
- /* hash the full path */
- if (di.data != lastrepodata || di.kv.id != lastdirid)
- {
- const char *dir;
- lastrepodata = di.data;
- lastdirid = di.kv.id;
- dir = repodata_dir2str(lastrepodata, lastdirid, "");
- lastdirlen = strlen(dir);
- lastdirhash = strhash(dir);
- checkthisdir = isindirmap(cbdata, lastdirhash ? lastdirhash : lastdirlen + 1);
- }
- if (!checkthisdir)
- continue;
- hx = strhash_cont(di.kv.str, lastdirhash);
- if (!hx)
- hx = lastdirlen + strlen(di.kv.str) + 1;
- }
- h = hx & cbdata->cflmapn;
- hh = HASHCHAIN_START;
- for (;;)
- {
- qx = cbdata->cflmap[2 * h];
- if (!qx)
- break;
- if (qx == hx)
- {
- found = 1;
- break;
- }
- h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
- }
- if (found)
- break;
- }
- dataiterator_free(&di);
- return found;
-}
-
-
-int
-pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, int flags, void *(*handle_cb)(Pool *, Id, void *) , void *handle_cbdata)
-{
- int i, j, cflmapn, idxmapset;
- struct cbdata cbdata;
- unsigned int now, start;
- void *handle;
- Repo *installed = pool->installed;
- Id p;
- int obsoleteusescolors = pool_get_flag(pool, POOL_FLAG_OBSOLETEUSESCOLORS);
- int hdrfetches;
-
- queue_empty(conflicts);
- if (!pkgs->count)
- return 0;
-
- now = start = solv_timems(0);
- POOL_DEBUG(SOLV_DEBUG_STATS, "searching for file conflicts\n");
- POOL_DEBUG(SOLV_DEBUG_STATS, "packages: %d, cutoff %d\n", pkgs->count, cutoff);
-
- memset(&cbdata, 0, sizeof(cbdata));
- cbdata.aliases = flags & FINDFILECONFLICTS_CHECK_DIRALIASING;
- cbdata.pool = pool;
- if (cbdata.aliases && (flags & FINDFILECONFLICTS_USE_ROOTDIR) != 0)
- {
- cbdata.rootdir = pool_get_rootdir(pool);
- if (cbdata.rootdir && !strcmp(cbdata.rootdir, "/"))
- cbdata.rootdir = 0;
- if (cbdata.rootdir)
- cbdata.rootdirl = strlen(cbdata.rootdir);
- if (!cbdata.rootdir)
- cbdata.usestat = 1;
- }
- queue_init(&cbdata.lookat);
- queue_init(&cbdata.lookat_dir);
- map_init(&cbdata.idxmap, pkgs->count);
-
- if (cutoff <= 0)
- cutoff = pkgs->count;
-
- /* avarage file list size: 200 files per package */
- /* avarage dir count: 20 dirs per package */
-
- /* first pass: scan dirs */
- if (!cbdata.aliases)
- {
- hdrfetches = 0;
- cflmapn = (cutoff + 3) * 64;
- while ((cflmapn & (cflmapn - 1)) != 0)
- cflmapn = cflmapn & (cflmapn - 1);
- cbdata.dirmap = solv_calloc(cflmapn, 2 * sizeof(Id));
- cbdata.dirmapn = cflmapn - 1; /* make it a mask */
- cbdata.create = 1;
- idxmapset = 0;
- for (i = 0; i < pkgs->count; i++)
- {
- if (i == cutoff)
- cbdata.create = 0;
- cbdata.idx = i;
- p = pkgs->elements[i];
- if ((flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed)
- {
- if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed)
- {
- iterate_solvable_dirs(pool, p, finddirs_cb, &cbdata);
- if (MAPTST(&cbdata.idxmap, i))
- idxmapset++;
- continue;
- }
- }
- handle = (*handle_cb)(pool, p, handle_cbdata);
- if (!handle)
- continue;
- hdrfetches++;
- rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_ONLYDIRS, finddirs_cb, &cbdata);
- if (MAPTST(&cbdata.idxmap, i))
- idxmapset++;
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap size: %d, used %d\n", cbdata.dirmapn + 1, cbdata.dirmapused);
- POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap memory usage: %d K\n", (cbdata.dirmapn + 1) * 2 * (int)sizeof(Id) / 1024);
- POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches);
- POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap creation took %d ms\n", solv_timems(now));
- POOL_DEBUG(SOLV_DEBUG_STATS, "dir conflicts found: %d, idxmap %d of %d\n", cbdata.dirconflicts, idxmapset, pkgs->count);
- }
-
- /* second pass: scan files */
- now = solv_timems(0);
- cflmapn = (cutoff + 3) * 128;
- while ((cflmapn & (cflmapn - 1)) != 0)
- cflmapn = cflmapn & (cflmapn - 1);
- cbdata.cflmap = solv_calloc(cflmapn, 2 * sizeof(Id));
- cbdata.cflmapn = cflmapn - 1; /* make it a mask */
- cbdata.create = 1;
- hdrfetches = 0;
- for (i = 0; i < pkgs->count; i++)
- {
- if (i == cutoff)
- cbdata.create = 0;
- if (!cbdata.aliases && !MAPTST(&cbdata.idxmap, i))
- continue;
- cbdata.idx = i;
- p = pkgs->elements[i];
- if (!cbdata.create && (flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed)
- {
- if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed)
- if (!precheck_solvable_files(&cbdata, pool, p))
- continue;
- }
- /* can't use FINDFILECONFLICTS_USE_SOLVABLEFILELIST because we have to know if
- * the file is a directory or not */
- handle = (*handle_cb)(pool, p, handle_cbdata);
- if (!handle)
- continue;
- hdrfetches++;
- cbdata.lastdiridx = -1;
- rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_NOGHOSTS, cbdata.aliases ? findfileconflicts_basename_cb : findfileconflicts_cb, &cbdata);
- }
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "filemap size: %d, used %d\n", cbdata.cflmapn + 1, cbdata.cflmapused);
- POOL_DEBUG(SOLV_DEBUG_STATS, "filemap memory usage: %d K\n", (cbdata.cflmapn + 1) * 2 * (int)sizeof(Id) / 1024);
- POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches);
- POOL_DEBUG(SOLV_DEBUG_STATS, "filemap creation took %d ms\n", solv_timems(now));
- POOL_DEBUG(SOLV_DEBUG_STATS, "lookat_dir size: %d\n", cbdata.lookat_dir.count);
- queue_free(&cbdata.lookat_dir);
-
- /* we need another pass for aliases */
- if (cbdata.aliases)
- {
- now = solv_timems(0);
- /* make sure the first offset is not zero */
- addfilesspace(&cbdata, 1);
- cflmapn = (cutoff + 3) * 16;
- while ((cflmapn & (cflmapn - 1)) != 0)
- cflmapn = cflmapn & (cflmapn - 1);
- cbdata.normap = solv_calloc(cflmapn, 2 * sizeof(Id));
- cbdata.normapn = cflmapn - 1; /* make it a mask */
- if (cbdata.usestat)
- {
- cbdata.statmap = solv_calloc(cflmapn, 2 * sizeof(Id));
- cbdata.statmapn = cflmapn - 1; /* make it a mask */
- }
- cbdata.create = 0;
- hdrfetches = 0;
- for (i = 0; i < pkgs->count; i++)
- {
- if (!MAPTST(&cbdata.idxmap, i))
- continue;
- p = pkgs->elements[i];
- cbdata.idx = i;
- /* can't use FINDFILECONFLICTS_USE_SOLVABLEFILELIST because we have to know if
- * the file is a directory or not */
- handle = (*handle_cb)(pool, p, handle_cbdata);
- if (!handle)
- continue;
- hdrfetches++;
- cbdata.lastdiridx = -1;
- rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_NOGHOSTS, findfileconflicts_alias_cb, &cbdata);
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "normap size: %d, used %d\n", cbdata.normapn + 1, cbdata.normapused);
- POOL_DEBUG(SOLV_DEBUG_STATS, "normap memory usage: %d K\n", (cbdata.normapn + 1) * 2 * (int)sizeof(Id) / 1024);
- POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches);
- POOL_DEBUG(SOLV_DEBUG_STATS, "stats made: %d\n", cbdata.statsmade);
- if (cbdata.usestat)
- {
- POOL_DEBUG(SOLV_DEBUG_STATS, "statmap size: %d, used %d\n", cbdata.statmapn + 1, cbdata.statmapused);
- POOL_DEBUG(SOLV_DEBUG_STATS, "statmap memory usage: %d K\n", (cbdata.statmapn + 1) * 2 * (int)sizeof(Id) / 1024);
- }
- cbdata.statmap = solv_free(cbdata.statmap);
- cbdata.statmapn = 0;
- cbdata.canonspace = solv_free(cbdata.canonspace);
- cbdata.canonspacen = 0;
- POOL_DEBUG(SOLV_DEBUG_STATS, "alias processing took %d ms\n", solv_timems(now));
- }
-
- cbdata.dirmap = solv_free(cbdata.dirmap);
- cbdata.dirmapn = 0;
- cbdata.dirmapused = 0;
- cbdata.cflmap = solv_free(cbdata.cflmap);
- cbdata.cflmapn = 0;
- cbdata.cflmapused = 0;
-
- map_free(&cbdata.idxmap);
-
- /* sort and unify/prune */
- now = solv_timems(0);
- POOL_DEBUG(SOLV_DEBUG_STATS, "raw candidates: %d, pruning\n", cbdata.lookat.count / 4);
- solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_hx_cmp, pool);
- for (i = j = 0; i < cbdata.lookat.count; )
- {
- int first = 1;
- Id hx = cbdata.lookat.elements[i];
- Id idx = cbdata.lookat.elements[i + 1];
- Id dhx = cbdata.lookat.elements[i + 2];
- Id dirid = cbdata.lookat.elements[i + 3];
- i += 4;
- for (; i < cbdata.lookat.count && hx == cbdata.lookat.elements[i] && (dirid == cbdata.lookat.elements[i + 3] || dirid == -cbdata.lookat.elements[i + 3]); i += 4)
- {
- if (idx == cbdata.lookat.elements[i + 1] && dhx == cbdata.lookat.elements[i + 2])
- continue; /* ignore duplicates */
- if (first)
- {
- if (dirid < 0)
- continue; /* all have a neg dirid */
- cbdata.lookat.elements[j++] = hx;
- cbdata.lookat.elements[j++] = idx;
- cbdata.lookat.elements[j++] = dhx;
- cbdata.lookat.elements[j++] = dirid;
- first = 0;
- }
- idx = cbdata.lookat.elements[i + 1];
- dhx = cbdata.lookat.elements[i + 2];
- cbdata.lookat.elements[j++] = hx;
- cbdata.lookat.elements[j++] = idx;
- cbdata.lookat.elements[j++] = dhx;
- cbdata.lookat.elements[j++] = dirid;
- }
- }
- queue_truncate(&cbdata.lookat, j);
- POOL_DEBUG(SOLV_DEBUG_STATS, "candidates now: %d\n", cbdata.lookat.count / 4);
- POOL_DEBUG(SOLV_DEBUG_STATS, "pruning took %d ms\n", solv_timems(now));
-
- /* third pass: collect file info for all files that match a hx */
- now = solv_timems(0);
- solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_idx_cmp, pool);
- queue_init(&cbdata.files);
- hdrfetches = 0;
- for (i = 0; i < cbdata.lookat.count; i += 4)
- {
- Id idx = cbdata.lookat.elements[i + 1];
- int iterflags = RPM_ITERATE_FILELIST_WITHMD5 | RPM_ITERATE_FILELIST_NOGHOSTS;
- if (obsoleteusescolors)
- iterflags |= RPM_ITERATE_FILELIST_WITHCOL;
- p = pkgs->elements[idx];
- handle = (*handle_cb)(pool, p, handle_cbdata);
- if (handle)
- hdrfetches++;
- for (;; i += 4)
- {
- int fstart = cbdata.files.count;
- queue_push(&cbdata.files, idx);
- queue_push(&cbdata.files, 0);
- cbdata.idx = idx;
- cbdata.hx = cbdata.lookat.elements[i];
- cbdata.dirhash = cbdata.lookat.elements[i + 2];
- cbdata.dirid = cbdata.lookat.elements[i + 3];
- cbdata.lastdiridx = -1;
- if (handle)
- rpm_iterate_filelist(handle, iterflags, findfileconflicts2_cb, &cbdata);
- cbdata.files.elements[fstart + 1] = cbdata.files.count;
- cbdata.lookat.elements[i + 1] = fstart;
- if (i + 4 >= cbdata.lookat.count || cbdata.lookat.elements[i + 4 + 1] != idx)
- break;
- }
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches);
- POOL_DEBUG(SOLV_DEBUG_STATS, "file info fetching took %d ms\n", solv_timems(now));
-
- cbdata.normap = solv_free(cbdata.normap);
- cbdata.normapn = 0;
-
- /* forth pass: for each hx we have, compare all matching files against all other matching files */
- now = solv_timems(0);
- solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_hx_cmp, pool);
- for (i = 0; i < cbdata.lookat.count - 4; i += 4)
- {
- Id hx = cbdata.lookat.elements[i];
- Id pstart = cbdata.lookat.elements[i + 1];
- Id dirid = cbdata.lookat.elements[i + 3];
- Id pidx = cbdata.files.elements[pstart];
- Id pend = cbdata.files.elements[pstart + 1];
- if (cbdata.lookat.elements[i + 4] != hx)
- continue; /* no package left with that hx */
- for (j = i + 4; j < cbdata.lookat.count && cbdata.lookat.elements[j] == hx && cbdata.lookat.elements[j + 3] == dirid; j += 4)
- {
- Id qstart = cbdata.lookat.elements[j + 1];
- Id qidx = cbdata.files.elements[qstart];
- Id qend = cbdata.files.elements[qstart + 1];
- int ii, jj;
- if (pidx >= cutoff && qidx >= cutoff)
- continue; /* no conflicts between packages with idx >= cutoff */
- for (ii = pstart + 2; ii < pend; ii++)
- for (jj = qstart + 2; jj < qend; jj++)
- {
- char *fsi = (char *)cbdata.filesspace + cbdata.files.elements[ii];
- char *fsj = (char *)cbdata.filesspace + cbdata.files.elements[jj];
- if (cbdata.aliases)
- {
- /* compare just the basenames, the dirs match because of the dirid */
- char *bsi = strrchr(fsi + 34, '/');
- char *bsj = strrchr(fsj + 34, '/');
- if (!bsi || !bsj)
- continue;
- if (strcmp(bsi, bsj))
- continue; /* different base names */
- }
- else
- {
- if (strcmp(fsi + 34, fsj + 34))
- continue; /* different file names */
- }
- if (!strcmp(fsi, fsj))
- continue; /* file digests match, no conflict */
- if (obsoleteusescolors && fsi[33] && fsj[33] && (fsi[33] & fsj[33]) == 0)
- continue; /* colors do not conflict */
- queue_push(conflicts, pool_str2id(pool, fsi + 34, 1));
- queue_push(conflicts, pkgs->elements[pidx]);
- queue_push(conflicts, pool_str2id(pool, fsi, 1));
- queue_push(conflicts, pool_str2id(pool, fsj + 34, 1));
- queue_push(conflicts, pkgs->elements[qidx]);
- queue_push(conflicts, pool_str2id(pool, fsj, 1));
- }
- }
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "filespace size: %d K\n", cbdata.filesspacen / 1024);
- POOL_DEBUG(SOLV_DEBUG_STATS, "candidate check took %d ms\n", solv_timems(now));
- cbdata.filesspace = solv_free(cbdata.filesspace);
- cbdata.filesspacen = 0;
- queue_free(&cbdata.lookat);
- queue_free(&cbdata.files);
- if (conflicts->count > 6)
- solv_sort(conflicts->elements, conflicts->count / 6, 6 * sizeof(Id), conflicts_cmp, pool);
- POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file conflicts\n", conflicts->count / 6);
- POOL_DEBUG(SOLV_DEBUG_STATS, "file conflict detection took %d ms\n", solv_timems(start));
-
- return conflicts->count / 6;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2009-2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef POOL_FILECONFLICTS_H
-#define POOL_FILECONFLICTS_H
-
-#include "pool.h"
-
-extern int pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, int flags, void *(*handle_cb)(Pool *, Id, void *) , void *handle_cbdata);
-
-#define FINDFILECONFLICTS_USE_SOLVABLEFILELIST (1 << 0)
-#define FINDFILECONFLICTS_CHECK_DIRALIASING (1 << 1)
-#define FINDFILECONFLICTS_USE_ROOTDIR (1 << 2)
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2015, SUSE Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/* this is used by repo_rpmmd, repo_rpmdb, and repo_susetags */
-
-#include <stdio.h>
-
-#include "pool.h"
-#include "pool_parserpmrichdep.h"
-
-static struct RichOpComp {
- const char *n;
- int l;
- Id fl;
-} RichOps[] = {
- { "and", 3, REL_AND },
- { "or", 2, REL_OR },
- { "if", 2, REL_COND },
- { "else", 4, REL_ELSE },
- { NULL, 0, 0},
-};
-
-static Id
-parseRichDep(Pool *pool, const char **depp, Id chainfl)
-{
- const char *p = *depp;
- const char *n;
- Id id, evr;
- int fl, bl;
- struct RichOpComp *op;
-
- if (!chainfl && *p++ != '(')
- return 0;
- while (*p == ' ')
- p++;
- if (*p == ')')
- return 0;
- if (*p == '(')
- {
- id = parseRichDep(pool, &p, 0);
- if (!id)
- return 0;
- }
- else
- {
- n = p;
- bl = 0;
- while (*p && !(*p == ' ' || *p == ',' || (*p == ')' && bl-- <= 0)))
- if (*p++ == '(')
- bl++;
- if (n == p)
- return 0;
- id = pool_strn2id(pool, n, p - n, 1);
- while (*p == ' ')
- p++;
- if (*p)
- {
- fl = 0;
- for (;; p++)
- {
- if (*p == '<')
- fl |= REL_LT;
- else if (*p == '=')
- fl |= REL_EQ;
- else if (*p == '>')
- fl |= REL_GT;
- else
- break;
- }
- if (fl)
- {
- while (*p == ' ')
- p++;
- n = p;
- bl = 0;
- while (*p && !(*p == ' ' || *p == ',' || (*p == ')' && bl-- <= 0)))
- if (*p++ == '(')
- bl++;
- if (p - n > 2 && n[0] == '0' && n[1] == ':')
- n += 2; /* strip zero epoch */
- if (n == p)
- return 0;
- id = pool_rel2id(pool, id, pool_strn2id(pool, n, p - n, 1), fl, 1);
- }
- }
- }
- while (*p == ' ')
- p++;
- if (!*p)
- return 0;
- if (*p == ')')
- {
- *depp = p + 1;
- return id;
- }
- n = p;
- while (*p && *p != ' ')
- p++;
- for (op = RichOps; op->n; op++)
- if (p - n == op->l && !strncmp(n, op->n, op->l))
- break;
- fl = op->fl;
- if (!fl)
- return 0;
- if (chainfl == REL_COND && fl == REL_ELSE)
- chainfl = 0;
- if (chainfl && fl != chainfl)
- return 0;
- evr = parseRichDep(pool, &p, fl);
- if (!evr)
- return 0;
- *depp = p;
- return pool_rel2id(pool, id, evr, fl, 1);
-}
-
-Id
-pool_parserpmrichdep(Pool *pool, const char *dep)
-{
- Id id = parseRichDep(pool, &dep, 0);
- if (id && *dep)
- id = 0;
- return id;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2014, SUSE Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef POOL_PARSERPMRICHDEP_H
-#define POOL_PARSERPMRICHDEP_H
-
-#include "pool.h"
-
-extern Id pool_parserpmrichdep(Pool *pool, const char *dep);
-
-#endif
+++ /dev/null
-/*
- * repo_appdatadb.c
- *
- * Parses AppSteam Data files.
- * See http://people.freedesktop.org/~hughsient/appdata/
- *
- *
- * Copyright (c) 2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <dirent.h>
-#include <expat.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "repo_appdata.h"
-
-
-enum state {
- STATE_START,
- STATE_APPLICATION,
- STATE_ID,
- STATE_PKGNAME,
- STATE_LICENCE,
- STATE_NAME,
- STATE_SUMMARY,
- STATE_DESCRIPTION,
- STATE_P,
- STATE_UL,
- STATE_UL_LI,
- STATE_OL,
- STATE_OL_LI,
- STATE_URL,
- STATE_GROUP,
- STATE_KEYWORDS,
- STATE_KEYWORD,
- STATE_EXTENDS,
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-/* !! must be sorted by first column !! */
-static struct stateswitch stateswitches[] = {
- { STATE_START, "applications", STATE_START, 0 },
- { STATE_START, "components", STATE_START, 0 },
- { STATE_START, "application", STATE_APPLICATION, 0 },
- { STATE_START, "component", STATE_APPLICATION, 0 },
- { STATE_APPLICATION, "id", STATE_ID, 1 },
- { STATE_APPLICATION, "pkgname", STATE_PKGNAME, 1 },
- { STATE_APPLICATION, "product_license", STATE_LICENCE, 1 },
- { STATE_APPLICATION, "name", STATE_NAME, 1 },
- { STATE_APPLICATION, "summary", STATE_SUMMARY, 1 },
- { STATE_APPLICATION, "description", STATE_DESCRIPTION, 0 },
- { STATE_APPLICATION, "url", STATE_URL, 1 },
- { STATE_APPLICATION, "project_group", STATE_GROUP, 1 },
- { STATE_APPLICATION, "keywords", STATE_KEYWORDS, 0 },
- { STATE_APPLICATION, "extends", STATE_EXTENDS, 1 },
- { STATE_DESCRIPTION, "p", STATE_P, 1 },
- { STATE_DESCRIPTION, "ul", STATE_UL, 0 },
- { STATE_DESCRIPTION, "ol", STATE_OL, 0 },
- { STATE_UL, "li", STATE_UL_LI, 1 },
- { STATE_OL, "li", STATE_OL_LI, 1 },
- { STATE_KEYWORDS, "keyword", STATE_KEYWORD, 1 },
- { NUMSTATES }
-};
-
-struct parsedata {
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
- Pool *pool;
- Repo *repo;
- Repodata *data;
-
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
-
- Solvable *solvable;
- Id handle;
-
- char *description;
- int licnt;
- int skip_depth;
- int flags;
- char *desktop_file;
- int havesummary;
- const char *filename;
- Queue *owners;
-};
-
-
-static inline const char *
-find_attr(const char *txt, const char **atts)
-{
- for (; *atts; atts += 2)
- if (!strcmp(*atts, txt))
- return atts[1];
- return 0;
-}
-
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- struct stateswitch *sw;
- const char *type;
-
-#if 0
- fprintf(stderr, "start: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
-
- pd->depth++;
- if (!pd->swtab[pd->state]) /* no statetable -> no substates */
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#endif
- return;
- }
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
- if (!strcmp(sw->ename, name))
- break;
-
- if (sw->from != pd->state)
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#endif
- return;
- }
- pd->state = sw->to;
- pd->docontent = sw->docontent;
- pd->statedepth = pd->depth;
- pd->lcontent = 0;
- *pd->content = 0;
-
- if (!pd->skip_depth && find_attr("xml:lang", atts))
- pd->skip_depth = pd->depth;
- if (pd->skip_depth)
- {
- pd->docontent = 0;
- return;
- }
-
- switch(pd->state)
- {
- case STATE_APPLICATION:
- s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
- pd->handle = s - pool->solvables;
- pd->havesummary = 0;
- type = find_attr("type", atts);
- if (!type || !*type)
- type = "desktop";
- repodata_set_poolstr(pd->data, pd->handle, SOLVABLE_CATEGORY, type);
- break;
- case STATE_DESCRIPTION:
- pd->description = solv_free(pd->description);
- break;
- case STATE_OL:
- case STATE_UL:
- pd->licnt = 0;
- break;
- default:
- break;
- }
-}
-
-/* replace whitespace with one space/newline */
-/* also strip starting/ending whitespace */
-static void
-wsstrip(struct parsedata *pd)
-{
- int i, j;
- int ws = 0;
- for (i = j = 0; pd->content[i]; i++)
- {
- if (pd->content[i] == ' ' || pd->content[i] == '\t' || pd->content[i] == '\n')
- {
- ws |= pd->content[i] == '\n' ? 2 : 1;
- continue;
- }
- if (ws && j)
- pd->content[j++] = (ws & 2) ? '\n' : ' ';
- ws = 0;
- pd->content[j++] = pd->content[i];
- }
- pd->content[j] = 0;
- pd->lcontent = j;
-}
-
-/* indent all lines */
-static void
-indent(struct parsedata *pd, int il)
-{
- int i, l;
- for (l = 0; pd->content[l]; )
- {
- if (pd->content[l] == '\n')
- {
- l++;
- continue;
- }
- if (pd->lcontent + il + 1 > pd->acontent)
- {
- pd->acontent = pd->lcontent + il + 256;
- pd->content = realloc(pd->content, pd->acontent);
- }
- memmove(pd->content + l + il, pd->content + l, pd->lcontent - l + 1);
- for (i = 0; i < il; i++)
- pd->content[l + i] = ' ';
- pd->lcontent += il;
- while (pd->content[l] && pd->content[l] != '\n')
- l++;
- }
-}
-
-static void
-add_missing_tags_from_desktop_file(struct parsedata *pd, Solvable *s, const char *desktop_file)
-{
- Pool *pool = pd->pool;
- FILE *fp;
- const char *filepath;
- char buf[1024];
- char *p, *p2, *p3;
- int inde = 0;
-
- filepath = pool_tmpjoin(pool, "/usr/share/applications/", desktop_file, 0);
- if (pd->flags & REPO_USE_ROOTDIR)
- filepath = pool_prepend_rootdir_tmp(pool, filepath);
- if (!(fp = fopen(filepath, "r")))
- return;
- while (fgets(buf, sizeof(buf), fp) > 0)
- {
- int c, l = strlen(buf);
- if (!l)
- continue;
- if (buf[l - 1] != '\n')
- {
- /* ignore overlong lines */
- while ((c = getc(fp)) != EOF)
- if (c == '\n')
- break;
- if (c == EOF)
- break;
- continue;
- }
- buf[--l] = 0;
- while (l && (buf[l - 1] == ' ' || buf[l - 1] == '\t'))
- buf[--l] = 0;
- p = buf;
- while (*p == ' ' || *p == '\t')
- p++;
- if (!*p || *p == '#')
- continue;
- if (*p == '[')
- inde = 0;
- if (!strcmp(p, "[Desktop Entry]"))
- {
- inde = 1;
- continue;
- }
- if (!inde)
- continue;
- p2 = strchr(p, '=');
- if (!p2 || p2 == p)
- continue;
- *p2 = 0;
- for (p3 = p2 - 1; *p3 == ' ' || *p3 == '\t'; p3--)
- *p3 = 0;
- p2++;
- while (*p2 == ' ' || *p2 == '\t')
- p2++;
- if (!*p2)
- continue;
- if (!s->name && !strcmp(p, "Name"))
- s->name = pool_str2id(pool, pool_tmpjoin(pool, "application:", p2, 0), 1);
- else if (!pd->havesummary && !strcmp(p, "Comment"))
- {
- pd->havesummary = 1;
- repodata_set_str(pd->data, pd->handle, SOLVABLE_SUMMARY, p2);
- }
- else
- continue;
- if (s->name && pd->havesummary)
- break; /* our work is done */
- }
- fclose(fp);
-}
-
-static char *
-guess_filename_from_id(Pool *pool, const char *id)
-{
- int l = strlen(id);
- char *r = pool_tmpjoin(pool, id, ".metainfo.xml", 0);
- if (l > 8 && !strcmp(".desktop", id + l - 8))
- strcpy(r + l - 8, ".appdata.xml");
- else if (l > 4 && !strcmp(".ttf", id + l - 4))
- strcpy(r + l - 4, ".metainfo.xml");
- else if (l > 4 && !strcmp(".otf", id + l - 4))
- strcpy(r + l - 4, ".metainfo.xml");
- else if (l > 4 && !strcmp(".xml", id + l - 4))
- strcpy(r + l - 4, ".metainfo.xml");
- else if (l > 3 && !strcmp(".db", id + l - 3))
- strcpy(r + l - 3, ".metainfo.xml");
- else
- return 0;
- return r;
-}
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- Id id;
-
-#if 0
- fprintf(stderr, "end: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
-#if 0
- fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-#endif
- return;
- }
-
- pd->depth--;
- pd->statedepth--;
-
- if (pd->skip_depth && pd->depth + 1 >= pd->skip_depth)
- {
- if (pd->depth + 1 == pd->skip_depth)
- pd->skip_depth = 0;
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
- return;
- }
- pd->skip_depth = 0;
-
- switch (pd->state)
- {
- case STATE_APPLICATION:
- if (!s->arch)
- s->arch = ARCH_NOARCH;
- if (!s->evr)
- s->evr = ID_EMPTY;
- if ((!s->name || !pd->havesummary) && (pd->flags & APPDATA_CHECK_DESKTOP_FILE) != 0 && pd->desktop_file)
- add_missing_tags_from_desktop_file(pd, s, pd->desktop_file);
- if (!s->name && pd->desktop_file)
- {
- char *name = pool_tmpjoin(pool, "application:", pd->desktop_file, 0);
- int l = strlen(name);
- if (l > 8 && !strcmp(".desktop", name + l - 8))
- l -= 8;
- s->name = pool_strn2id(pool, name, l, 1);
- }
- if (!s->requires && pd->owners)
- {
- int i;
- Id id;
- for (i = 0; i < pd->owners->count; i++)
- {
- Solvable *os = pd->pool->solvables + pd->owners->elements[i];
- s->requires = repo_addid_dep(pd->repo, s->requires, os->name, 0);
- id = pool_str2id(pd->pool, pool_tmpjoin(pd->pool, "application-appdata(", pool_id2str(pd->pool, os->name), ")"), 1);
- s->provides = repo_addid_dep(pd->repo, s->provides, id, 0);
- }
- }
- if (!s->requires && (pd->desktop_file || pd->filename))
- {
- /* add appdata() link requires/provides */
- const char *filename = pd->filename;
- if (!filename)
- filename = guess_filename_from_id(pool, pd->desktop_file);
- if (filename)
- {
- filename = pool_tmpjoin(pool, "application-appdata(", filename, ")");
- s->requires = repo_addid_dep(pd->repo, s->requires, pool_str2id(pd->pool, filename + 12, 1), 0);
- s->provides = repo_addid_dep(pd->repo, s->provides, pool_str2id(pd->pool, filename, 1), 0);
- }
- }
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
- pd->solvable = 0;
- pd->desktop_file = solv_free(pd->desktop_file);
- break;
- case STATE_ID:
- pd->desktop_file = solv_strdup(pd->content);
- break;
- case STATE_NAME:
- s->name = pool_str2id(pd->pool, pool_tmpjoin(pool, "application:", pd->content, 0), 1);
- break;
- case STATE_LICENCE:
- repodata_add_poolstr_array(pd->data, pd->handle, SOLVABLE_LICENSE, pd->content);
- break;
- case STATE_SUMMARY:
- pd->havesummary = 1;
- repodata_set_str(pd->data, pd->handle, SOLVABLE_SUMMARY, pd->content);
- break;
- case STATE_URL:
- repodata_set_str(pd->data, pd->handle, SOLVABLE_URL, pd->content);
- break;
- case STATE_GROUP:
- repodata_add_poolstr_array(pd->data, pd->handle, SOLVABLE_GROUP, pd->content);
- break;
- case STATE_EXTENDS:
- repodata_add_poolstr_array(pd->data, pd->handle, SOLVABLE_EXTENDS, pd->content);
- break;
- case STATE_DESCRIPTION:
- if (pd->description)
- {
- /* strip trailing newlines */
- int l = strlen(pd->description);
- while (l && pd->description[l - 1] == '\n')
- pd->description[--l] = 0;
- repodata_set_str(pd->data, pd->handle, SOLVABLE_DESCRIPTION, pd->description);
- }
- break;
- case STATE_P:
- wsstrip(pd);
- pd->description = solv_dupappend(pd->description, pd->content, "\n\n");
- break;
- case STATE_UL_LI:
- wsstrip(pd);
- indent(pd, 4);
- pd->content[2] = '-';
- pd->description = solv_dupappend(pd->description, pd->content, "\n");
- break;
- case STATE_OL_LI:
- wsstrip(pd);
- indent(pd, 4);
- if (++pd->licnt >= 10)
- pd->content[0] = '0' + (pd->licnt / 10) % 10;
- pd->content[1] = '0' + pd->licnt % 10;
- pd->content[2] = '.';
- pd->description = solv_dupappend(pd->description, pd->content, "\n");
- break;
- case STATE_UL:
- case STATE_OL:
- pd->description = solv_dupappend(pd->description, "\n", 0);
- break;
- case STATE_PKGNAME:
- id = pool_str2id(pd->pool, pd->content, 1);
- s->requires = repo_addid_dep(pd->repo, s->requires, id, 0);
- id = pool_str2id(pd->pool, pool_tmpjoin(pd->pool, "application-appdata(", pd->content, ")"), 1);
- s->provides = repo_addid_dep(pd->repo, s->provides, id, 0);
- break;
- case STATE_KEYWORD:
- repodata_add_poolstr_array(pd->data, pd->handle, SOLVABLE_KEYWORDS, pd->content);
- break;
- default:
- break;
- }
-
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
-
-#if 0
- fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
-#endif
-}
-
-
-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->acontent = l + 256;
- pd->content = realloc(pd->content, pd->acontent);
- }
- c = pd->content + pd->lcontent;
- pd->lcontent += len;
- while (len-- > 0)
- *c++ = *s++;
- *c = 0;
-}
-
-#define BUFF_SIZE 8192
-
-static int
-repo_add_appdata_fn(Repo *repo, FILE *fp, int flags, const char *filename, Queue *owners)
-{
- Pool *pool = repo->pool;
- struct parsedata pd;
- struct stateswitch *sw;
- Repodata *data;
- char buf[BUFF_SIZE];
- int i, l;
- int ret = 0;
-
- data = repo_add_repodata(repo, flags);
- memset(&pd, 0, sizeof(pd));
- pd.repo = repo;
- pd.pool = repo->pool;
- pd.data = data;
- pd.flags = flags;
- pd.filename = filename;
- pd.owners = owners;
-
- pd.content = malloc(256);
- pd.acontent = 256;
-
- 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;
- }
-
- 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)
- {
- pool_error(pool, -1, "repo_appdata: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- if (pd.solvable)
- {
- repo_free_solvable(repo, pd.solvable - pd.pool->solvables, 1);
- pd.solvable = 0;
- }
- ret = -1;
- break;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
-
- solv_free(pd.content);
- solv_free(pd.desktop_file);
- solv_free(pd.description);
- return ret;
-}
-
-int
-repo_add_appdata(Repo *repo, FILE *fp, int flags)
-{
- return repo_add_appdata_fn(repo, fp, flags, 0, 0);
-}
-
-static void
-search_uninternalized_filelist(Repo *repo, const char *dir, Queue *res)
-{
- Pool *pool = repo->pool;
- Id rdid, p;
- Id iter, did, idid;
-
- for (rdid = 1; rdid < repo->nrepodata; rdid++)
- {
- Repodata *data = repo_id2repodata(repo, rdid);
- if (!data)
- continue;
- if (data->state == REPODATA_STUB)
- continue;
- if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
- continue;
- did = repodata_str2dir(data, dir, 0);
- if (!did)
- continue;
- for (p = data->start; p < data->end; p++)
- {
- if (p >= pool->nsolvables)
- continue;
- if (pool->solvables[p].repo != repo)
- continue;
- iter = 0;
- for (;;)
- {
- const char *str;
- int l;
- Id id;
- idid = did;
- str = repodata_lookup_dirstrarray_uninternalized(data, p, SOLVABLE_FILELIST, &idid, &iter);
- if (!iter)
- break;
- l = strlen(str);
- if (l > 12 && strncmp(str + l - 12, ".appdata.xml", 12))
- id = pool_str2id(pool, str, 1);
- else if (l > 13 && strncmp(str + l - 13, ".metainfo.xml", 13))
- id = pool_str2id(pool, str, 1);
- else
- continue;
- queue_push2(res, p, id);
- }
- }
- }
-}
-
-/* add all files ending in .appdata.xml */
-int
-repo_add_appdata_dir(Repo *repo, const char *appdatadir, int flags)
-{
- DIR *dir;
- char *dirpath;
- Repodata *data;
- Queue flq;
- Queue oq;
-
- queue_init(&flq);
- queue_init(&oq);
- if (flags & APPDATA_SEARCH_UNINTERNALIZED_FILELIST)
- search_uninternalized_filelist(repo, appdatadir, &flq);
- data = repo_add_repodata(repo, flags);
- if (flags & REPO_USE_ROOTDIR)
- dirpath = pool_prepend_rootdir(repo->pool, appdatadir);
- else
- dirpath = solv_strdup(appdatadir);
- if ((dir = opendir(dirpath)) != 0)
- {
- struct dirent *entry;
- while ((entry = readdir(dir)))
- {
- const char *n;
- FILE *fp;
- int len = strlen(entry->d_name);
- if (entry->d_name[0] == '.')
- continue;
- if (!(len > 12 && !strcmp(entry->d_name + len - 12, ".appdata.xml")) &&
- !(len > 13 && !strcmp(entry->d_name + len - 13, ".metainfo.xml")))
- continue;
- n = pool_tmpjoin(repo->pool, dirpath, "/", entry->d_name);
- fp = fopen(n, "r");
- if (!fp)
- {
- pool_error(repo->pool, 0, "%s: %s", n, strerror(errno));
- continue;
- }
- if (flags & APPDATA_SEARCH_UNINTERNALIZED_FILELIST)
- {
- Id id = pool_str2id(repo->pool, entry->d_name, 0);
- queue_empty(&oq);
- if (id)
- {
- int i;
- for (i = 0; i < flq.count; i += 2)
- if (flq.elements[i + 1] == id)
- queue_push(&oq, flq.elements[i]);
- }
- }
- repo_add_appdata_fn(repo, fp, flags | REPO_NO_INTERNALIZE | REPO_REUSE_REPODATA | APPDATA_CHECK_DESKTOP_FILE, entry->d_name, oq.count ? &oq : 0);
- fclose(fp);
- }
- closedir(dir);
- }
- solv_free(dirpath);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- queue_free(&oq);
- queue_free(&flq);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (c) 2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-int repo_add_appdata(Repo *repo, FILE *fp, int flags);
-int repo_add_appdata_dir(Repo *repo, const char *appdatadir, int flags);
-
-#define APPDATA_SEARCH_UNINTERNALIZED_FILELIST (1 << 8)
-#define APPDATA_CHECK_DESKTOP_FILE (1 << 30) /* internal */
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "chksum.h"
-#include "solv_xfopen.h"
-#include "repo_arch.h"
-
-static long long parsenum(unsigned char *p, int cnt)
-{
- long long x = 0;
- if (!cnt)
- return -1;
- if (*p & 0x80)
- {
- /* binary format */
- x = *p & 0x40 ? (-1 << 8 | *p) : (*p ^ 0x80);
- while (--cnt > 0)
- x = (x << 8) | *p++;
- return x;
- }
- while (cnt > 0 && (*p == ' ' || *p == '\t'))
- cnt--, p++;
- if (*p == '-')
- return -1;
- for (; cnt > 0 && *p >= '0' && *p < '8'; cnt--, p++)
- x = (x << 3) | (*p - '0');
- return x;
-}
-
-static int readblock(FILE *fp, unsigned char *blk)
-{
- int r, l = 0;
- while (l < 512)
- {
- r = fread(blk + l, 1, 512 - l, fp);
- if (r <= 0)
- return -1;
- l += r;
- }
- return 0;
-}
-
-struct tarhead {
- FILE *fp;
- unsigned char blk[512];
- int type;
- long long length;
- char *path;
- int eof;
- int ispax;
- int off;
- int end;
-};
-
-static char *getsentry(struct tarhead *th, char *s, int size)
-{
- char *os = s;
- if (th->eof || size <= 1)
- return 0;
- size--; /* terminating 0 */
- for (;;)
- {
- int i;
- for (i = th->off; i < th->end; i++)
- {
- *s++ = th->blk[i];
- size--;
- if (!size || th->blk[i] == '\n')
- {
- th->off = i + 1;
- *s = 0;
- return os;
- }
- }
- th->off = i;
- if (!th->path)
- {
- /* fake entry */
- th->end = fread(th->blk, 1, 512, th->fp);
- if (th->end <= 0)
- {
- th->eof = 1;
- return 0;
- }
- th->off = 0;
- continue;
- }
- if (th->length <= 0)
- return 0;
- if (readblock(th->fp, th->blk))
- {
- th->eof = 1;
- return 0;
- }
- th->off = 0;
- th->end = th->length > 512 ? 512 : th->length;
- th->length -= th->end;
- }
-}
-
-static void skipentry(struct tarhead *th)
-{
- for (; th->length > 0; th->length -= 512)
- {
- if (readblock(th->fp, th->blk))
- {
- th->eof = 1;
- th->length = 0;
- return;
- }
- }
- th->length = 0;
- th->off = th->end = 0;
-}
-
-static void inittarhead(struct tarhead *th, FILE *fp)
-{
- memset(th, 0, sizeof(*th));
- th->fp = fp;
-}
-
-static void freetarhead(struct tarhead *th)
-{
- solv_free(th->path);
-}
-
-static int gettarhead(struct tarhead *th)
-{
- int l, type;
- long long length;
-
- th->path = solv_free(th->path);
- th->ispax = 0;
- th->type = 0;
- th->length = 0;
- th->off = 0;
- th->end = 0;
- if (th->eof)
- return 0;
- for (;;)
- {
- int r = readblock(th->fp, th->blk);
- if (r)
- {
- if (feof(th->fp))
- {
- th->eof = 1;
- return 0;
- }
- return -1;
- }
- if (th->blk[0] == 0)
- {
- th->eof = 1;
- return 0;
- }
- length = parsenum(th->blk + 124, 12);
- if (length < 0)
- return -1;
- type = 0;
- switch (th->blk[156])
- {
- case 'S': case '0':
- type = 1; /* file */
- break;
- case '1':
- /* hard link, special length magic... */
- if (!th->ispax)
- length = 0;
- break;
- case '5':
- type = 2; /* dir */
- break;
- case '2': case '3': case '4': case '6':
- length = 0;
- break;
- case 'X': case 'x': case 'L':
- {
- char *data, *pp;
- if (length < 1 || length >= 1024 * 1024)
- return -1;
- data = pp = solv_malloc(length + 512);
- for (l = length; l > 0; l -= 512, pp += 512)
- if (readblock(th->fp, (unsigned char *)pp))
- {
- solv_free(data);
- return -1;
- }
- data[length] = 0;
- type = 3; /* extension */
- if (th->blk[156] == 'L')
- {
- solv_free(th->path);
- th->path = data;
- length = 0;
- break;
- }
- pp = data;
- while (length > 0)
- {
- int ll = 0;
- for (l = 0; l < length && pp[l] >= '0' && pp[l] <= '9'; l++)
- ll = ll * 10 + (pp[l] - '0');
- if (l == length || pp[l] != ' ' || ll < 1 || ll > length || pp[ll - 1] != '\n')
- {
- solv_free(data);
- return -1;
- }
- length -= ll;
- pp += l + 1;
- ll -= l + 1;
- pp[ll - 1] = 0;
- if (!strncmp(pp, "path=", 5))
- {
- solv_free(th->path);
- th->path = solv_strdup(pp + 5);
- }
- pp += ll;
- }
- solv_free(data);
- th->ispax = 1;
- length = 0;
- break;
- }
- default:
- type = 3; /* extension */
- break;
- }
- if ((type == 1 || type == 2) && !th->path)
- {
- char path[157];
- memcpy(path, th->blk, 156);
- path[156] = 0;
- if (!memcmp(th->blk + 257, "ustar\0\060\060", 8) && !th->path && th->blk[345])
- {
- /* POSIX ustar with prefix */
- char prefix[156];
- memcpy(prefix, th->blk + 345, 155);
- prefix[155] = 0;
- l = strlen(prefix);
- if (l && prefix[l - 1] == '/')
- prefix[l - 1] = 0;
- th->path = solv_dupjoin(prefix, "/", path);
- }
- else
- th->path = solv_dupjoin(path, 0, 0);
- }
- if (type == 1 || type == 2)
- {
- l = strlen(th->path);
- if (l && th->path[l - 1] == '/')
- {
- if (l > 1)
- th->path[l - 1] = 0;
- type = 2;
- }
- }
- if (type != 3)
- break;
- while (length > 0)
- {
- r = readblock(th->fp, th->blk);
- if (r)
- return r;
- length -= 512;
- }
- }
- th->type = type;
- th->length = length;
- return 1;
-}
-
-static Offset
-adddep(Repo *repo, Offset olddeps, char *line)
-{
- Pool *pool = repo->pool;
- char *p;
- Id id;
-
- while (*line == ' ' || *line == '\t')
- line++;
- p = line;
- while (*p && *p != ' ' && *p != '\t' && *p != '<' && *p != '=' && *p != '>')
- p++;
- id = pool_strn2id(pool, line, p - line, 1);
- while (*p == ' ' || *p == '\t')
- p++;
- if (*p == '<' || *p == '=' || *p == '>')
- {
- int flags = 0;
- for (;; p++)
- {
- if (*p == '<')
- flags |= REL_LT;
- else if (*p == '=')
- flags |= REL_EQ;
- else if (*p == '>')
- flags |= REL_GT;
- else
- break;
- }
- while (*p == ' ' || *p == '\t')
- p++;
- line = p;
- while (*p && *p != ' ' && *p != '\t')
- p++;
- id = pool_rel2id(pool, id, pool_strn2id(pool, line, p - line, 1), flags, 1);
- }
- return repo_addid_dep(repo, olddeps, id, 0);
-}
-
-Id
-repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- FILE *fp;
- struct tarhead th;
- char line[4096];
- int ignoreline;
- Solvable *s;
- int l, fd;
- struct stat stb;
- Chksum *pkgidchk = 0;
-
- data = repo_add_repodata(repo, flags);
- if ((fd = open(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, fn) : fn, O_RDONLY, 0)) < 0)
- {
- pool_error(pool, -1, "%s: %s", fn, strerror(errno));
- return 0;
- }
- if (fstat(fd, &stb))
- {
- pool_error(pool, -1, "%s: fstat: %s", fn, strerror(errno));
- close(fd);
- return 0;
- }
- if (!(fp = solv_xfopen_fd(fn, fd, "r")))
- {
- pool_error(pool, -1, "%s: fdopen failed", fn);
- close(fd);
- return 0;
- }
- s = 0;
- inittarhead(&th, fp);
- while (gettarhead(&th) > 0)
- {
- if (th.type != 1 || strcmp(th.path, ".PKGINFO") != 0)
- {
- skipentry(&th);
- continue;
- }
- ignoreline = 0;
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- if (flags & ARCH_ADD_WITH_PKGID)
- pkgidchk = solv_chksum_create(REPOKEY_TYPE_MD5);
- while (getsentry(&th, line, sizeof(line)))
- {
- l = strlen(line);
- if (l == 0)
- continue;
- if (pkgidchk)
- solv_chksum_add(pkgidchk, line, l);
- if (line[l - 1] != '\n')
- {
- ignoreline = 1;
- continue;
- }
- if (ignoreline)
- {
- ignoreline = 0;
- continue;
- }
- line[--l] = 0;
- if (l == 0 || line[0] == '#')
- continue;
- if (!strncmp(line, "pkgname = ", 10))
- s->name = pool_str2id(pool, line + 10, 1);
- else if (!strncmp(line, "pkgver = ", 9))
- s->evr = pool_str2id(pool, line + 9, 1);
- else if (!strncmp(line, "pkgdesc = ", 10))
- {
- repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 10);
- repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line + 10);
- }
- else if (!strncmp(line, "url = ", 6))
- repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line + 6);
- else if (!strncmp(line, "builddate = ", 12))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line + 12, 0, 10));
- else if (!strncmp(line, "packager = ", 11))
- repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line + 11);
- else if (!strncmp(line, "size = ", 7))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line + 7, 0, 10));
- else if (!strncmp(line, "arch = ", 7))
- s->arch = pool_str2id(pool, line + 7, 1);
- else if (!strncmp(line, "license = ", 10))
- repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line + 10);
- else if (!strncmp(line, "replaces = ", 11))
- s->obsoletes = adddep(repo, s->obsoletes, line + 11);
- else if (!strncmp(line, "group = ", 8))
- repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_GROUP, line + 8);
- else if (!strncmp(line, "depend = ", 9))
- s->requires = adddep(repo, s->requires, line + 9);
- else if (!strncmp(line, "optdepend = ", 12))
- {
- char *p = strchr(line, ':');
- if (p)
- *p = 0;
- s->suggests = adddep(repo, s->suggests, line + 12);
- }
- else if (!strncmp(line, "conflict = ", 11))
- s->conflicts = adddep(repo, s->conflicts, line + 11);
- else if (!strncmp(line, "provides = ", 11))
- s->provides = adddep(repo, s->provides, line + 11);
- }
- break;
- }
- freetarhead(&th);
- fclose(fp);
- if (!s)
- {
- pool_error(pool, -1, "%s: not an arch package", fn);
- if (pkgidchk)
- solv_chksum_free(pkgidchk, 0);
- return 0;
- }
- if (s && !s->name)
- {
- pool_error(pool, -1, "%s: package has no name", fn);
- repo_free_solvable(repo, s - pool->solvables, 1);
- s = 0;
- }
- if (s)
- {
- if (!s->arch)
- s->arch = ARCH_ANY;
- if (!s->evr)
- s->evr = ID_EMPTY;
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- if (!(flags & REPO_NO_LOCATION))
- repodata_set_location(data, s - pool->solvables, 0, 0, fn);
- if (S_ISREG(stb.st_mode))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
- if (pkgidchk)
- {
- unsigned char pkgid[16];
- solv_chksum_free(pkgidchk, pkgid);
- repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
- pkgidchk = 0;
- }
- }
- if (pkgidchk)
- solv_chksum_free(pkgidchk, 0);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return s ? s - pool->solvables : 0;
-}
-
-static char *getsentrynl(struct tarhead *th, char *s, int size)
-{
- int l;
- if (!getsentry(th, s, size))
- {
- *s = 0; /* eof */
- return 0;
- }
- l = strlen(s);
- if (!l)
- return 0;
- if (l && s[l - 1] == '\n')
- {
- s[l - 1] = 0;
- return s;
- }
- while (getsentry(th, s, size))
- {
- l = strlen(s);
- if (!l || s[l - 1] == '\n')
- return 0;
- }
- *s = 0; /* eof */
- return 0;
-}
-
-static Hashtable
-joinhash_init(Repo *repo, Hashval *hmp)
-{
- Hashval hm = mkmask(repo->nsolvables);
- Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
- Hashval h, hh;
- Solvable *s;
- int i;
-
- FOR_REPO_SOLVABLES(repo, i, s)
- {
- hh = HASHCHAIN_START;
- h = s->name & hm;
- while (ht[h])
- h = HASHCHAIN_NEXT(h, hh, hm);
- ht[h] = i;
- }
- *hmp = hm;
- return ht;
-}
-
-static Solvable *
-joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, const char *fn)
-{
- const char *p;
- Id name, evr;
- Hashval h, hh;
-
- if ((p = strrchr(fn, '/')) != 0)
- fn = p + 1;
- /* here we assume that the dirname is name-evr */
- if (!*fn)
- return 0;
- for (p = fn + strlen(fn) - 1; p > fn; p--)
- {
- while (p > fn && *p != '-')
- p--;
- if (p == fn)
- return 0;
- name = pool_strn2id(repo->pool, fn, p - fn, 0);
- if (!name)
- continue;
- evr = pool_str2id(repo->pool, p + 1, 0);
- if (!evr)
- continue;
- /* found valid name/evr combination, check hash */
- hh = HASHCHAIN_START;
- h = name & hm;
- while (ht[h])
- {
- Solvable *s = repo->pool->solvables + ht[h];
- if (s->name == name && s->evr == evr)
- return s;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- }
- return 0;
-}
-
-static void
-adddata(Repodata *data, Solvable *s, struct tarhead *th)
-{
- Repo *repo = data->repo;
- Pool *pool = repo->pool;
- char line[4096];
- int l;
- int havesha256 = 0;
-
- while (getsentry(th, line, sizeof(line)))
- {
- l = strlen(line);
- if (l == 0 || line[l - 1] != '\n')
- continue;
- line[--l] = 0;
- if (l <= 2 || line[0] != '%' || line[l - 1] != '%')
- continue;
- if (!strcmp(line, "%FILENAME%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- repodata_set_location(data, s - pool->solvables, 0, 0, line);
- }
- else if (!strcmp(line, "%NAME%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- s->name = pool_str2id(pool, line, 1);
- }
- else if (!strcmp(line, "%VERSION%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- s->evr = pool_str2id(pool, line, 1);
- }
- else if (!strcmp(line, "%DESC%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- {
- repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line);
- repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line);
- }
- }
- else if (!strcmp(line, "%GROUPS%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_GROUP, line);
- }
- else if (!strcmp(line, "%CSIZE%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(line, 0, 10));
- }
- else if (!strcmp(line, "%ISIZE%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line, 0, 10));
- }
- else if (!strcmp(line, "%MD5SUM%"))
- {
- if (getsentrynl(th, line, sizeof(line)) && !havesha256)
- repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_MD5, line);
- }
- else if (!strcmp(line, "%SHA256SUM%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- {
- repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, line);
- havesha256 = 1;
- }
- }
- else if (!strcmp(line, "%URL%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line);
- }
- else if (!strcmp(line, "%LICENSE%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line);
- }
- else if (!strcmp(line, "%ARCH%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- s->arch = pool_str2id(pool, line, 1);
- }
- else if (!strcmp(line, "%BUILDDATE%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line, 0, 10));
- }
- else if (!strcmp(line, "%PACKAGER%"))
- {
- if (getsentrynl(th, line, sizeof(line)))
- repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line);
- }
- else if (!strcmp(line, "%REPLACES%"))
- {
- while (getsentrynl(th, line, sizeof(line)) && *line)
- s->obsoletes = adddep(repo, s->obsoletes, line);
- }
- else if (!strcmp(line, "%DEPENDS%"))
- {
- while (getsentrynl(th, line, sizeof(line)) && *line)
- s->requires = adddep(repo, s->requires, line);
- }
- else if (!strcmp(line, "%CONFLICTS%"))
- {
- while (getsentrynl(th, line, sizeof(line)) && *line)
- s->conflicts = adddep(repo, s->conflicts, line);
- }
- else if (!strcmp(line, "%PROVIDES%"))
- {
- while (getsentrynl(th, line, sizeof(line)) && *line)
- s->provides = adddep(repo, s->provides, line);
- }
- else if (!strcmp(line, "%OPTDEPENDS%"))
- {
- while (getsentrynl(th, line, sizeof(line)) && *line)
- {
- char *p = strchr(line, ':');
- if (p && p > line)
- *p = 0;
- s->suggests = adddep(repo, s->suggests, line);
- }
- }
- else if (!strcmp(line, "%FILES%"))
- {
- while (getsentrynl(th, line, sizeof(line)) && *line)
- {
- char *p;
- Id id;
- l = strlen(line);
- if (l > 1 && line[l - 1] == '/')
- line[--l] = 0; /* remove trailing slashes */
- if ((p = strrchr(line , '/')) != 0)
- {
- *p++ = 0;
- if (line[0] != '/') /* anchor */
- {
- char tmp = *p;
- memmove(line + 1, line, p - 1 - line);
- *line = '/';
- *p = 0;
- id = repodata_str2dir(data, line, 1);
- *p = tmp;
- }
- else
- id = repodata_str2dir(data, line, 1);
- }
- else
- {
- p = line;
- id = 0;
- }
- if (!id)
- id = repodata_str2dir(data, "/", 1);
- repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, id, p);
- }
- }
- while (*line)
- getsentrynl(th, line, sizeof(line));
- }
-}
-
-static void
-finishsolvable(Repo *repo, Solvable *s)
-{
- Pool *pool = repo->pool;
- if (!s)
- return;
- if (!s->name)
- {
- repo_free_solvable(repo, s - pool->solvables, 1);
- return;
- }
- if (!s->arch)
- s->arch = ARCH_ANY;
- if (!s->evr)
- s->evr = ID_EMPTY;
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
-}
-
-int
-repo_add_arch_repo(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- struct tarhead th;
- char *lastdn = 0;
- int lastdnlen = 0;
- Solvable *s = 0;
- Hashtable joinhash = 0;
- Hashval joinhashmask = 0;
-
- data = repo_add_repodata(repo, flags);
-
- if (flags & REPO_EXTEND_SOLVABLES)
- joinhash = joinhash_init(repo, &joinhashmask);
-
- inittarhead(&th, fp);
- while (gettarhead(&th) > 0)
- {
- char *bn;
- if (th.type != 1)
- {
- skipentry(&th);
- continue;
- }
- bn = strrchr(th.path, '/');
- if (!bn || (strcmp(bn + 1, "desc") != 0 && strcmp(bn + 1, "depends") != 0 && strcmp(bn + 1, "files") != 0))
- {
- skipentry(&th);
- continue;
- }
- if ((flags & REPO_EXTEND_SOLVABLES) != 0 && (!strcmp(bn + 1, "desc") || !strcmp(bn + 1, "depends")))
- {
- skipentry(&th);
- continue; /* skip those when we're extending */
- }
- if (!lastdn || (bn - th.path) != lastdnlen || strncmp(lastdn, th.path, lastdnlen) != 0)
- {
- finishsolvable(repo, s);
- solv_free(lastdn);
- lastdn = solv_strdup(th.path);
- lastdnlen = bn - th.path;
- lastdn[lastdnlen] = 0;
- if (flags & REPO_EXTEND_SOLVABLES)
- {
- s = joinhash_lookup(repo, joinhash, joinhashmask, lastdn);
- if (!s)
- {
- skipentry(&th);
- continue;
- }
- }
- else
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- }
- adddata(data, s, &th);
- }
- finishsolvable(repo, s);
- solv_free(joinhash);
- solv_free(lastdn);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-int
-repo_add_arch_local(Repo *repo, const char *dir, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- DIR *dp;
- struct dirent *de;
- char *entrydir, *file;
- FILE *fp;
- Solvable *s;
-
- data = repo_add_repodata(repo, flags);
-
- if (flags & REPO_USE_ROOTDIR)
- dir = pool_prepend_rootdir(pool, dir);
- dp = opendir(dir);
- if (dp)
- {
- while ((de = readdir(dp)) != 0)
- {
- if (!de->d_name[0] || de->d_name[0] == '.')
- continue;
- entrydir = solv_dupjoin(dir, "/", de->d_name);
- file = pool_tmpjoin(repo->pool, entrydir, "/desc", 0);
- s = 0;
- if ((fp = fopen(file, "r")) != 0)
- {
- struct tarhead th;
- inittarhead(&th, fp);
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- adddata(data, s, &th);
- freetarhead(&th);
- fclose(fp);
- file = pool_tmpjoin(repo->pool, entrydir, "/files", 0);
- if ((fp = fopen(file, "r")) != 0)
- {
- inittarhead(&th, fp);
- adddata(data, s, &th);
- freetarhead(&th);
- fclose(fp);
- }
- }
- solv_free(entrydir);
- }
- closedir(dp);
- }
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- if (flags & REPO_USE_ROOTDIR)
- solv_free((char *)dir);
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define ARCH_ADD_WITH_PKGID (1 << 8)
-
-extern Id repo_add_arch_pkg(Repo *repo, const char *fn, int flags);
-extern Id repo_add_arch_repo(Repo *repo, FILE *fp, int flags);
-extern Id repo_add_arch_local(Repo *repo, const char *dir, int flags);
-
+++ /dev/null
-/*
- * Copyright (c) 2013, SUSE Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define _GNU_SOURCE
-#define _XOPEN_SOURCE
-#include <time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "repo_autopattern.h"
-
-static void
-unescape(char *p)
-{
- char *q = p;
- while (*p)
- {
- if (*p == '%' && p[1] && p[2])
- {
- int d1 = p[1], d2 = p[2];
- if (d1 >= '0' && d1 <= '9')
- d1 -= '0';
- else if (d1 >= 'a' && d1 <= 'f')
- d1 -= 'a' - 10;
- else if (d1 >= 'A' && d1 <= 'F')
- d1 -= 'A' - 10;
- else
- d1 = -1;
- if (d2 >= '0' && d2 <= '9')
- d2 -= '0';
- else if (d2 >= 'a' && d2 <= 'f')
- d2 -= 'a' - 10;
- else if (d2 >= 'A' && d2 <= 'F')
- d2 -= 'A' - 10;
- else
- d2 = -1;
- if (d1 != -1 && d2 != -1)
- {
- *q++ = d1 << 4 | d2;
- p += 3;
- continue;
- }
- }
- *q++ = *p++;
- }
- *q = 0;
-}
-
-static time_t
-datestr2timestamp(const char *date)
-{
- const char *p;
- struct tm tm;
-
- if (!date || !*date)
- return 0;
- for (p = date; *p >= '0' && *p <= '9'; p++)
- ;
- if (!*p)
- return atoi(date);
- memset(&tm, 0, sizeof(tm));
- p = strptime(date, "%F%T", &tm);
- if (!p)
- {
- memset(&tm, 0, sizeof(tm));
- p = strptime(date, "%F", &tm);
- if (!p || *p)
- return 0;
- }
- return timegm(&tm);
-}
-
-int
-repo_add_autopattern(Repo *repo, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data = 0;
- Solvable *s, *s2;
- Queue patq, patq2;
- Queue prdq, prdq2;
- Id p;
- Id pattern_id, product_id;
- Id autopattern_id = 0, autoproduct_id = 0;
- int i, j;
-
- queue_init(&patq);
- queue_init(&patq2);
- queue_init(&prdq);
- queue_init(&prdq2);
-
- if (repo == pool->installed)
- flags |= ADD_NO_AUTOPRODUCTS; /* no auto products for installed repos */
-
- pattern_id = pool_str2id(pool, "pattern()", 9);
- product_id = pool_str2id(pool, "product()", 9);
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- const char *n = pool_id2str(pool, s->name);
- if (*n == 'p')
- {
- if (!strncmp("pattern:", n, 8))
- {
- queue_push(&patq, p);
- continue;
- }
- else if (!strncmp("product:", n, 8))
- {
- queue_push(&prdq, p);
- continue;
- }
- }
- if (s->provides)
- {
- Id prv, *prvp = repo->idarraydata + s->provides;
- while ((prv = *prvp++) != 0) /* go through all provides */
- if (ISRELDEP(prv))
- {
- Reldep *rd = GETRELDEP(pool, prv);
- if (rd->flags != REL_EQ)
- continue;
- if (rd->name == pattern_id)
- {
- const char *evrstr = pool_id2str(pool, rd->evr);
- if (evrstr[0] == '.') /* hack to allow provides that do not create a pattern */
- continue;
- if (patq2.count && patq2.elements[patq2.count - 2] == p)
- {
- /* hmm, two provides. choose by evrstr */
- if (strcmp(evrstr, pool_id2str(pool, patq2.elements[patq2.count - 1])) >= 0)
- continue;
- patq2.count -= 2;
- }
- queue_push2(&patq2, p, rd->evr);
- }
- if (rd->name == product_id)
- {
- const char *evrstr = pool_id2str(pool, rd->evr);
- if (prdq2.count && prdq2.elements[prdq2.count - 2] == p)
- {
- /* hmm, two provides. choose by evrstr */
- if (strcmp(evrstr, pool_id2str(pool, prdq2.elements[prdq2.count - 1])) >= 0)
- continue;
- prdq2.count -= 2;
- }
- queue_push2(&prdq2, p, rd->evr);
- }
- }
- }
- }
- for (i = 0; i < patq2.count; i += 2)
- {
- const char *pn = 0;
- char *newname;
- Id name, prv, *prvp;
- const char *str;
- unsigned long long num;
-
- s = pool->solvables + patq2.elements[i];
- /* construct new name */
- newname = pool_tmpjoin(pool, "pattern:", pool_id2str(pool, patq2.elements[i + 1]), 0);
- unescape(newname);
- name = pool_str2id(pool, newname, 0);
- if (name)
- {
- /* check if we already have that pattern */
- for (j = 0; j < patq.count; j++)
- {
- s2 = pool->solvables + patq.elements[j];
- if (s2->name == name && s2->arch == s->arch && s2->evr == s->evr)
- break;
- }
- if (j < patq.count)
- continue; /* yes, do not add again */
- }
- /* new pattern */
- if (!name)
- name = pool_str2id(pool, newname, 1);
- if (!data)
- {
- repo_internalize(repo); /* to make that the lookups work */
- data = repo_add_repodata(repo, flags);
- }
- s2 = pool_id2solvable(pool, repo_add_solvable(repo));
- s = pool->solvables + patq2.elements[i]; /* re-calc pointer */
- s2->name = name;
- s2->arch = s->arch;
- s2->evr = s->evr;
- s2->vendor = s->vendor;
- /* add link requires */
- s2->requires = repo_addid_dep(repo, s2->requires, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1) , 0);
- /* add autopattern provides */
- if (!autopattern_id)
- autopattern_id = pool_str2id(pool, "autopattern()", 1);
- s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autopattern_id, s->name, REL_EQ, 1), 0);
- /* add self provides */
- s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
- if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
- repodata_set_num(data, s2 - pool->solvables, SOLVABLE_INSTALLTIME, num);
- if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
- repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
- if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
- if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
- /* fill in stuff from provides */
- prvp = repo->idarraydata + s->provides;
- while ((prv = *prvp++) != 0) /* go through all provides */
- {
- Id evr = 0;
- if (ISRELDEP(prv))
- {
- Reldep *rd = GETRELDEP(pool, prv);
- if (rd->flags != REL_EQ)
- continue;
- prv = rd->name;
- evr = rd->evr;
- }
- pn = pool_id2str(pool, prv);
- if (strncmp("pattern-", pn, 8) != 0)
- continue;
- newname = 0;
- if (evr)
- {
- newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
- unescape(newname);
- }
- if (!strncmp(pn, "pattern-category(", 17) && evr)
- {
- char lang[9];
- int l = strlen(pn);
- Id langtag;
- if (l > 17 + 9 || pn[l - 1] != ')')
- continue;
- strncpy(lang, pn + 17, l - 17 - 1);
- lang[l - 17 - 1] = 0;
- langtag = SOLVABLE_CATEGORY;
- if (*lang && strcmp(lang, "en") != 0)
- langtag = pool_id2langid(pool, SOLVABLE_CATEGORY, lang, 1);
- if (newname[solv_validutf8(newname)] == 0)
- repodata_set_str(data, s2 - pool->solvables, langtag, newname);
- else
- {
- char *ustr = solv_latin1toutf8(newname);
- repodata_set_str(data, s2 - pool->solvables, langtag, ustr);
- solv_free(ustr);
- }
- }
- else if (!strcmp(pn, "pattern-includes()") && evr)
- repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_INCLUDES, pool_tmpjoin(pool, "pattern:", newname, 0));
- else if (!strcmp(pn, "pattern-extends()") && evr)
- repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_EXTENDS, pool_tmpjoin(pool, "pattern:", newname, 0));
- else if (!strcmp(pn, "pattern-icon()") && evr)
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ICON, newname);
- else if (!strcmp(pn, "pattern-order()") && evr)
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ORDER, newname);
- else if (!strcmp(pn, "pattern-visible()"))
- {
- if (!evr)
- repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
- else
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE, newname);
- }
- }
- }
- queue_free(&patq);
- queue_free(&patq2);
-
- if ((flags & ADD_NO_AUTOPRODUCTS) != 0)
- queue_empty(&prdq2);
-
- for (i = 0; i < prdq2.count; i += 2)
- {
- const char *pn = 0;
- char *newname;
- Id name, evr = 0, prv, *prvp;
- const char *str;
- unsigned long long num;
-
- s = pool->solvables + prdq2.elements[i];
- /* construct new name */
- newname = pool_tmpjoin(pool, "product(", pool_id2str(pool, prdq2.elements[i + 1]), ")");
- unescape(newname);
- name = pool_str2id(pool, newname, 0);
- if (!name)
- continue; /* must have it in provides! */
- prvp = repo->idarraydata + s->provides;
- while ((prv = *prvp++) != 0) /* go through all provides */
- {
- if (ISRELDEP(prv))
- {
- Reldep *rd = GETRELDEP(pool, prv);
- if (rd->name == name && rd->flags == REL_EQ)
- {
- evr = rd->evr;
- break;
- }
- }
- }
- if (!prv)
- continue; /* not found in provides */
- newname = pool_tmpjoin(pool, "product:", pool_id2str(pool, prdq2.elements[i + 1]), 0);
- unescape(newname);
- name = pool_str2id(pool, newname, 0);
- if (name)
- {
- /* check if we already have that product */
- for (j = 0; j < prdq.count; j++)
- {
- s2 = pool->solvables + prdq.elements[j];
- if (s2->name == name && s2->arch == s->arch && s2->evr == evr)
- break;
- }
- if (j < prdq.count)
- continue; /* yes, do not add again */
- }
- /* new product */
- if (!name)
- name = pool_str2id(pool, newname, 1);
- if (!data)
- {
- repo_internalize(repo); /* to make that the lookups work */
- data = repo_add_repodata(repo, flags);
- }
- if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
- continue; /* eek, not for installed packages, please! */
- s2 = pool_id2solvable(pool, repo_add_solvable(repo));
- s = pool->solvables + prdq2.elements[i]; /* re-calc pointer */
- s2->name = name;
- s2->arch = s->arch;
- s2->evr = evr;
- s2->vendor = s->vendor;
- /* add link requires */
- s2->requires = repo_addid_dep(repo, s2->requires, prv, 0);
- if (!autoproduct_id)
- autoproduct_id = pool_str2id(pool, "autoproduct()", 1);
- s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autoproduct_id, s->name, REL_EQ, 1), 0);
- /* add self provides */
- s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
- if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
- repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
- if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
- if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
- if ((str = solvable_lookup_str(s, SOLVABLE_DISTRIBUTION)) != 0)
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DISTRIBUTION, str);
- /* fill in stuff from provides */
- prvp = repo->idarraydata + s->provides;
- while ((prv = *prvp++) != 0) /* go through all provides */
- {
- Id evr = 0;
- if (ISRELDEP(prv))
- {
- Reldep *rd = GETRELDEP(pool, prv);
- if (rd->flags != REL_EQ)
- continue;
- prv = rd->name;
- evr = rd->evr;
- }
- pn = pool_id2str(pool, prv);
- if (strncmp("product-", pn, 8) != 0)
- continue;
- newname = 0;
- if (evr)
- {
- newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
- unescape(newname);
- }
- if (!strcmp(pn, "product-label()") && evr)
- repodata_set_str(data, s2 - pool->solvables, PRODUCT_SHORTLABEL, newname);
- else if (!strcmp(pn, "product-register-target()") && evr)
- repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_TARGET, newname);
- else if (!strcmp(pn, "product-register-flavor()") && evr)
- repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_FLAVOR, newname);
- else if (!strcmp(pn, "product-type()") && evr)
- repodata_set_str(data, s2 - pool->solvables, PRODUCT_TYPE, newname);
- else if (!strcmp(pn, "product-cpeid()") && evr)
- repodata_set_str(data, s2 - pool->solvables, SOLVABLE_CPEID, newname);
- else if (!strcmp(pn, "product-flags()") && evr)
- repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_FLAGS, newname);
- else if (!strcmp(pn, "product-updates-repoid()") && evr)
- {
- Id h = repodata_new_handle(data);
- repodata_set_str(data, h, PRODUCT_UPDATES_REPOID, newname);
- repodata_add_flexarray(data, s2 - pool->solvables, PRODUCT_UPDATES, h);
- }
- else if (!strcmp(pn, "product-endoflife()") && evr)
- {
- time_t t = datestr2timestamp(newname);
- if (t)
- repodata_set_num(data, s2 - pool->solvables, PRODUCT_ENDOFLIFE, t);
- }
- else if (!strncmp(pn, "product-url(", 12) && evr && pn[12] && pn[13] && strlen(pn + 12) < 32)
- {
- char type[34];
- strcpy(type, pn + 12);
- type[strlen(type) - 1] = 0; /* closing ) */
- repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL_TYPE, type);
- repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL, newname);
- }
- }
- }
- queue_free(&prdq);
- queue_free(&prdq2);
-
- if (data && !(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- else if (!data && !(flags & REPO_NO_INTERNALIZE))
- repo_internalize(repo);
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2013, SUSE Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define ADD_NO_AUTOPRODUCTS (1 << 8)
-
-extern int repo_add_autopattern(Repo *repo, int flags);
+++ /dev/null
-/*
- * repo_comps.c
- *
- * Parses RedHat comps format
- *
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <dirent.h>
-#include <expat.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#define DISABLE_SPLIT
-#include "tools_util.h"
-#include "repo_comps.h"
-
-/*
- * TODO:
- *
- * what's the difference between group/category?
- * handle "default" and "langonly".
- *
- * maybe handle REL_COND in solver recommends handling?
- */
-
-enum state {
- STATE_START,
- STATE_COMPS,
- STATE_GROUP,
- STATE_ID,
- STATE_NAME,
- STATE_DESCRIPTION,
- STATE_DISPLAY_ORDER,
- STATE_DEFAULT,
- STATE_LANGONLY,
- STATE_LANG_ONLY,
- STATE_USERVISIBLE,
- STATE_PACKAGELIST,
- STATE_PACKAGEREQ,
- STATE_CATEGORY,
- STATE_CID,
- STATE_CNAME,
- STATE_CDESCRIPTION,
- STATE_CDISPLAY_ORDER,
- STATE_GROUPLIST,
- STATE_GROUPID,
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-/* must be sorted by first column */
-static struct stateswitch stateswitches[] = {
- { STATE_START, "comps", STATE_COMPS, 0 },
- { STATE_COMPS, "group", STATE_GROUP, 0 },
- { STATE_COMPS, "category", STATE_CATEGORY, 0 },
- { STATE_GROUP, "id", STATE_ID, 1 },
- { STATE_GROUP, "name", STATE_NAME, 1 },
- { STATE_GROUP, "description", STATE_DESCRIPTION, 1 },
- { STATE_GROUP, "uservisible", STATE_USERVISIBLE, 1 },
- { STATE_GROUP, "display_order", STATE_DISPLAY_ORDER, 1 },
- { STATE_GROUP, "default", STATE_DEFAULT, 1 },
- { STATE_GROUP, "langonly", STATE_LANGONLY, 1 },
- { STATE_GROUP, "lang_only", STATE_LANG_ONLY, 1 },
- { STATE_GROUP, "packagelist", STATE_PACKAGELIST, 0 },
- { STATE_PACKAGELIST, "packagereq", STATE_PACKAGEREQ, 1 },
- { STATE_CATEGORY, "id", STATE_CID, 1 },
- { STATE_CATEGORY, "name", STATE_CNAME, 1 },
- { STATE_CATEGORY, "description", STATE_CDESCRIPTION, 1 },
- { STATE_CATEGORY , "grouplist", STATE_GROUPLIST, 0 },
- { STATE_CATEGORY , "display_order", STATE_CDISPLAY_ORDER, 1 },
- { STATE_GROUPLIST, "groupid", STATE_GROUPID, 1 },
- { NUMSTATES }
-};
-
-struct parsedata {
- Pool *pool;
- Repo *repo;
- Repodata *data;
- const char *filename;
- const char *basename;
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
-
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
- struct joindata jd;
-
- const char *tmplang;
- Id reqtype;
- Id condreq;
-
- Solvable *solvable;
- Id handle;
-};
-
-
-/*
- * find_attr
- * find value for xml attribute
- * I: txt, name of attribute
- * I: atts, list of key/value attributes
- * O: pointer to value of matching key, or NULL
- *
- */
-
-static inline const char *
-find_attr(const char *txt, const char **atts)
-{
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, txt))
- return atts[1];
- }
- return 0;
-}
-
-
-/*
- * XML callback: startElement
- */
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- struct stateswitch *sw;
-
-#if 0
- fprintf(stderr, "start: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
-
- pd->depth++;
- if (!pd->swtab[pd->state]) /* no statetable -> no substates */
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#endif
- return;
- }
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
- if (!strcmp(sw->ename, name))
- break;
-
- if (sw->from != pd->state)
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#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_GROUP:
- case STATE_CATEGORY:
- s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
- pd->handle = s - pool->solvables;
- break;
-
- case STATE_NAME:
- case STATE_CNAME:
- case STATE_DESCRIPTION:
- case STATE_CDESCRIPTION:
- pd->tmplang = join_dup(&pd->jd, find_attr("xml:lang", atts));
- break;
-
- case STATE_PACKAGEREQ:
- {
- const char *type = find_attr("type", atts);
- pd->condreq = 0;
- pd->reqtype = SOLVABLE_RECOMMENDS;
- if (type && !strcmp(type, "conditional"))
- {
- const char *requires = find_attr("requires", atts);
- if (requires && *requires)
- pd->condreq = pool_str2id(pool, requires, 1);
- }
- else if (type && !strcmp(type, "mandatory"))
- pd->reqtype = SOLVABLE_REQUIRES;
- else if (type && !strcmp(type, "optional"))
- pd->reqtype = SOLVABLE_SUGGESTS;
- break;
- }
-
- default:
- break;
- }
-}
-
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- Solvable *s = pd->solvable;
- Id id;
-
-#if 0
- fprintf(stderr, "end: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
-#if 0
- fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-#endif
- return;
- }
-
- pd->depth--;
- pd->statedepth--;
-
- switch (pd->state)
- {
- case STATE_GROUP:
- case STATE_CATEGORY:
- if (!s->arch)
- s->arch = ARCH_NOARCH;
- if (!s->evr)
- s->evr = ID_EMPTY;
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
- pd->solvable = 0;
- break;
-
- case STATE_ID:
- case STATE_CID:
- s->name = pool_str2id(pd->pool, join2(&pd->jd, pd->state == STATE_ID ? "group" : "category", ":", pd->content), 1);
- break;
-
- case STATE_NAME:
- case STATE_CNAME:
- repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_SUMMARY, pd->tmplang, 1), pd->content);
- break;
-
- case STATE_DESCRIPTION:
- case STATE_CDESCRIPTION:
- repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_DESCRIPTION, pd->tmplang, 1), pd->content);
- break;
-
- case STATE_PACKAGEREQ:
- id = pool_str2id(pd->pool, pd->content, 1);
- if (pd->condreq)
- id = pool_rel2id(pd->pool, id, pd->condreq, REL_COND, 1);
- repo_add_idarray(pd->repo, pd->handle, pd->reqtype, id);
- break;
-
- case STATE_GROUPID:
- id = pool_str2id(pd->pool, join2(&pd->jd, "group", ":", pd->content), 1);
- s->requires = repo_addid_dep(pd->repo, s->requires, id, 0);
- break;
-
- case STATE_USERVISIBLE:
- repodata_set_void(pd->data, pd->handle, SOLVABLE_ISVISIBLE);
- break;
-
- case STATE_DISPLAY_ORDER:
- case STATE_CDISPLAY_ORDER:
- repodata_set_str(pd->data, pd->handle, SOLVABLE_ORDER, pd->content);
- break;
-
- case STATE_DEFAULT:
- break;
-
- case STATE_LANGONLY:
- case STATE_LANG_ONLY:
- break;
-
- default:
- break;
- }
-
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
-
-#if 0
- fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
-#endif
-}
-
-
-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 = solv_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
-
-
-int
-repo_add_comps(Repo *repo, FILE *fp, int flags)
-{
- Repodata *data;
- struct parsedata pd;
- char buf[BUFF_SIZE];
- int i, l;
- struct stateswitch *sw;
- XML_Parser parser;
-
- data = repo_add_repodata(repo, flags);
-
- memset(&pd, 0, sizeof(pd));
- pd.repo = repo;
- pd.pool = repo->pool;
- pd.data = data;
-
- pd.content = solv_malloc(256);
- pd.acontent = 256;
-
- 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;
- }
-
- 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)
- {
- pool_debug(pd.pool, SOLV_ERROR, "%s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- break;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
-
- solv_free(pd.content);
- join_freemem(&pd.jd);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-/* EOF */
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_comps(Repo *repo, FILE *fp, int flags);
+++ /dev/null
-/*
- * repo_content.c
- *
- * Parses 'content' file into .solv
- * A 'content' file is the repomd.xml of the susetags format
- *
- * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata/content for a description
- * of the syntax
- *
- *
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "chksum.h"
-#include "repo_content.h"
-#define DISABLE_SPLIT
-#define DISABLE_JOIN2
-#include "tools_util.h"
-
-/* split off a word, return null terminated pointer to it.
- * return NULL if there is no word left. */
-static char *
-splitword(char **lp)
-{
- char *w, *l = *lp;
-
- while (*l == ' ' || *l == '\t')
- l++;
- w = *l ? l : 0;
- while (*l && *l != ' ' && *l != '\t')
- l++;
- if (*l)
- *l++ = 0; /* terminate word */
- while (*l == ' ' || *l == '\t')
- l++; /* convenience: advance to next word */
- *lp = l;
- return w;
-}
-
-struct parsedata {
- Repo *repo;
- char *tmp;
- int tmpl;
-
- const char *tmpvers;
- const char *tmprel;
-};
-
-/*
- * dependency relations
- */
-
-static char *flagtab[] = {
- ">",
- "=",
- ">=",
- "<",
- "!=",
- "<="
-};
-
-
-/*
- * join up to three strings into one
- */
-
-static char *
-join(struct parsedata *pd, const char *s1, const char *s2, const 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;
- pd->tmp = solv_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);
- }
- *p = 0;
- return pd->tmp;
-}
-
-
-/*
- * add dependency to pool
- * OBSOLETES product:SUSE_LINUX product:openSUSE < 11.0 package:openSUSE < 11.0
- */
-
-static unsigned int
-adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
-{
- char *name;
- Id id;
-
- while ((name = splitword(&line)) != 0)
- {
- /* Hack, as the content file adds 'package:' for package
- dependencies sometimes. */
- if (!strncmp (name, "package:", 8))
- name += 8;
- id = pool_str2id(pool, name, 1);
- if (*line == '<' || *line == '>' || *line == '=') /* rel follows */
- {
- char *rel = splitword(&line);
- char *evr = splitword(&line);
- int flags;
-
- if (!rel || !evr)
- {
- pool_debug(pool, SOLV_ERROR, "repo_content: bad relation '%s %s'\n", name, rel);
- continue;
- }
- for (flags = 0; flags < 6; flags++)
- if (!strcmp(rel, flagtab[flags]))
- break;
- if (flags == 6)
- {
- pool_debug(pool, SOLV_ERROR, "repo_content: unknown relation '%s'\n", rel);
- continue;
- }
- id = pool_rel2id(pool, id, pool_str2id(pool, evr, 1), flags + 1, 1);
- }
- olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
- }
- return olddeps;
-}
-
-
-/*
- * split value and add to pool
- */
-
-static void
-add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
-{
- char *str;
-
- while ((str = splitword(&value)) != 0)
- repodata_add_poolstr_array(data, handle, name, str);
-}
-
-/*
- * split value and add to pool
- */
-
-static void
-add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
-{
- char *url;
-
- while ((url = splitword(&value)) != 0)
- {
- repodata_add_poolstr_array(data, handle, PRODUCT_URL, url);
- repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
- }
-}
-
-
-
-/*
- * add 'content' to repo
- *
- */
-
-int
-repo_add_content(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- char *line, *linep;
- int aline;
- Solvable *s;
- struct parsedata pd;
- Repodata *data;
- Id handle = 0;
- int contentstyle = 0;
- char *descrdir = 0;
- char *datadir = 0;
- char *defvendor = 0;
-
- int i = 0;
- int res = 0;
-
- /* architectures
- we use the first architecture in BASEARCHS or noarch
- for the product. At the end we create (clone) the product
- for each one of the remaining architectures
- we allow max 4 archs
- */
- unsigned int numotherarchs = 0;
- Id *otherarchs = 0;
-
- memset(&pd, 0, sizeof(pd));
- line = solv_malloc(1024);
- aline = 1024;
-
- pd.repo = repo;
- linep = line;
- s = 0;
-
- data = repo_add_repodata(repo, flags);
-
- for (;;)
- {
- char *key, *value;
-
- /* read line into big-enough buffer */
- if (linep - line + 16 > aline)
- {
- aline = linep - line;
- line = solv_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;
- while ( --linep > line && ( linep[-1] == ' ' || linep[-1] == '\t' ) )
- ; /* skip trailing ws */
- *linep = 0;
- linep = line;
-
- /* expect "key value" lines */
- value = line;
- key = splitword(&value);
-
- if (key)
- {
-#if 0
- fprintf (stderr, "key %s, value %s\n", key, value);
-#endif
-
-#define istag(x) (!strcmp (key, x))
-#define code10 (contentstyle == 10)
-#define code11 (contentstyle == 11)
-
-
- if (istag ("CONTENTSTYLE"))
- {
- if (contentstyle)
- pool_debug(pool, SOLV_ERROR, "repo_content: 'CONTENTSTYLE' must be first line of 'content'\n");
- contentstyle = atoi(value);
- continue;
- }
- if (!contentstyle)
- contentstyle = 10;
-
- /* repository tags */
- /* we also replicate some of them into the product solvables
- * to be backward compatible */
-
- if (istag ("REPOID"))
- {
- repodata_add_poolstr_array(data, SOLVID_META, REPOSITORY_REPOID, value);
- continue;
- }
- if (istag ("REPOKEYWORDS"))
- {
- add_multiple_strings(data, SOLVID_META, REPOSITORY_KEYWORDS, value);
- continue;
- }
- if (istag ("DISTRO"))
- {
- Id dh = repodata_new_handle(data);
- char *p;
- /* like with createrepo --distro */
- if ((p = strchr(value, ',')) != 0)
- {
- *p++ = 0;
- if (*value)
- repodata_set_poolstr(data, dh, REPOSITORY_PRODUCT_CPEID, value);
- }
- else
- p = value;
- if (*p)
- repodata_set_str(data, dh, REPOSITORY_PRODUCT_LABEL, p);
- repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DISTROS, dh);
- continue;
- }
-
- if (istag ("DESCRDIR"))
- {
- if (descrdir)
- free(descrdir);
- else
- repodata_set_str(data, SOLVID_META, SUSETAGS_DESCRDIR, value);
- if (s)
- repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, value);
- descrdir = solv_strdup(value);
- continue;
- }
- if (istag ("DATADIR"))
- {
- if (datadir)
- free(datadir);
- else
- repodata_set_str(data, SOLVID_META, SUSETAGS_DATADIR, value);
- if (s)
- repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, value);
- datadir = solv_strdup(value);
- continue;
- }
- if (istag ("VENDOR"))
- {
- if (defvendor)
- free(defvendor);
- else
- repodata_set_poolstr(data, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
- if (s)
- s->vendor = pool_str2id(pool, value, 1);
- defvendor = solv_strdup(value);
- continue;
- }
-
- if (istag ("META") || istag ("HASH") || istag ("KEY"))
- {
- char *checksumtype, *checksum;
- Id fh, type;
- int l;
-
- if ((checksumtype = splitword(&value)) == 0)
- continue;
- if ((checksum = splitword(&value)) == 0)
- continue;
- if (!*value)
- continue;
- type = solv_chksum_str2type(checksumtype);
- if (!type)
- {
- pool_error(pool, -1, "%s: unknown checksum type '%s'", value, checksumtype);
- res = 1;
- continue;
- }
- l = solv_chksum_len(type);
- if (strlen(checksum) != 2 * l)
- {
- pool_error(pool, -1, "%s: invalid checksum length for %s", value, checksumtype);
- res = 1;
- continue;
- }
- fh = repodata_new_handle(data);
- repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
- repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
- repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
- repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
- continue;
- }
-
- /* product tags */
-
- if ((code10 && istag ("PRODUCT"))
- || (code11 && istag ("NAME")))
- {
- if (s && !s->name)
- {
- /* this solvable was created without seeing a
- PRODUCT entry, just set the name and continue */
- s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
- continue;
- }
- if (s)
- {
- /* finish old solvable */
- if (!s->arch)
- s->arch = ARCH_NOARCH;
- if (!s->evr)
- s->evr = ID_EMPTY;
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- if (code10)
- s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
- }
- /* create new solvable */
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- handle = s - pool->solvables;
- s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
- if (datadir)
- repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, datadir);
- if (descrdir)
- repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
- if (defvendor)
- s->vendor = pool_str2id(pool, defvendor, 1);
- continue;
- }
-
- /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
- from here on. */
- if (!s)
- {
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- handle = s - pool->solvables;
- }
-
- if (istag ("VERSION"))
- pd.tmpvers = solv_strdup(value);
- else if (istag ("RELEASE"))
- pd.tmprel = solv_strdup(value);
- else if (code11 && istag ("DISTRIBUTION"))
- repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
- else if (istag ("UPDATEURLS"))
- add_multiple_urls(data, handle, value, pool_str2id(pool, "update", 1));
- else if (istag ("EXTRAURLS"))
- add_multiple_urls(data, handle, value, pool_str2id(pool, "extra", 1));
- else if (istag ("OPTIONALURLS"))
- add_multiple_urls(data, handle, value, pool_str2id(pool, "optional", 1));
- else if (istag ("RELNOTESURL"))
- add_multiple_urls(data, handle, value, pool_str2id(pool, "releasenotes", 1));
- else if (istag ("SHORTLABEL"))
- repodata_set_str(data, s - pool->solvables, PRODUCT_SHORTLABEL, value);
- else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
- repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, value);
- else if (!strncmp (key, "LABEL.", 6))
- repodata_set_str(data, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
- else if (istag ("FLAGS"))
- add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
- else if (istag ("VENDOR")) /* actually already handled above */
- s->vendor = pool_str2id(pool, value, 1);
- else if (istag ("BASEARCHS"))
- {
- char *arch;
-
- if ((arch = splitword(&value)) != 0)
- {
- s->arch = pool_str2id(pool, arch, 1);
- while ((arch = splitword(&value)) != 0)
- {
- otherarchs = solv_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
- otherarchs[numotherarchs++] = pool_str2id(pool, arch, 1);
- }
- }
- }
- if (!code10)
- continue;
-
- /*
- * Every tag below is Code10 only
- *
- */
-
- if (istag ("ARCH"))
- /* Theoretically we want to have the best arch of the given
- modifiers which still is compatible with the system
- arch. We don't know the latter here, though. */
- s->arch = ARCH_NOARCH;
- else if (istag ("PREREQUIRES"))
- s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
- else if (istag ("REQUIRES"))
- s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
- else if (istag ("PROVIDES"))
- s->provides = adddep(pool, &pd, s->provides, value, 0);
- else if (istag ("CONFLICTS"))
- s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
- else if (istag ("OBSOLETES"))
- s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
- else if (istag ("RECOMMENDS"))
- s->recommends = adddep(pool, &pd, s->recommends, value, 0);
- else if (istag ("SUGGESTS"))
- s->suggests = adddep(pool, &pd, s->suggests, value, 0);
- else if (istag ("SUPPLEMENTS"))
- s->supplements = adddep(pool, &pd, s->supplements, value, 0);
- else if (istag ("ENHANCES"))
- s->enhances = adddep(pool, &pd, s->enhances, value, 0);
- /* FRESHENS doesn't seem to exist. */
- else if (istag ("TYPE"))
- repodata_set_str(data, s - pool->solvables, PRODUCT_TYPE, value);
-
- /* XXX do something about LINGUAS and ARCH?
- * <ma>: Don't think so. zypp does not use or propagate them.
- */
-#undef istag
- }
- else
- pool_debug(pool, SOLV_ERROR, "repo_content: malformed line: %s\n", line);
- }
-
- if (datadir)
- free(datadir);
- if (descrdir)
- free(descrdir);
- if (defvendor)
- free(defvendor);
-
- if (s && !s->name)
- {
- pool_debug(pool, SOLV_ERROR, "repo_content: 'content' incomplete, no product solvable created!\n");
- repo_free_solvable(repo, s - pool->solvables, 1);
- s = 0;
- }
- if (s)
- {
- if (pd.tmprel)
- s->evr = makeevr(pool, join(&pd, pd.tmpvers, "-", pd.tmprel));
- else
- s->evr = makeevr(pool, pd.tmpvers);
- pd.tmpvers = solv_free((void *)pd.tmpvers);
- pd.tmprel = solv_free((void *)pd.tmprel);
-
- if (!s->arch)
- s->arch = ARCH_NOARCH;
- if (!s->evr)
- s->evr = ID_EMPTY;
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- if (code10)
- s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
-
- /* now for every other arch, clone the product except the architecture */
- for (i = 0; i < numotherarchs; ++i)
- {
- Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
- p->name = s->name;
- p->evr = s->evr;
- p->vendor = s->vendor;
- p->arch = otherarchs[i];
-
- /* self provides */
- if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
- p->provides = repo_addid_dep(repo, p->provides, pool_rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
-
- /* now merge the attributes */
- repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
- }
- }
-
- if (pd.tmp)
- solv_free(pd.tmp);
- solv_free(line);
- solv_free(otherarchs);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return res;
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_content(Repo *repo, FILE *fp, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <zlib.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "chksum.h"
-#include "solver.h"
-#include "repo_cudf.h"
-
-static Id
-parseonedep(Pool *pool, char *p)
-{
- char *n, *ne, *e, *ee;
- Id name, evr;
- int flags;
-
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- if (!*p)
- return 0;
- if (!strcmp(p, "!true"))
- return 0;
- if (!strcmp(p, "!false"))
- return pool_str2id(pool, p, 1);
- n = p;
- /* find end of name */
- while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '|')
- p++;
- ne = p;
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- evr = 0;
- flags = 0;
- e = ee = 0;
- if (*p == '>' || *p == '<' || *p == '=' || *p == '!')
- {
- if (*p == '>')
- flags |= REL_GT;
- else if (*p == '=')
- flags |= REL_EQ;
- else if (*p == '<')
- flags |= REL_LT;
- else if (*p == '!')
- flags |= REL_LT | REL_GT | REL_EQ;
- p++;
- if (flags && *p == '=')
- {
- if (p[-1] != '=')
- flags ^= REL_EQ;
- p++;
- }
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- e = p;
- while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '|')
- p++;
- ee = p;
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- }
- name = pool_strn2id(pool, n, ne - n, 1);
- if (e)
- {
- evr = pool_strn2id(pool, e, ee - e, 1);
- name = pool_rel2id(pool, name, evr, flags, 1);
- }
- if (*p == '|')
- {
- Id id = parseonedep(pool, p + 1);
- if (id)
- name = pool_rel2id(pool, name, id, REL_OR, 1);
- }
- return name;
-}
-static unsigned int
-makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
-{
- Pool *pool = repo->pool;
- char *p;
- Id id;
-
- while ((p = strchr(deps, ',')) != 0)
- {
- *p = 0;
- olddeps = makedeps(repo, deps, olddeps, marker);
- *p = ',';
- deps = p + 1;
- }
- id = parseonedep(pool, deps);
- if (!id)
- return olddeps;
- return repo_addid_dep(repo, olddeps, id, marker);
-}
-
-static Offset
-copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
-{
- Id *ida, *from;
- int cc;
- Offset off;
-
- if (!fromoff)
- return 0;
- from = fromrepo->idarraydata + fromoff;
- for (ida = from, cc = 0; *ida; ida++, cc++)
- ;
- if (cc == 0)
- return 0;
- off = repo_reserve_ids(repo, 0, cc);
- memcpy(repo->idarraydata + off, from, (cc + 1) * sizeof(Id));
- repo->idarraysize += cc + 1;
- return off;
-}
-
-static void
-copysolvabledata(Pool *pool, Solvable *s, Repo *repo)
-{
- Repo *srepo = s->repo;
- if (srepo == repo)
- return;
- s->provides = copydeps(pool, repo, s->provides, srepo);
- s->requires = copydeps(pool, repo, s->requires, srepo);
- s->conflicts = copydeps(pool, repo, s->conflicts, srepo);
- s->obsoletes = copydeps(pool, repo, s->obsoletes, srepo);
- s->recommends = copydeps(pool, repo, s->recommends, srepo);
- s->suggests = copydeps(pool, repo, s->suggests, srepo);
- s->supplements = copydeps(pool, repo, s->supplements, srepo);
- s->enhances = copydeps(pool, repo, s->enhances, srepo);
-}
-
-#define KEEP_VERSION 1
-#define KEEP_PACKAGE 2
-#define KEEP_FEATURE 3
-
-static void
-finishpackage(Pool *pool, Solvable *s, int keep, Queue *job)
-{
- Id *idp, id, sid;
- if (!s)
- return;
- if (!s->arch)
- s->arch = ARCH_ANY;
- if (!s->evr)
- s->evr = ID_EMPTY;
- sid = pool_rel2id(pool, s->name, s->evr, REL_EQ, 1);
- s->provides = repo_addid_dep(s->repo, s->provides, sid, 0);
- if (!job || !pool->installed || s->repo != pool->installed)
- return;
- if (keep == KEEP_VERSION)
- queue_push2(job, SOLVER_INSTALL|SOLVER_SOLVABLE_NAME, sid);
- else if (keep == KEEP_PACKAGE)
- queue_push2(job, SOLVER_INSTALL|SOLVER_SOLVABLE_NAME, s->name);
- else if (keep == KEEP_FEATURE)
- {
- for (idp = s->repo->idarraydata + s->provides; (id = *idp) != 0; idp++)
- {
- if (id != sid) /* skip self-provides */
- queue_push2(job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, id);
- }
- }
-}
-
-int
-repo_add_cudf(Repo *repo, Repo *installedrepo, FILE *fp, Queue *job, int flags)
-{
- Pool *pool;
- char *buf, *p;
- int bufa, bufl, c;
- Solvable *s;
- int instanza = 0;
- int inrequest = 0;
- int isinstalled = 0;
- int keep = 0;
- Repo *xrepo;
-
- xrepo = repo ? repo : installedrepo;
- if (!xrepo)
- return -1;
- pool = xrepo->pool;
-
- buf = solv_malloc(4096);
- bufa = 4096;
- bufl = 0;
- s = 0;
-
- while (fgets(buf + bufl, bufa - bufl, fp) > 0)
- {
- bufl += strlen(buf + bufl);
- if (bufl && buf[bufl - 1] != '\n')
- {
- if (bufa - bufl < 256)
- {
- bufa += 4096;
- buf = solv_realloc(buf, bufa);
- }
- continue;
- }
- buf[--bufl] = 0;
- c = getc(fp);
- if (c == ' ' || c == '\t')
- {
- /* continuation line */
- buf[bufl++] = ' ';
- continue;
- }
- if (c != EOF)
- ungetc(c, fp);
- bufl = 0;
- if (*buf == '#')
- continue;
- if (!*buf)
- {
- if (s && !repo && !isinstalled)
- {
- repo_free_solvable(repo, s - pool->solvables, 1);
- s = 0;
- }
- if (s)
- finishpackage(pool, s, keep, job);
- s = 0;
- keep = 0;
- instanza = 0;
- inrequest = 0;
- continue;
- }
- p = strchr(buf, ':');
- if (!p)
- continue; /* hmm */
- *p++ = 0;
- while (*p == ' ' || *p == '\t')
- p++;
- if (!instanza)
- {
- instanza = 1;
- inrequest = 0;
- if (!strcmp(buf, "request"))
- {
- inrequest = 1;
- continue;
- }
- if (!strcmp(buf, "package"))
- {
- s = pool_id2solvable(pool, repo_add_solvable(xrepo));
- isinstalled = 0;
- keep = 0;
- }
- }
- if (inrequest)
- {
- if (!job)
- continue;
- if (!strcmp(buf, "install"))
- {
- Id id, *idp;
- Offset off = makedeps(xrepo, p, 0, 0);
- for (idp = xrepo->idarraydata + off; (id = *idp) != 0; idp++)
- queue_push2(job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, id);
- }
- else if (!strcmp(buf, "remove"))
- {
- Id id, *idp;
- Offset off = makedeps(xrepo, p, 0, 0);
- for (idp = xrepo->idarraydata + off; (id = *idp) != 0; idp++)
- queue_push2(job, SOLVER_ERASE|SOLVER_SOLVABLE_PROVIDES, id);
- }
- else if (!strcmp(buf, "upgrade"))
- {
- Id id, *idp;
- Offset off = makedeps(xrepo, p, 0, 0);
- for (idp = xrepo->idarraydata + off; (id = *idp) != 0; idp++)
- queue_push2(job, SOLVER_INSTALL|SOLVER_ORUPDATE|SOLVER_SOLVABLE_PROVIDES, id);
- }
- continue;
- }
- if (!s)
- continue; /* we ignore the preamble for now */
- switch (buf[0])
- {
- case 'c':
- if (!strcmp(buf, "conflicts"))
- {
- s->conflicts = makedeps(s->repo, p, s->conflicts, 0);
- continue;
- }
- case 'd':
- if (!strcmp(buf, "depends"))
- {
- s->requires = makedeps(s->repo, p, s->requires, 0);
- continue;
- }
- break;
- case 'k':
- if (!strcmp(buf, "keep"))
- {
- if (!job)
- continue;
- if (!strcmp(p, "version"))
- keep = KEEP_VERSION;
- else if (!strcmp(p, "package"))
- keep = KEEP_PACKAGE;
- else if (!strcmp(p, "feature"))
- keep = KEEP_FEATURE;
- continue;
- }
- break;
- case 'i':
- if (!strcmp(buf, "installed"))
- {
- if (!strcmp(p, "true"))
- {
- isinstalled = 1;
- if (!installedrepo)
- {
- repo_free_solvable(repo, s - pool->solvables, 1);
- s = 0;
- }
- else if (s->repo != installedrepo)
- {
- copysolvabledata(pool, s, installedrepo);
- s->repo->nsolvables--;
- s->repo = installedrepo;
- if (s - pool->solvables < s->repo->start)
- s->repo->start = s - pool->solvables;
- if (s - pool->solvables >= s->repo->end)
- s->repo->end = s - pool->solvables + 1;
- s->repo->nsolvables++;
- }
- }
- continue;
- }
- break;
- case 'p':
- if (!strcmp(buf, "package"))
- {
- s->name = pool_str2id(pool, p, 1);
- continue;
- }
- if (!strcmp(buf, "provides"))
- {
- s->provides = makedeps(s->repo, p, s->provides, 0);
- continue;
- }
- break;
- case 'r':
- if (!strcmp(buf, "depends"))
- {
- s->recommends = makedeps(s->repo, p, s->recommends, 0);
- continue;
- }
- break;
- case 'v':
- if (!strcmp(buf, "version"))
- {
- s->evr = pool_str2id(pool, p, 1);
- continue;
- }
- break;
- }
- }
- if (s && !repo && !isinstalled)
- {
- repo_free_solvable(repo, s - pool->solvables, 1);
- s = 0;
- }
- if (s)
- finishpackage(pool, s, keep, job);
- solv_free(buf);
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_cudf(Repo *repo, Repo *installedrepo, FILE *fp, Queue *job, int flags);
-
+++ /dev/null
-/*
- * Copyright (c) 2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <zlib.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "solver.h" /* for GET_USERINSTALLED_ flags */
-#include "chksum.h"
-#include "repo_deb.h"
-
-static unsigned char *
-decompress(unsigned char *in, int inl, int *outlp)
-{
- z_stream strm;
- int outl, ret;
- unsigned char *out;
-
- memset(&strm, 0, sizeof(strm));
- strm.next_in = in;
- strm.avail_in = inl;
- out = solv_malloc(4096);
- strm.next_out = out;
- strm.avail_out = 4096;
- outl = 0;
- ret = inflateInit2(&strm, -MAX_WBITS);
- if (ret != Z_OK)
- {
- free(out);
- return 0;
- }
- for (;;)
- {
- if (strm.avail_out == 0)
- {
- outl += 4096;
- out = solv_realloc(out, outl + 4096);
- strm.next_out = out + outl;
- strm.avail_out = 4096;
- }
- ret = inflate(&strm, Z_NO_FLUSH);
- if (ret == Z_STREAM_END)
- break;
- if (ret != Z_OK)
- {
- free(out);
- return 0;
- }
- }
- outl += 4096 - strm.avail_out;
- inflateEnd(&strm);
- *outlp = outl;
- return out;
-}
-
-static Id
-parseonedep(Pool *pool, char *p)
-{
- char *n, *ne, *e, *ee;
- Id name, evr;
- int flags;
-
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- if (!*p || *p == '(')
- return 0;
- n = p;
- /* find end of name */
- while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '(' && *p != '|')
- p++;
- ne = p;
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- evr = 0;
- flags = 0;
- e = ee = 0;
- if (*p == '(')
- {
- p++;
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- if (*p == '>')
- flags |= REL_GT;
- else if (*p == '=')
- flags |= REL_EQ;
- else if (*p == '<')
- flags |= REL_LT;
- if (flags)
- {
- p++;
- if (*p == '>')
- flags |= REL_GT;
- else if (*p == '=')
- flags |= REL_EQ;
- else if (*p == '<')
- flags |= REL_LT;
- else
- p--;
- p++;
- }
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- e = p;
- while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != ')')
- p++;
- ee = p;
- while (*p && *p != ')')
- p++;
- if (*p)
- p++;
- while (*p == ' ' || *p == '\t' || *p == '\n')
- p++;
- }
- if (ne - n > 4 && ne[-4] == ':' && !strncmp(ne - 4, ":any", 4))
- {
- /* multiarch annotation */
- name = pool_strn2id(pool, n, ne - n - 4, 1);
- name = pool_rel2id(pool, name, ARCH_ANY, REL_MULTIARCH, 1);
- }
- else
- name = pool_strn2id(pool, n, ne - n, 1);
- if (e)
- {
- evr = pool_strn2id(pool, e, ee - e, 1);
- name = pool_rel2id(pool, name, evr, flags, 1);
- }
- if (*p == '|')
- {
- Id id = parseonedep(pool, p + 1);
- if (id)
- name = pool_rel2id(pool, name, id, REL_OR, 1);
- }
- return name;
-}
-
-static unsigned int
-makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
-{
- Pool *pool = repo->pool;
- char *p;
- Id id;
-
- while ((p = strchr(deps, ',')) != 0)
- {
- *p = 0;
- olddeps = makedeps(repo, deps, olddeps, marker);
- *p = ',';
- deps = p + 1;
- }
- id = parseonedep(pool, deps);
- if (!id)
- return olddeps;
- return repo_addid_dep(repo, olddeps, id, marker);
-}
-
-
-/* put data from control file into the solvable */
-/* warning: does inplace changes */
-static void
-control2solvable(Solvable *s, Repodata *data, char *control)
-{
- Repo *repo = s->repo;
- Pool *pool = repo->pool;
- char *p, *q, *end, *tag;
- int x, l;
- int havesource = 0;
- char checksum[32 * 2 + 1];
- Id checksumtype = 0;
- Id newtype;
-
- p = control;
- while (*p)
- {
- p = strchr(p, '\n');
- if (!p)
- break;
- if (p[1] == ' ' || p[1] == '\t')
- {
- char *q;
- /* continuation line */
- q = p - 1;
- while (q >= control && *q == ' ' && *q == '\t')
- q--;
- l = q + 1 - control;
- if (l)
- memmove(p + 1 - l, control, l);
- control = p + 1 - l;
- p[1] = '\n';
- p += 2;
- continue;
- }
- end = p - 1;
- if (*p)
- *p++ = 0;
- /* strip trailing space */
- while (end >= control && (*end == ' ' || *end == '\t'))
- *end-- = 0;
- tag = control;
- control = p;
- q = strchr(tag, ':');
- if (!q || q - tag < 4)
- continue;
- *q++ = 0;
- while (*q == ' ' || *q == '\t')
- q++;
- x = '@' + (tag[0] & 0x1f);
- x = (x << 8) + '@' + (tag[1] & 0x1f);
- switch(x)
- {
- case 'A' << 8 | 'R':
- if (!strcasecmp(tag, "architecture"))
- s->arch = pool_str2id(pool, q, 1);
- break;
- case 'B' << 8 | 'R':
- if (!strcasecmp(tag, "breaks"))
- s->conflicts = makedeps(repo, q, s->conflicts, 0);
- break;
- case 'C' << 8 | 'O':
- if (!strcasecmp(tag, "conflicts"))
- s->conflicts = makedeps(repo, q, s->conflicts, 0);
- break;
- case 'D' << 8 | 'E':
- if (!strcasecmp(tag, "depends"))
- s->requires = makedeps(repo, q, s->requires, -SOLVABLE_PREREQMARKER);
- else if (!strcasecmp(tag, "description"))
- {
- char *ld = strchr(q, '\n');
- if (ld)
- {
- *ld++ = 0;
- repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, ld);
- }
- else
- repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, q);
- repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, q);
- }
- break;
- case 'E' << 8 | 'N':
- if (!strcasecmp(tag, "enhances"))
- s->enhances = makedeps(repo, q, s->enhances, 0);
- break;
- case 'F' << 8 | 'I':
- if (!strcasecmp(tag, "filename"))
- repodata_set_location(data, s - pool->solvables, 0, 0, q);
- break;
- case 'H' << 8 | 'O':
- if (!strcasecmp(tag, "homepage"))
- repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, q);
- break;
- case 'I' << 8 | 'N':
- if (!strcasecmp(tag, "installed-size"))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(q, 0, 10) << 10);
- break;
- case 'M' << 8 | 'D':
- if (!strcasecmp(tag, "md5sum") && !checksumtype && strlen(q) == 16 * 2)
- {
- strcpy(checksum, q);
- checksumtype = REPOKEY_TYPE_MD5;
- }
- break;
- case 'P' << 8 | 'A':
- if (!strcasecmp(tag, "package"))
- s->name = pool_str2id(pool, q, 1);
- break;
- case 'P' << 8 | 'R':
- if (!strcasecmp(tag, "pre-depends"))
- s->requires = makedeps(repo, q, s->requires, SOLVABLE_PREREQMARKER);
- else if (!strcasecmp(tag, "provides"))
- s->provides = makedeps(repo, q, s->provides, 0);
- break;
- case 'R' << 8 | 'E':
- if (!strcasecmp(tag, "replaces"))
- s->obsoletes = makedeps(repo, q, s->obsoletes, 0);
- else if (!strcasecmp(tag, "recommends"))
- s->recommends = makedeps(repo, q, s->recommends, 0);
- break;
- case 'S' << 8 | 'H':
- newtype = solv_chksum_str2type(tag);
- if (!newtype || solv_chksum_len(newtype) * 2 != strlen(q))
- break;
- if (!checksumtype || (newtype == REPOKEY_TYPE_SHA1 && checksumtype != REPOKEY_TYPE_SHA256) || newtype == REPOKEY_TYPE_SHA256)
- {
- strcpy(checksum, q);
- checksumtype = newtype;
- }
- break;
- case 'S' << 8 | 'O':
- if (!strcasecmp(tag, "source"))
- {
- char *q2;
- /* ignore version for now */
- for (q2 = q; *q2; q2++)
- if (*q2 == ' ' || *q2 == '\t')
- {
- *q2 = 0;
- break;
- }
- if (s->name && !strcmp(q, pool_id2str(pool, s->name)))
- repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
- else
- repodata_set_id(data, s - pool->solvables, SOLVABLE_SOURCENAME, pool_str2id(pool, q, 1));
- havesource = 1;
- }
- break;
- case 'S' << 8 | 'T':
- if (!strcasecmp(tag, "status"))
- repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_INSTALLSTATUS, q);
- break;
- case 'S' << 8 | 'U':
- if (!strcasecmp(tag, "suggests"))
- s->suggests = makedeps(repo, q, s->suggests, 0);
- break;
- case 'V' << 8 | 'E':
- if (!strcasecmp(tag, "version"))
- s->evr = pool_str2id(pool, q, 1);
- break;
- }
- }
- if (checksumtype)
- repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, checksumtype, checksum);
- if (!s->arch)
- s->arch = ARCH_ALL;
- if (!s->evr)
- s->evr = ID_EMPTY;
- if (s->name)
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- if (s->name && !havesource)
- repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
- if (s->obsoletes)
- {
- /* obsoletes only count when the packages also conflict */
- /* XXX: should not transcode here */
- int i, j, k;
- Id d, cid;
- for (i = j = s->obsoletes; (d = repo->idarraydata[i]) != 0; i++)
- {
- if (!s->conflicts)
- continue;
- for (k = s->conflicts; (cid = repo->idarraydata[k]) != 0; k++)
- {
- if (repo->idarraydata[k] == cid)
- break;
- if (ISRELDEP(cid))
- {
- Reldep *rd = GETRELDEP(pool, cid);
- if (rd->flags < 8 && rd->name == d)
- break; /* specialize obsoletes */
- }
- }
- if (cid)
- repo->idarraydata[j++] = cid;
- }
- repo->idarraydata[j] = 0;
- if (j == s->obsoletes)
- s->obsoletes = 0;
- }
-}
-
-int
-repo_add_debpackages(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- char *buf, *p;
- int bufl, l, ll;
- Solvable *s;
-
- data = repo_add_repodata(repo, flags);
- buf = solv_malloc(4096);
- bufl = 4096;
- l = 0;
- buf[l] = 0;
- p = buf;
- for (;;)
- {
- if (!(p = strchr(p, '\n')))
- {
- int l3;
- if (l + 1024 >= bufl)
- {
- buf = solv_realloc(buf, bufl + 4096);
- bufl += 4096;
- p = buf + l;
- continue;
- }
- p = buf + l;
- ll = fread(p, 1, bufl - l - 1, fp);
- if (ll <= 0)
- break;
- p[ll] = 0;
- while ((l3 = strlen(p)) < ll)
- p[l3] = '\n';
- l += ll;
- continue;
- }
- p++;
- if (*p != '\n')
- continue;
- *p = 0;
- ll = p - buf + 1;
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- control2solvable(s, data, buf);
- if (!s->name)
- repo_free_solvable(repo, s - pool->solvables, 1);
- if (l > ll)
- memmove(buf, p + 1, l - ll);
- l -= ll;
- p = buf;
- buf[l] = 0;
- }
- if (l)
- {
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- control2solvable(s, data, buf);
- if (!s->name)
- repo_free_solvable(repo, s - pool->solvables, 1);
- }
- solv_free(buf);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-int
-repo_add_debdb(Repo *repo, int flags)
-{
- FILE *fp;
- const char *path = "/var/lib/dpkg/status";
- if (flags & REPO_USE_ROOTDIR)
- path = pool_prepend_rootdir_tmp(repo->pool, path);
- if ((fp = fopen(path, "r")) == 0)
- return pool_error(repo->pool, -1, "%s: %s", path, strerror(errno));
- repo_add_debpackages(repo, fp, flags);
- fclose(fp);
- return 0;
-}
-
-Id
-repo_add_deb(Repo *repo, const char *deb, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- unsigned char buf[4096], *bp;
- int l, l2, vlen, clen, ctarlen;
- unsigned char *ctgz;
- unsigned char pkgid[16];
- unsigned char *ctar;
- int gotpkgid;
- FILE *fp;
- Solvable *s;
- struct stat stb;
-
- data = repo_add_repodata(repo, flags);
- if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, deb) : deb, "r")) == 0)
- {
- pool_error(pool, -1, "%s: %s", deb, strerror(errno));
- return 0;
- }
- if (fstat(fileno(fp), &stb))
- {
- pool_error(pool, -1, "fstat: %s", strerror(errno));
- fclose(fp);
- return 0;
- }
- l = fread(buf, 1, sizeof(buf), fp);
- if (l < 8 + 60 || (strncmp((char *)buf, "!<arch>\ndebian-binary ", 8 + 16) != 0 && strncmp((char *)buf, "!<arch>\ndebian-binary/ ", 8 + 16) != 0))
- {
- pool_error(pool, -1, "%s: not a deb package", deb);
- fclose(fp);
- return 0;
- }
- vlen = atoi((char *)buf + 8 + 48);
- if (vlen < 0 || vlen > l)
- {
- pool_error(pool, -1, "%s: not a deb package", deb);
- fclose(fp);
- return 0;
- }
- vlen += vlen & 1;
- if (l < 8 + 60 + vlen + 60)
- {
- pool_error(pool, -1, "%s: unhandled deb package", deb);
- fclose(fp);
- return 0;
- }
- if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz ", 16) != 0 && strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz/ ", 16) != 0)
- {
- pool_error(pool, -1, "%s: control.tar.gz is not second entry", deb);
- fclose(fp);
- return 0;
- }
- clen = atoi((char *)buf + 8 + 60 + vlen + 48);
- if (clen <= 0 || clen >= 0x100000)
- {
- pool_error(pool, -1, "%s: control.tar.gz has illegal size", deb);
- fclose(fp);
- return 0;
- }
- ctgz = solv_calloc(1, clen + 4);
- bp = buf + 8 + 60 + vlen + 60;
- l -= 8 + 60 + vlen + 60;
- if (l > clen)
- l = clen;
- if (l)
- memcpy(ctgz, bp, l);
- if (l < clen)
- {
- if (fread(ctgz + l, clen - l, 1, fp) != 1)
- {
- pool_error(pool, -1, "%s: unexpected EOF", deb);
- solv_free(ctgz);
- fclose(fp);
- return 0;
- }
- }
- fclose(fp);
- gotpkgid = 0;
- if (flags & DEBS_ADD_WITH_PKGID)
- {
- Chksum *chk = solv_chksum_create(REPOKEY_TYPE_MD5);
- solv_chksum_add(chk, ctgz, clen);
- solv_chksum_free(chk, pkgid);
- gotpkgid = 1;
- }
- if (ctgz[0] != 0x1f || ctgz[1] != 0x8b)
- {
- pool_error(pool, -1, "%s: control.tar.gz is not gzipped", deb);
- solv_free(ctgz);
- return 0;
- }
- if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0)
- {
- pool_error(pool, -1, "%s: control.tar.gz is compressed in a strange way", deb);
- solv_free(ctgz);
- return 0;
- }
- bp = ctgz + 4;
- bp += 6; /* skip time, xflags and OS code */
- if (ctgz[3] & 0x04)
- {
- /* skip extra field */
- l = bp[0] | bp[1] << 8;
- bp += l + 2;
- if (bp >= ctgz + clen)
- {
- pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
- solv_free(ctgz);
- return 0;
- }
- }
- if (ctgz[3] & 0x08) /* orig filename */
- while (*bp)
- bp++;
- if (ctgz[3] & 0x10) /* file comment */
- while (*bp)
- bp++;
- if (ctgz[3] & 0x02) /* header crc */
- bp += 2;
- if (bp >= ctgz + clen)
- {
- pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
- solv_free(ctgz);
- return 0;
- }
- ctar = decompress(bp, ctgz + clen - bp, &ctarlen);
- solv_free(ctgz);
- if (!ctar)
- {
- pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
- return 0;
- }
- bp = ctar;
- l = ctarlen;
- while (l > 512)
- {
- int j;
- l2 = 0;
- for (j = 124; j < 124 + 12; j++)
- if (bp[j] >= '0' && bp[j] <= '7')
- l2 = l2 * 8 + (bp[j] - '0');
- if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control"))
- break;
- l2 = 512 + ((l2 + 511) & ~511);
- l -= l2;
- bp += l2;
- }
- if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
- {
- pool_error(pool, -1, "%s: control.tar.gz contains no control file", deb);
- free(ctar);
- return 0;
- }
- memmove(ctar, bp + 512, l2);
- ctar = solv_realloc(ctar, l2 + 1);
- ctar[l2] = 0;
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- control2solvable(s, data, (char *)ctar);
- if (!(flags & REPO_NO_LOCATION))
- repodata_set_location(data, s - pool->solvables, 0, 0, deb);
- if (S_ISREG(stb.st_mode))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
- if (gotpkgid)
- repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
- solv_free(ctar);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return s - pool->solvables;
-}
-
-void
-pool_deb_get_autoinstalled(Pool *pool, FILE *fp, Queue *q, int flags)
-{
- Id name = 0, arch = 0;
- int autoinstalled = -1;
- char *buf, *bp;
- int x, l, bufl, eof = 0;
- Id p, pp;
-
- queue_empty(q);
- buf = solv_malloc(4096);
- bufl = 4096;
- l = 0;
- while (!eof)
- {
- while (bufl - l < 1024)
- {
- bufl += 4096;
- if (bufl > 1024 * 64)
- break; /* hmm? */
- buf = solv_realloc(buf, bufl);
- }
- if (!fgets(buf + l, bufl - l, fp))
- {
- eof = 1;
- buf[l] = '\n';
- buf[l + 1] = 0;
- }
- l = strlen(buf);
- if (l && buf[l - 1] == '\n')
- buf[--l] = 0;
- if (!*buf || eof)
- {
- l = 0;
- if (name && autoinstalled > 0)
- {
- if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
- queue_push2(q, name, arch);
- else if ((flags & GET_USERINSTALLED_NAMES) != 0)
- queue_push(q, name);
- else
- {
- FOR_PROVIDES(p, pp, name)
- {
- Solvable *s = pool->solvables + p;
- if (s->name != name)
- continue;
- if (arch && s->arch != arch)
- continue;
- queue_push(q, p);
- }
- }
- }
- name = arch = 0;
- autoinstalled = -1;
- continue;
- }
- /* strip trailing space */
- while (l && (buf[l - 1] == ' ' || buf[l - 1] == '\t'))
- buf[--l] = 0;
- l = 0;
-
- bp = strchr(buf, ':');
- if (!bp || bp - buf < 4)
- continue;
- *bp++ = 0;
- while (*bp == ' ' || *bp == '\t')
- bp++;
- x = '@' + (buf[0] & 0x1f);
- x = (x << 8) + '@' + (buf[1] & 0x1f);
- switch(x)
- {
- case 'P' << 8 | 'A':
- if (!strcasecmp(buf, "package"))
- name = pool_str2id(pool, bp, 1);
- break;
- case 'A' << 8 | 'R':
- if (!strcasecmp(buf, "architecture"))
- arch = pool_str2id(pool, bp, 1);
- break;
- case 'A' << 8 | 'U':
- if (!strcasecmp(buf, "auto-installed"))
- autoinstalled = atoi(bp);
- break;
- default:
- break;
- }
- }
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_debpackages(Repo *repo, FILE *fp, int flags);
-extern int repo_add_debdb(Repo *repo, int flags);
-extern Id repo_add_deb(Repo *repo, const char *deb, int flags);
-extern void pool_deb_get_autoinstalled(Pool *pool, FILE *fp, Queue *q, int flags);
-
-#define DEBS_ADD_WITH_PKGID (1 << 8)
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define DO_ARRAY 1
-
-#define _GNU_SOURCE
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <expat.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "repo_deltainfoxml.h"
-
-/*
- * <deltainfo>
- * <newpackage name="libtool" epoch="0" version="1.5.24" release="6.fc9" arch="i386">
- * <delta oldepoch="0" oldversion="1.5.24" oldrelease="3.fc8">
- * <filename>DRPMS/libtool-1.5.24-3.fc8_1.5.24-6.fc9.i386.drpm</filename>
- * <sequence>libtool-1.5.24-3.fc8-d3571f98b048b1a870e40241bb46c67ab4</sequence>
- * <size>22452</size>
- * <checksum type="sha">8f05394695dee9399c204614e21e5f6848990ab7</checksum>
- * </delta>
- * <delta oldepoch="0" oldversion="1.5.22" oldrelease="11.fc7">
- * <filename>DRPMS/libtool-1.5.22-11.fc7_1.5.24-6.fc9.i386.drpm</filename>
- * <sequence>libtool-1.5.22-11.fc7-e82691677eee1e83b4812572c5c9ce8eb</sequence>
- * <size>110362</size>
- * <checksum type="sha">326658fee45c0baec1e70231046dbaf560f941ce</checksum>
- * </delta>
- * </newpackage>
- * </deltainfo>
- */
-
-enum state {
- STATE_START,
- STATE_NEWPACKAGE, /* 1 */
- STATE_DELTA, /* 2 */
- STATE_FILENAME, /* 3 */
- STATE_SEQUENCE, /* 4 */
- STATE_SIZE, /* 5 */
- STATE_CHECKSUM, /* 6 */
- STATE_LOCATION, /* 7 */
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-/* !! must be sorted by first column !! */
-static struct stateswitch stateswitches[] = {
- /* compatibility with old yum-presto */
- { STATE_START, "prestodelta", STATE_START, 0 },
- { STATE_START, "deltainfo", STATE_START, 0 },
- { STATE_START, "newpackage", STATE_NEWPACKAGE, 0 },
- { STATE_NEWPACKAGE, "delta", STATE_DELTA, 0 },
- /* compatibility with yum-presto */
- { STATE_DELTA, "filename", STATE_FILENAME, 1 },
- { STATE_DELTA, "location", STATE_LOCATION, 0 },
- { STATE_DELTA, "sequence", STATE_SEQUENCE, 1 },
- { STATE_DELTA, "size", STATE_SIZE, 1 },
- { STATE_DELTA, "checksum", STATE_CHECKSUM, 1 },
- { NUMSTATES }
-};
-
-/* Cumulated info about the current deltarpm or patchrpm */
-struct deltarpm {
- char *location;
- char *locbase;
- unsigned int buildtime;
- unsigned long long downloadsize;
- char *filechecksum;
- int filechecksumtype;
- /* Baseversion. deltarpm only has one. */
- Id *bevr;
- unsigned nbevr;
- Id seqname;
- Id seqevr;
- char *seqnum;
-};
-
-struct parsedata {
- int ret;
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
- Pool *pool;
- Repo *repo;
- Repodata *data;
-
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
- struct deltarpm delta;
- Id newpkgevr;
- Id newpkgname;
- Id newpkgarch;
-
- Id *handles;
- int nhandles;
-};
-
-/*
- * find attribute
- */
-
-static const char *
-find_attr(const char *txt, const char **atts)
-{
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, txt))
- return atts[1];
- }
- return 0;
-}
-
-
-/*
- * create evr (as Id) from 'epoch', 'version' and 'release' attributes
- */
-
-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, "oldepoch"))
- e = atts[1];
- else if (!strcmp(*atts, "epoch"))
- e = atts[1];
- else if (!strcmp(*atts, "version"))
- v = atts[1];
- else if (!strcmp(*atts, "oldversion"))
- v = atts[1];
- else if (!strcmp(*atts, "release"))
- r = atts[1];
- else if (!strcmp(*atts, "oldrelease"))
- r = atts[1];
- }
- if (e && (!*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 = solv_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 pool_str2id(pool, pd->content, 1);
-}
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- struct stateswitch *sw;
- const char *str;
-
-#if 0
- fprintf(stderr, "start: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
-
- pd->depth++;
- if (!pd->swtab[pd->state])
- return;
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
- if (!strcmp(sw->ename, name))
- break;
- if (sw->from != pd->state)
- {
-#if 0
- fprintf(stderr, "into unknown: [%d]%s (from: %d)\n", sw->to, name, sw->from);
-#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_START:
- break;
- case STATE_NEWPACKAGE:
- if ((str = find_attr("name", atts)) != 0)
- pd->newpkgname = pool_str2id(pool, str, 1);
- pd->newpkgevr = makeevr_atts(pool, pd, atts);
- if ((str = find_attr("arch", atts)) != 0)
- pd->newpkgarch = pool_str2id(pool, str, 1);
- break;
-
- case STATE_DELTA:
- memset(&pd->delta, 0, sizeof(pd->delta));
- pd->delta.bevr = solv_extend(pd->delta.bevr, pd->delta.nbevr, 1, sizeof(Id), 7);
- pd->delta.bevr[pd->delta.nbevr++] = makeevr_atts(pool, pd, atts);
- break;
- case STATE_FILENAME:
- if ((str = find_attr("xml:base", atts)))
- pd->delta.locbase = solv_strdup(str);
- break;
- case STATE_LOCATION:
- pd->delta.location = solv_strdup(find_attr("href", atts));
- if ((str = find_attr("xml:base", atts)))
- pd->delta.locbase = solv_strdup(str);
- break;
- case STATE_SIZE:
- break;
- case STATE_CHECKSUM:
- pd->delta.filechecksum = 0;
- pd->delta.filechecksumtype = REPOKEY_TYPE_SHA1;
- if ((str = find_attr("type", atts)) != 0)
- {
- pd->delta.filechecksumtype = solv_chksum_str2type(str);
- if (!pd->delta.filechecksumtype)
- pool_debug(pool, SOLV_ERROR, "unknown checksum type: '%s'\n", str);
- }
- break;
- case STATE_SEQUENCE:
- break;
- default:
- break;
- }
-}
-
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- const char *str;
-
-#if 0
- fprintf(stderr, "end: %s\n", name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
-#if 0
- fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-#endif
- return;
- }
-
- pd->depth--;
- pd->statedepth--;
- switch (pd->state)
- {
- case STATE_START:
- break;
- case STATE_NEWPACKAGE:
- break;
- case STATE_DELTA:
- {
- /* read all data for a deltarpm. commit into attributes */
- Id handle;
- struct deltarpm *d = &pd->delta;
-
- handle = repodata_new_handle(pd->data);
- /* we commit all handles later on in one go so that the
- * repodata code doesn't need to realloc every time */
- pd->handles = solv_extend(pd->handles, pd->nhandles, 1, sizeof(Id), 63);
- pd->handles[pd->nhandles++] = handle;
- repodata_set_id(pd->data, handle, DELTA_PACKAGE_NAME, pd->newpkgname);
- repodata_set_id(pd->data, handle, DELTA_PACKAGE_EVR, pd->newpkgevr);
- repodata_set_id(pd->data, handle, DELTA_PACKAGE_ARCH, pd->newpkgarch);
- if (d->location)
- {
- repodata_set_deltalocation(pd->data, handle, 0, 0, d->location);
- if (d->locbase)
- repodata_set_poolstr(pd->data, handle, DELTA_LOCATION_BASE, d->locbase);
- }
- if (d->downloadsize)
- repodata_set_num(pd->data, handle, DELTA_DOWNLOADSIZE, d->downloadsize);
- if (d->filechecksum)
- repodata_set_checksum(pd->data, handle, DELTA_CHECKSUM, d->filechecksumtype, d->filechecksum);
- if (d->seqnum)
- {
- repodata_set_id(pd->data, handle, DELTA_BASE_EVR, d->bevr[0]);
- repodata_set_id(pd->data, handle, DELTA_SEQ_NAME, d->seqname);
- repodata_set_id(pd->data, handle, DELTA_SEQ_EVR, d->seqevr);
- /* should store as binary blob! */
- repodata_set_str(pd->data, handle, DELTA_SEQ_NUM, d->seqnum);
- }
- }
- pd->delta.filechecksum = solv_free(pd->delta.filechecksum);
- pd->delta.bevr = solv_free(pd->delta.bevr);
- pd->delta.nbevr = 0;
- pd->delta.seqnum = solv_free(pd->delta.seqnum);
- pd->delta.location = solv_free(pd->delta.location);
- pd->delta.locbase = solv_free(pd->delta.locbase);
- break;
- case STATE_FILENAME:
- pd->delta.location = solv_strdup(pd->content);
- break;
- case STATE_CHECKSUM:
- pd->delta.filechecksum = solv_strdup(pd->content);
- break;
- case STATE_SIZE:
- pd->delta.downloadsize = strtoull(pd->content, 0, 10);
- break;
- case STATE_SEQUENCE:
- if ((str = pd->content))
- {
- const char *s1, *s2;
- s1 = strrchr(str, '-');
- if (s1)
- {
- for (s2 = s1 - 1; s2 > str; s2--)
- if (*s2 == '-')
- break;
- if (*s2 == '-')
- {
- for (s2 = s2 - 1; s2 > str; s2--)
- if (*s2 == '-')
- break;
- if (*s2 == '-')
- {
- pd->delta.seqevr = pool_strn2id(pool, s2 + 1, s1 - s2 - 1, 1);
- pd->delta.seqname = pool_strn2id(pool, str, s2 - str, 1);
- str = s1 + 1;
- }
- }
- }
- pd->delta.seqnum = solv_strdup(str);
- }
- default:
- break;
- }
-
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
-}
-
-
-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 = solv_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
-
-int
-repo_add_deltainfoxml(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- struct parsedata pd;
- char buf[BUFF_SIZE];
- int i, l;
- struct stateswitch *sw;
- Repodata *data;
- XML_Parser parser;
-
- data = repo_add_repodata(repo, flags);
-
- 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.repo = repo;
- pd.data = data;
-
- pd.content = solv_malloc(256);
- pd.acontent = 256;
- pd.lcontent = 0;
-
- 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)
- {
- pd.ret = pool_error(pool, -1, "repo_updateinfoxml: %s at line %u:%u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- break;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
- solv_free(pd.content);
-
- /* now commit all handles */
- if (!pd.ret)
- for (i = 0; i < pd.nhandles; i++)
- repodata_add_flexarray(pd.data, SOLVID_META, REPOSITORY_DELTAINFO, pd.handles[i]);
- solv_free(pd.handles);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return pd.ret;
-}
-
-/* EOF */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_deltainfoxml(Repo *repo, FILE *fp, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2011-2013, Ingo Weinhold <ingo_weinhold@gmx.de>
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <package/PackageInfo.h>
-#include <package/PackageInfoSet.h>
-#include <package/PackageRoster.h>
-#include <package/PackageVersion.h>
-#include <package/RepositoryCache.h>
-#include <package/RepositoryConfig.h>
-
-#include "repo_haiku.h"
-
-using namespace BPackageKit;
-using namespace BPackageKit::BHPKG;
-
-static void add_dependency(Repo *repo, Offset &dependencies, const char *name,
- const char *version, int flags, const char* compatVersion = NULL)
-{
- Pool *pool = repo->pool;
-
- Id dependency = pool_str2id(pool, name, 1);
-
- if (version && version[0] != '\0')
- {
- Id versionId = pool_str2id(pool, version, 1);
-
- if (compatVersion && compatVersion[0] != '\0')
- {
- versionId = pool_rel2id(pool, versionId, pool_str2id(pool, compatVersion, 1),
- REL_COMPAT, 1);
- }
-
- dependency = pool_rel2id(pool, dependency, versionId, flags, 1);
- }
-
- dependencies = repo_addid_dep(repo, dependencies, dependency, 0);
-}
-
-static void add_dependency(Repo *repo, Offset &dependencies, const char *name,
- const BPackageVersion &version, int flags)
-{
- add_dependency(repo, dependencies, name, version.ToString(),
- flags);
-}
-
-static void add_resolvables(Repo *repo, Offset &dependencies,
- const BObjectList<BPackageResolvable> &resolvables)
-{
- for (int32 i = 0; BPackageResolvable *resolvable = resolvables.ItemAt(i); i++)
- {
- add_dependency(repo, dependencies, resolvable->Name(),
- resolvable->Version().ToString(), REL_EQ,
- resolvable->CompatibleVersion().ToString());
- }
-}
-
-static void add_resolvable_expressions(Repo *repo, Offset &dependencies,
- const BObjectList<BPackageResolvableExpression> &expressions)
-{
- for (int32 i = 0;
- BPackageResolvableExpression *expression = expressions.ItemAt(i); i++)
- {
- // It is possible that no version is specified. In that case any version
- // is acceptable.
- if (expression->Version().InitCheck() != B_OK)
- {
- BPackageVersion version;
- add_dependency(repo, dependencies, expression->Name(), NULL, 0);
- continue;
- }
-
- int flags = 0;
- switch (expression->Operator())
- {
- case B_PACKAGE_RESOLVABLE_OP_LESS:
- flags |= REL_LT;
- break;
- case B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL:
- flags |= REL_LT | REL_EQ;
- break;
- case B_PACKAGE_RESOLVABLE_OP_EQUAL:
- flags |= REL_EQ;
- break;
- case B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL:
- break;
- case B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL:
- flags |= REL_GT | REL_EQ;
- break;
- case B_PACKAGE_RESOLVABLE_OP_GREATER:
- flags |= REL_GT;
- break;
- }
-
- add_dependency(repo, dependencies, expression->Name(),
- expression->Version(), flags);
- }
-}
-
-static void add_replaces_list(Repo *repo, Offset &dependencies,
- const BStringList &packageNames)
-{
- int32 count = packageNames.CountStrings();
- for (int32 i = 0; i < count; i++)
- {
- const BString &packageName = packageNames.StringAt(i);
- add_dependency(repo, dependencies, packageName, BPackageVersion(), 0);
- }
-}
-
-static Id add_package_info_to_repo(Repo *repo, Repodata *repoData,
- const BPackageInfo &packageInfo)
-{
- Pool *pool = repo->pool;
-
- Id solvableId = repo_add_solvable(repo);
- Solvable *solvable = pool_id2solvable(pool, solvableId);
- // Prepend "pkg:" to package name, so "provides" don't match unless explicitly
- // specified this way.
- BString name("pkg:");
- name << packageInfo.Name();
- solvable->name = pool_str2id(pool, name, 1);
- if (packageInfo.Architecture() == B_PACKAGE_ARCHITECTURE_ANY)
- solvable->arch = ARCH_ANY;
- else if (packageInfo.Architecture() == B_PACKAGE_ARCHITECTURE_SOURCE)
- solvable->arch = ARCH_SRC;
- else
- solvable->arch = pool_str2id(pool,
- BPackageInfo::kArchitectureNames[packageInfo.Architecture()], 1);
- solvable->evr = pool_str2id(pool, packageInfo.Version().ToString(), 1);
- solvable->vendor = pool_str2id(pool, packageInfo.Vendor(), 1);
- repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_SUMMARY,
- packageInfo.Summary());
- repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_DESCRIPTION,
- packageInfo.Description());
- repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_PACKAGER,
- packageInfo.Packager());
-
- if (!packageInfo.Checksum().IsEmpty())
- repodata_set_checksum(repoData, solvable - pool->solvables,
- SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, packageInfo.Checksum());
-
- solvable->provides = repo_addid_dep(repo, solvable->provides,
- pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0);
-
- add_resolvables(repo, solvable->provides, packageInfo.ProvidesList());
- add_resolvable_expressions(repo, solvable->requires,
- packageInfo.RequiresList());
- add_resolvable_expressions(repo, solvable->supplements,
- packageInfo.SupplementsList());
- add_resolvable_expressions(repo, solvable->conflicts,
- packageInfo.ConflictsList());
- add_resolvable_expressions(repo, solvable->enhances,
- packageInfo.FreshensList());
- add_replaces_list(repo, solvable->obsoletes, packageInfo.ReplacesList());
- // TODO: Check whether freshens and replaces does indeed work as intended
- // here.
-
- // TODO: copyrights, licenses, URLs, source URLs
-
- return solvableId;
-}
-
-static void add_installed_packages(Repo *repo, Repodata *repoData,
- BPackageInstallationLocation location)
-{
- BPackageRoster roster;
- BPackageInfoSet packageInfos;
- if (roster.GetActivePackages(location, packageInfos) == B_OK)
- {
- BRepositoryCache::Iterator it = packageInfos.GetIterator();
- while (const BPackageInfo *packageInfo = it.Next())
- add_package_info_to_repo(repo, repoData, *packageInfo);
- }
-}
-
-int repo_add_haiku_installed_packages(Repo *repo, const char *rootdir,
- int flags)
-{
- Repodata *repoData = repo_add_repodata(repo, flags);
-
- add_installed_packages(repo, repoData,
- B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
- add_installed_packages(repo, repoData, B_PACKAGE_INSTALLATION_LOCATION_HOME);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(repoData);
-
- return 0;
-}
-
-Id repo_add_haiku_package(Repo *repo, const char *hpkgPath, int flags)
-{
- BPackageInfo packageInfo;
- if (packageInfo.ReadFromPackageFile(hpkgPath) != B_OK)
- return 0;
-
- return repo_add_haiku_package_info(repo, packageInfo, flags);
-}
-
-int repo_add_haiku_packages(Repo *repo, const char *repoName, int flags)
-{
- BPackageRoster roster;
- BRepositoryCache cache;
- if (roster.GetRepositoryCache(repoName, &cache) != B_OK)
- return 0;
-
- Repodata *repoData = repo_add_repodata(repo, flags);
-
- BRepositoryCache::Iterator it = cache.GetIterator();
- while (const BPackageInfo *packageInfo = it.Next())
- add_package_info_to_repo(repo, repoData, *packageInfo);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(repoData);
-
- return 0;
-}
-
-Id repo_add_haiku_package_info(Repo *repo,
- const BPackageKit::BPackageInfo &packageInfo, int flags)
-{
- if (packageInfo.InitCheck() != B_OK)
- return 0;
-
- Repodata *repoData = repo_add_repodata(repo, flags);
-
- Id id = add_package_info_to_repo(repo, repoData, packageInfo);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(repoData);
-
- return id;
-}
+++ /dev/null
-/*
- * Copyright (c) 2011-2013, Ingo Weinhold <ingo_weinhold@gmx.de>
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef REPO_HAIKU_H
-#define REPO_HAIKU_H
-
-#include "repo.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int repo_add_haiku_installed_packages(Repo *repo, const char *rootdir,
- int flags);
-Id repo_add_haiku_package(Repo *repo, const char *hpkgPath, int flags);
-int repo_add_haiku_packages(Repo *repo, const char *repoName, int flags);
-
-#ifdef __cplusplus
-
-namespace BPackageKit {
- class BPackageInfo;
-}
-
-Id repo_add_haiku_package_info(Repo *repo,
- const BPackageKit::BPackageInfo &packageInfo, int flags);
-
-} /* extern "C" */
-
-#endif /*__cplusplus*/
-
-#endif /* REPO_HAIKU_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo_helix.c
- *
- * Parse 'helix' XML representation
- * and create 'repo'
- *
- * A bit of history: "Helix Code" was the name of the company that
- * wrote Red Carpet. The company was later renamed to Ximian.
- * The Red Carpet solver was merged into the ZYPP project, the
- * library used both by ZENworks and YaST for package management.
- * Red Carpet came with solver testcases in its own repository
- * format, the 'helix' format.
- *
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <expat.h>
-
-#include "repo_helix.h"
-#include "evr.h"
-
-
-/* XML parser states */
-
-enum state {
- STATE_START,
- STATE_CHANNEL,
- STATE_SUBCHANNEL,
- STATE_PACKAGE,
- STATE_NAME,
- STATE_VENDOR,
- STATE_BUILDTIME,
- STATE_HISTORY,
- STATE_UPDATE,
- STATE_EPOCH,
- STATE_VERSION,
- STATE_RELEASE,
- STATE_ARCH,
- STATE_PROVIDES,
- STATE_PROVIDESENTRY,
- STATE_REQUIRES,
- STATE_REQUIRESENTRY,
- STATE_PREREQUIRES,
- STATE_PREREQUIRESENTRY,
- 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_PRODUCT,
-
- STATE_PEPOCH,
- STATE_PVERSION,
- STATE_PRELEASE,
- STATE_PARCH,
-
- NUMSTATES
-};
-
-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, "srcpackage", 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_SUBCHANNEL, "product", STATE_PACKAGE, 0 },
- { STATE_SUBCHANNEL, "application", STATE_PACKAGE, 0 },
- { STATE_PACKAGE, "name", STATE_NAME, 1 },
- { STATE_PACKAGE, "vendor", STATE_VENDOR, 1 },
- { STATE_PACKAGE, "buildtime", STATE_BUILDTIME, 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, "prerequires", STATE_PREREQUIRES, 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_PREREQUIRES, "dep", STATE_PREREQUIRESENTRY, 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 }
-
-};
-
-/*
- * parser data
- */
-
-typedef struct _parsedata {
- int ret;
- /* 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 */
-
- /* repo data */
- Pool *pool; /* current pool */
- Repo *repo; /* current repo */
- Repodata *data; /* current repo data */
- Solvable *solvable; /* current solvable */
- Offset freshens; /* current freshens vector */
-
- /* 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 && (!*e || !strcmp(e, "0")))
- e = 0;
-
- 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 pool_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, Id marker)
-{
- 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 (marker && !strcmp(*a, "pre") && a[1][0] == '1')
- marker = SOLVABLE_PREREQMARKER;
- }
- if (!n) /* quit if no name found */
- return olddeps;
-
- /* kind, name */
- if (k && !strcmp(k, "package"))
- k = NULL; /* package is default */
-
- if (k) /* if kind!=package, intern <kind>:<name> */
- {
- int l = strlen(k) + 1 + strlen(n) + 1;
- if (l > pd->acontent) /* extend buffer if needed */
- {
- pd->content = (char *)realloc(pd->content, l + 256);
- pd->acontent = l + 256;
- }
- sprintf(pd->content, "%s:%s", k, n);
- name = pool_str2id(pool, pd->content, 1);
- }
- else
- {
- name = pool_str2id(pool, n, 1); /* package: just intern <name> */
- }
-
- if (f) /* operator ? */
- {
- /* intern e:v-r */
- Id evr = evr_atts2id(pool, pd, atts);
- /* parser operator to flags */
- int flags;
- for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
- if (!strcmp(f, flagtab[flags].from))
- {
- flags = flagtab[flags].to;
- break;
- }
- if (flags > 7)
- flags = 0;
- /* intern rel */
- id = pool_rel2id(pool, name, evr, flags, 1);
- }
- else
- id = name; /* no operator */
-
- /* add new dependency to repo */
- return repo_addid_dep(pd->repo, olddeps, id, marker);
-}
-
-
-/*----------------------------------------------------------------*/
-
-/*
- * XML callback
- * <name>
- *
- */
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- Parsedata *pd = (Parsedata *)userData;
- struct stateswitch *sw;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
-
- 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 */
- if (!pd->swtab[pd->state])
- return;
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
- {
- if (!strcmp(sw->ename, name))
- break;
- }
-
- /* check if we're at the right level */
- if (sw->from != pd->state)
- {
-#if 0
- fprintf(stderr, "into unknown: %s\n", name);
-#endif
- return;
- }
-
- /* set new state */
- pd->state = sw->to;
-
- pd->docontent = sw->docontent;
- pd->statedepth = pd->depth;
-
- /* start with empty content */
- /* (will collect data until end element) */
- pd->lcontent = 0;
- *pd->content = 0;
-
- switch (pd->state)
- {
-
- case STATE_NAME:
- if (pd->kind) /* if kind is set (non package) */
- {
- strcpy(pd->content, pd->kind);
- pd->lcontent = strlen(pd->content);
- pd->content[pd->lcontent++] = ':'; /* prefix name with '<kind>:' */
- pd->content[pd->lcontent] = 0;
- }
- break;
-
- case STATE_PACKAGE: /* solvable name */
- pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
- 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, "product"))
- pd->kind = "product";
- else if (!strcmp(name, "patch"))
- pd->kind = "patch";
- else if (!strcmp(name, "application"))
- pd->kind = "application";
- else
- pd->kind = NULL; /* default is package */
- pd->levrspace = 1;
- pd->epoch = 0;
- pd->version = 0;
- pd->release = 0;
- pd->freshens = 0;
-#if 0
- fprintf(stderr, "package #%d\n", s - pool->solvables);
-#endif
- break;
-
- case STATE_UPDATE:
- pd->levrspace = 1;
- pd->epoch = 0;
- pd->version = 0;
- pd->release = 0;
- break;
-
- case STATE_PROVIDES: /* start of provides */
- s->provides = 0;
- break;
- case STATE_PROVIDESENTRY: /* entry within provides */
- s->provides = adddep(pool, pd, s->provides, atts, 0);
- break;
- case STATE_REQUIRESENTRY:
- s->requires = adddep(pool, pd, s->requires, atts, -SOLVABLE_PREREQMARKER);
- break;
- case STATE_PREREQUIRESENTRY:
- s->requires = adddep(pool, pd, s->requires, atts, SOLVABLE_PREREQMARKER);
- break;
- case STATE_OBSOLETES:
- s->obsoletes = 0;
- break;
- case STATE_OBSOLETESENTRY:
- s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
- break;
- case STATE_CONFLICTS:
- s->conflicts = 0;
- break;
- case STATE_CONFLICTSENTRY:
- s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
- break;
- case STATE_RECOMMENDS:
- s->recommends = 0;
- break;
- case STATE_RECOMMENDSENTRY:
- s->recommends = adddep(pool, pd, s->recommends, atts, 0);
- break;
- case STATE_SUPPLEMENTS:
- s->supplements= 0;
- break;
- case STATE_SUPPLEMENTSENTRY:
- s->supplements = adddep(pool, pd, s->supplements, atts, 0);
- break;
- case STATE_SUGGESTS:
- s->suggests = 0;
- break;
- case STATE_SUGGESTSENTRY:
- s->suggests = adddep(pool, pd, s->suggests, atts, 0);
- break;
- case STATE_ENHANCES:
- s->enhances = 0;
- break;
- case STATE_ENHANCESENTRY:
- s->enhances = adddep(pool, pd, s->enhances, atts, 0);
- break;
- case STATE_FRESHENS:
- pd->freshens = 0;
- break;
- case STATE_FRESHENSENTRY:
- pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
- break;
- default:
- break;
- }
-}
-
-static const char *findKernelFlavor(Parsedata *pd, Solvable *s)
-{
- Pool *pool = pd->pool;
- Id pid, *pidp;
-
- if (s->provides)
- {
- pidp = pd->repo->idarraydata + s->provides;
- while ((pid = *pidp++) != 0)
- {
- Reldep *prd;
- const char *depname;
-
- if (!ISRELDEP(pid))
- continue; /* wrong provides name */
- prd = GETRELDEP(pool, pid);
- depname = pool_id2str(pool, prd->name);
- if (!strncmp(depname, "kernel-", 7))
- return depname + 7;
- }
- }
-
- if (s->requires)
- {
- pidp = pd->repo->idarraydata + s->requires;
- while ((pid = *pidp++) != 0)
- {
- const char *depname;
-
- if (!ISRELDEP(pid))
- {
- depname = pool_id2str(pool, pid);
- }
- else
- {
- Reldep *prd = GETRELDEP(pool, pid);
- depname = pool_id2str(pool, prd->name);
- }
- if (!strncmp(depname, "kernel-", 7))
- return depname + 7;
- }
- }
-
- return 0;
-}
-
-
-/*
- * XML callback
- * </name>
- *
- * create Solvable from collected data
- */
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- Parsedata *pd = (Parsedata *)userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- Id evr;
- unsigned int t = 0;
- const char *flavor;
-
- 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 (name[0] == 's' && name[1] == 'r' && name[2] == 'c' && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->arch = ARCH_SRC;
- 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->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, pd->freshens);
- s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
- pd->freshens = 0;
-
- /* see bugzilla bnc#190163 */
- flavor = findKernelFlavor(pd, s);
- if (flavor)
- {
- char *cflavor = solv_strdup(flavor); /* make pointer safe */
-
- Id npr;
- Id pid;
-
- /* this is either a kernel package or a kmp */
- if (s->provides)
- {
- Offset prov = s->provides;
- npr = 0;
- while ((pid = pd->repo->idarraydata[prov++]) != 0)
- {
- const char *depname = 0;
- Reldep *prd = 0;
-
- if (ISRELDEP(pid))
- {
- prd = GETRELDEP(pool, pid);
- depname = pool_id2str(pool, prd->name);
- }
- else
- {
- depname = pool_id2str(pool, pid);
- }
-
-
- if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
- {
- char newdep[100];
- snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
- pid = pool_str2id(pool, newdep, 1);
- if (prd)
- pid = pool_rel2id(pool, pid, prd->evr, prd->flags, 1);
- }
-
- npr = repo_addid_dep(pd->repo, npr, pid, 0);
- }
- s->provides = npr;
- }
-#if 1
-
- if (s->requires)
- {
- Offset reqs = s->requires;
- npr = 0;
- while ((pid = pd->repo->idarraydata[reqs++]) != 0)
- {
- const char *depname = 0;
- Reldep *prd = 0;
-
- if (ISRELDEP(pid))
- {
- prd = GETRELDEP(pool, pid);
- depname = pool_id2str(pool, prd->name);
- }
- else
- {
- depname = pool_id2str(pool, pid);
- }
-
- if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
- {
- char newdep[100];
- snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
- pid = pool_str2id(pool, newdep, 1);
- if (prd)
- pid = pool_rel2id(pool, pid, prd->evr, prd->flags, 1);
- }
- npr = repo_addid_dep(pd->repo, npr, pid, 0);
- }
- s->requires = npr;
- }
-#endif
- free(cflavor);
- }
- break;
- case STATE_NAME:
- s->name = pool_str2id(pool, pd->content, 1);
- break;
- case STATE_VENDOR:
- s->vendor = pool_str2id(pool, pd->content, 1);
- break;
- case STATE_BUILDTIME:
- t = atoi (pd->content);
- if (t)
- repodata_set_num(pd->data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
- 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 || pool_evrcmp(pool, s->evr, evr, EVRCMP_COMPARE) <= 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 = pool_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/repo
- *
- */
-
-int
-repo_add_helix(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- Parsedata pd;
- Repodata *data;
- char buf[BUFF_SIZE];
- int i, l;
- struct stateswitch *sw;
- unsigned int now;
- XML_Parser parser;
-
- now = solv_timems(0);
- data = repo_add_repodata(repo, flags);
-
- /* 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.repo = repo;
-
- 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;
- pd.data = data;
-
- /* set up 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)
- {
- pd.ret = pool_error(pool, -1, "%s at line %u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
- break;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
- free(pd.content);
- free(pd.evrspace);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_helix took %d ms\n", solv_timems(now));
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
- return pd.ret;
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo_helix.h
- *
- */
-
-#ifndef LIBSOLV_REPO_HELIX_H
-#define LIBSOLV_REPO_HELIX_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdio.h>
-#include "pool.h"
-#include "repo.h"
-
-extern int repo_add_helix(Repo *repo, FILE *fp, int flags);
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* LIBSOLV_REPO_HELIX_H */
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <expat.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "chksum.h"
-#include "repo_mdk.h"
-
-static Offset
-parse_deps(Solvable *s, char *bp, Id marker)
-{
- Pool *pool = s->repo->pool;
- Offset deps = 0;
- char *nbp, *ebp;
- for (; bp; bp = nbp)
- {
- int ispre = 0;
- Id id, evr = 0;
- int flags = 0;
-
- nbp = strchr(bp, '@');
- if (!nbp)
- ebp = bp + strlen(bp);
- else
- {
- ebp = nbp;
- *nbp++ = 0;
- }
- if (ebp[-1] == ']')
- {
- char *sbp = ebp - 1;
- while (sbp >= bp && *sbp != '[')
- sbp--;
- if (sbp >= bp && sbp[1] != '*')
- {
- char *fbp;
- for (fbp = sbp + 1;; fbp++)
- {
- if (*fbp == '>')
- flags |= REL_GT;
- else if (*fbp == '=')
- flags |= REL_EQ;
- else if (*fbp == '<')
- flags |= REL_LT;
- else
- break;
- }
- if (*fbp == ' ')
- fbp++;
- evr = pool_strn2id(pool, fbp, ebp - 1 - fbp, 1);
- ebp = sbp;
- }
- }
- if (ebp[-1] == ']' && ebp >= bp + 3 && !strncmp(ebp - 3, "[*]", 3))
- {
- ispre = 1;
- ebp -= 3;
- }
- id = pool_strn2id(pool, bp, ebp - bp, 1);
- if (evr)
- id = pool_rel2id(pool, id, evr, flags, 1);
- deps = repo_addid_dep(s->repo, deps, id, ispre ? marker : 0);
- bp = nbp;
- }
- return deps;
-}
-
-int
-repo_add_mdk(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- Solvable *s;
- char *buf;
- int bufa, bufl;
-
- data = repo_add_repodata(repo, flags);
- bufa = 4096;
- buf = solv_malloc(bufa);
- bufl = 0;
- s = 0;
- while (fgets(buf + bufl, bufa - bufl, fp) > 0)
- {
- bufl += strlen(buf + bufl);
- if (!bufl)
- continue;
- if (buf[bufl - 1] != '\n')
- {
- if (bufa - bufl < 256)
- {
- bufa += 4096;
- buf = solv_realloc(buf, bufa);
- }
- continue;
- }
- buf[bufl - 1] = 0;
- bufl = 0;
- if (buf[0] != '@')
- {
- pool_debug(pool, SOLV_ERROR, "bad line <%s>\n", buf);
- continue;
- }
- if (!s)
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- if (!strncmp(buf + 1, "filesize@", 9))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(buf + 10, 0, 10));
- else if (!strncmp(buf + 1, "summary@", 8))
- repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, buf + 9);
- else if (!strncmp(buf + 1, "provides@", 9))
- s->provides = parse_deps(s, buf + 10, 0);
- else if (!strncmp(buf + 1, "requires@", 9))
- s->requires = parse_deps(s, buf + 10, SOLVABLE_PREREQMARKER);
- else if (!strncmp(buf + 1, "recommends@", 11))
- s->recommends = parse_deps(s, buf + 10, 0);
- else if (!strncmp(buf + 1, "suggests@", 9))
- s->suggests = parse_deps(s, buf + 10, 0);
- else if (!strncmp(buf + 1, "obsoletes@", 10))
- s->obsoletes = parse_deps(s, buf + 11, 0);
- else if (!strncmp(buf + 1, "conflicts@", 10))
- s->conflicts = parse_deps(s, buf + 11, 0);
- else if (!strncmp(buf + 1, "info@", 5))
- {
- char *nvra = buf + 6;
- char *epochstr;
- char *arch;
- char *version;
- char *filename;
- char *disttag = 0;
- char *distepoch = 0;
- if ((epochstr = strchr(nvra, '@')) != 0)
- {
- char *sizestr;
- *epochstr++ = 0;
- if ((sizestr = strchr(epochstr, '@')) != 0)
- {
- char *groupstr;
- *sizestr++ = 0;
- if ((groupstr = strchr(sizestr, '@')) != 0)
- {
- *groupstr++ = 0;
- if ((disttag = strchr(groupstr, '@')) != 0)
- {
- *disttag++ = 0;
- if ((distepoch = strchr(disttag, '@')) != 0)
- {
- char *n;
- *distepoch++ = 0;
- if ((n = strchr(distepoch, '@')) != 0)
- *n = 0;
- }
- }
- if (*groupstr)
- repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_GROUP, groupstr);
- }
- if (*sizestr)
- repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(sizestr, 0, 10));
- }
- }
- filename = pool_tmpjoin(pool, nvra, ".rpm", 0);
- arch = strrchr(nvra, '.');
- if (arch)
- {
- *arch++ = 0;
- s->arch = pool_str2id(pool, arch, 1);
- }
- if (disttag && *disttag)
- {
- /* strip disttag from release */
- char *n = strrchr(nvra, '-');
- if (n && !strncmp(n + 1, disttag, strlen(disttag)))
- *n = 0;
- }
- if (distepoch && *distepoch)
- {
- /* add distepoch */
- int le = strlen(distepoch);
- int ln = strlen(nvra);
- nvra[ln++] = ':';
- memmove(nvra + ln, distepoch, le); /* may overlap */
- nvra[le + ln] = 0;
- }
- version = strrchr(nvra, '-');
- if (version)
- {
- char *release = version;
- *release = 0;
- version = strrchr(nvra, '-');
- *release = '-';
- if (!version)
- version = release;
- *version++ = 0;
- }
- else
- version = "";
- s->name = pool_str2id(pool, nvra, 1);
- if (epochstr && *epochstr && strcmp(epochstr, "0") != 0)
- {
- char *evr = pool_tmpjoin(pool, epochstr, ":", version);
- s->evr = pool_str2id(pool, evr, 1);
- }
- else
- s->evr = pool_str2id(pool, version, 1);
- repodata_set_location(data, s - pool->solvables, 0, 0, filename);
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- s = 0;
- }
- else
- {
- char *tagend = strchr(buf + 1, '@');
- if (tagend)
- *tagend = 0;
- pool_debug(pool, SOLV_ERROR, "unknown tag <%s>\n", buf + 1);
- continue;
- }
- }
- if (s)
- {
- pool_debug(pool, SOLV_ERROR, "unclosed package at EOF\n");
- repo_free_solvable(s->repo, s - pool->solvables, 1);
- }
- solv_free(buf);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-enum state {
- STATE_START,
- STATE_MEDIA_INFO,
- STATE_INFO,
- STATE_FILES,
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-/* must be sorted by first column */
-static struct stateswitch stateswitches[] = {
- { STATE_START, "media_info", STATE_MEDIA_INFO, 0 },
- { STATE_MEDIA_INFO, "info", STATE_INFO, 1 },
- { STATE_MEDIA_INFO, "files", STATE_FILES, 1 },
- { NUMSTATES }
-};
-
-struct parsedata {
- Pool *pool;
- Repo *repo;
- Repodata *data;
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
- Solvable *solvable;
- Hashtable joinhash;
- Hashval joinhashmask;
-};
-
-static inline const char *
-find_attr(const char *txt, const char **atts)
-{
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, txt))
- return atts[1];
- }
- return 0;
-}
-
-static Hashtable
-joinhash_init(Repo *repo, Hashval *hmp)
-{
- Hashval hm = mkmask(repo->nsolvables);
- Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
- Hashval h, hh;
- Solvable *s;
- int i;
-
- FOR_REPO_SOLVABLES(repo, i, s)
- {
- hh = HASHCHAIN_START;
- h = s->name & hm;
- while (ht[h])
- h = HASHCHAIN_NEXT(h, hh, hm);
- ht[h] = i;
- }
- *hmp = hm;
- return ht;
-}
-
-static Solvable *
-joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, const char *fn, const char *distepoch)
-{
- Hashval h, hh;
- const char *p, *vrstart, *vrend;
- Id name, arch;
-
- if (!fn || !*fn)
- return 0;
- if (distepoch && !*distepoch)
- distepoch = 0;
- p = fn + strlen(fn);
- while (--p > fn)
- if (*p == '.')
- break;
- if (p == fn)
- return 0;
- arch = pool_str2id(repo->pool, p + 1, 0);
- if (!arch)
- return 0;
- if (distepoch)
- {
- while (--p > fn)
- if (*p == '-')
- break;
- if (p == fn)
- return 0;
- }
- vrend = p;
- while (--p > fn)
- if (*p == '-')
- break;
- if (p == fn)
- return 0;
- while (--p > fn)
- if (*p == '-')
- break;
- if (p == fn)
- return 0;
- vrstart = p + 1;
- name = pool_strn2id(repo->pool, fn, p - fn, 0);
- if (!name)
- return 0;
- hh = HASHCHAIN_START;
- h = name & hm;
- while (ht[h])
- {
- Solvable *s = repo->pool->solvables + ht[h];
- if (s->name == name && s->arch == arch)
- {
- /* too bad we don't know the epoch... */
- const char *evr = pool_id2str(repo->pool, s->evr);
- for (p = evr; *p >= '0' && *p <= '9'; p++)
- ;
- if (p > evr && *p == ':')
- evr = p + 1;
- if (distepoch)
- {
- if (!strncmp(evr, vrstart, vrend - vrstart) && evr[vrend - vrstart] == ':' && !strcmp(distepoch, evr + (vrend - vrstart + 1)))
- return s;
- }
- else if (!strncmp(evr, vrstart, vrend - vrstart) && evr[vrend - vrstart] == 0)
- return s;
- }
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- return 0;
-}
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- struct stateswitch *sw;
-
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
- pd->depth++;
- if (!pd->swtab[pd->state])
- return;
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
- if (!strcmp(sw->ename, name))
- break;
- if (sw->from != pd->state)
- return;
- pd->state = sw->to;
- pd->docontent = sw->docontent;
- pd->statedepth = pd->depth;
- pd->lcontent = 0;
- *pd->content = 0;
- switch (pd->state)
- {
- case STATE_INFO:
- {
- const char *fn = find_attr("fn", atts);
- const char *distepoch = find_attr("distepoch", atts);
- const char *str;
- pd->solvable = joinhash_lookup(pd->repo, pd->joinhash, pd->joinhashmask, fn, distepoch);
- if (!pd->solvable)
- break;
- str = find_attr("url", atts);
- if (str && *str)
- repodata_set_str(pd->data, pd->solvable - pool->solvables, SOLVABLE_URL, str);
- str = find_attr("license", atts);
- if (str && *str)
- repodata_set_poolstr(pd->data, pd->solvable - pool->solvables, SOLVABLE_LICENSE, str);
- str = find_attr("sourcerpm", atts);
- if (str && *str)
- repodata_set_sourcepkg(pd->data, pd->solvable - pool->solvables, str);
- break;
- }
- case STATE_FILES:
- {
- const char *fn = find_attr("fn", atts);
- const char *distepoch = find_attr("distepoch", atts);
- pd->solvable = joinhash_lookup(pd->repo, pd->joinhash, pd->joinhashmask, fn, distepoch);
- break;
- }
- default:
- break;
- }
-}
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- Solvable *s = pd->solvable;
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
- return;
- }
- pd->depth--;
- pd->statedepth--;
- switch (pd->state)
- {
- case STATE_INFO:
- if (s && *pd->content)
- repodata_set_str(pd->data, s - pd->pool->solvables, SOLVABLE_DESCRIPTION, pd->content);
- break;
- case STATE_FILES:
- if (s && *pd->content)
- {
- char *np, *p, *sl;
- for (p = pd->content; p && *p; p = np)
- {
- Id id;
- np = strchr(p, '\n');
- if (np)
- *np++ = 0;
- if (!*p)
- continue;
- sl = strrchr(p, '/');
- if (sl)
- {
- *sl++ = 0;
- id = repodata_str2dir(pd->data, p, 1);
- }
- else
- {
- sl = p;
- id = 0;
- }
- if (!id)
- id = repodata_str2dir(pd->data, "/", 1);
- repodata_add_dirstr(pd->data, s - pd->pool->solvables, SOLVABLE_FILELIST, id, sl);
- }
- }
- break;
- default:
- break;
- }
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
-}
-
-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 = solv_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
-
-int
-repo_add_mdk_info(Repo *repo, FILE *fp, int flags)
-{
- Repodata *data;
- struct parsedata pd;
- char buf[BUFF_SIZE];
- int i, l;
- struct stateswitch *sw;
- XML_Parser parser;
-
- if (!(flags & REPO_EXTEND_SOLVABLES))
- {
- pool_debug(repo->pool, SOLV_ERROR, "repo_add_mdk_info: can only extend existing solvables\n");
- return -1;
- }
-
- data = repo_add_repodata(repo, flags);
-
- memset(&pd, 0, sizeof(pd));
- pd.repo = repo;
- pd.pool = repo->pool;
- pd.data = data;
-
- pd.content = solv_malloc(256);
- pd.acontent = 256;
-
- pd.joinhash = joinhash_init(repo, &pd.joinhashmask);
-
- 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;
- }
-
- 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)
- {
- pool_debug(pd.pool, SOLV_ERROR, "%s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- break;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
- solv_free(pd.content);
- solv_free(pd.joinhash);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_mdk(Repo *repo, FILE *fp, int flags);
-extern int repo_add_mdk_info(Repo *repo, FILE *fp, int flags);
-
+++ /dev/null
-/*
- * repo_products.c
- *
- * Parses all files below 'proddir'
- * See http://en.opensuse.org/Product_Management/Code11
- *
- *
- * Copyright (c) 2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define _GNU_SOURCE
-#define _XOPEN_SOURCE
-#include <time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <dirent.h>
-#include <expat.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#define DISABLE_SPLIT
-#include "tools_util.h"
-#include "repo_content.h"
-#include "repo_zyppdb.h"
-#include "repo_products.h"
-#include "repo_releasefile_products.h"
-
-
-enum state {
- STATE_START,
- STATE_PRODUCT,
- STATE_VENDOR,
- STATE_NAME,
- STATE_VERSION,
- STATE_RELEASE,
- STATE_ARCH,
- STATE_SUMMARY,
- STATE_SHORTSUMMARY,
- STATE_DESCRIPTION,
- STATE_UPDATEREPOKEY,
- STATE_CPEID,
- STATE_URLS,
- STATE_URL,
- STATE_RUNTIMECONFIG,
- STATE_LINGUAS,
- STATE_LANG,
- STATE_REGISTER,
- STATE_TARGET,
- STATE_REGRELEASE,
- STATE_REGFLAVOR,
- STATE_PRODUCTLINE,
- STATE_REGUPDATES,
- STATE_REGUPDREPO,
- STATE_ENDOFLIFE,
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-/* !! must be sorted by first column !! */
-static struct stateswitch stateswitches[] = {
- { STATE_START, "product", STATE_PRODUCT, 0 },
- { STATE_PRODUCT, "vendor", STATE_VENDOR, 1 },
- { STATE_PRODUCT, "name", STATE_NAME, 1 },
- { STATE_PRODUCT, "version", STATE_VERSION, 1 },
- { STATE_PRODUCT, "release", STATE_RELEASE, 1 },
- { STATE_PRODUCT, "arch", STATE_ARCH, 1 },
- { STATE_PRODUCT, "productline", STATE_PRODUCTLINE, 1 },
- { STATE_PRODUCT, "summary", STATE_SUMMARY, 1 },
- { STATE_PRODUCT, "shortsummary", STATE_SHORTSUMMARY, 1 },
- { STATE_PRODUCT, "description", STATE_DESCRIPTION, 1 },
- { STATE_PRODUCT, "register", STATE_REGISTER, 0 },
- { STATE_PRODUCT, "urls", STATE_URLS, 0 },
- { STATE_PRODUCT, "runtimeconfig", STATE_RUNTIMECONFIG, 0 },
- { STATE_PRODUCT, "linguas", STATE_LINGUAS, 0 },
- { STATE_PRODUCT, "updaterepokey", STATE_UPDATEREPOKEY, 1 },
- { STATE_PRODUCT, "cpeid", STATE_CPEID, 1 },
- { STATE_PRODUCT, "endoflife", STATE_ENDOFLIFE, 1 },
- { STATE_URLS, "url", STATE_URL, 1 },
- { STATE_LINGUAS, "lang", STATE_LANG, 0 },
- { STATE_REGISTER, "target", STATE_TARGET, 1 },
- { STATE_REGISTER, "release", STATE_REGRELEASE, 1 },
- { STATE_REGISTER, "flavor", STATE_REGFLAVOR, 1 },
- { STATE_REGISTER, "updates", STATE_REGUPDATES, 0 },
- { STATE_REGUPDATES, "repository", STATE_REGUPDREPO, 0 },
- { NUMSTATES }
-};
-
-struct parsedata {
- const char *filename;
- const char *basename;
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
- Pool *pool;
- Repo *repo;
- Repodata *data;
-
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
- struct joindata jd;
-
- const char *tmplang;
-
- const char *tmpvers;
- const char *tmprel;
- Id urltype;
-
- unsigned int ctime;
-
- Solvable *solvable;
- Id handle;
-
- ino_t baseproduct;
- ino_t currentproduct;
- int productscheme;
-};
-
-
-/*
- * find_attr
- * find value for xml attribute
- * I: txt, name of attribute
- * I: atts, list of key/value attributes
- * O: pointer to value of matching key, or NULL
- *
- */
-
-static inline const char *
-find_attr(const char *txt, const char **atts)
-{
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, txt))
- return atts[1];
- }
- return 0;
-}
-
-static time_t
-datestr2timestamp(const char *date)
-{
- const char *p;
- struct tm tm;
-
- if (!date || !*date)
- return 0;
- for (p = date; *p >= '0' && *p <= '9'; p++)
- ;
- if (!*p)
- return atoi(date);
- memset(&tm, 0, sizeof(tm));
- p = strptime(date, "%F%T", &tm);
- if (!p)
- {
- memset(&tm, 0, sizeof(tm));
- p = strptime(date, "%F", &tm);
- if (!p || *p)
- return 0;
- }
- return timegm(&tm);
-}
-
-/*
- * XML callback: startElement
- */
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- struct stateswitch *sw;
-
-#if 0
- fprintf(stderr, "start: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
-
- pd->depth++;
- if (!pd->swtab[pd->state]) /* no statetable -> no substates */
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#endif
- return;
- }
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
- if (!strcmp(sw->ename, name))
- break;
-
- if (sw->from != pd->state)
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#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_PRODUCT:
- /* parse 'schemeversion' and store in global variable */
- {
- const char * scheme = find_attr("schemeversion", atts);
- pd->productscheme = (scheme && *scheme) ? atoi(scheme) : -1;
- }
- if (!s)
- {
- s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
- pd->handle = s - pool->solvables;
- }
- break;
-
- /* <summary lang="xy">... */
- case STATE_SUMMARY:
- case STATE_DESCRIPTION:
- pd->tmplang = join_dup(&pd->jd, find_attr("lang", atts));
- break;
- case STATE_URL:
- pd->urltype = pool_str2id(pd->pool, find_attr("name", atts), 1);
- break;
- case STATE_REGUPDREPO:
- {
- const char *repoid = find_attr("repoid", atts);
- if (repoid && *repoid)
- {
- Id h = repodata_new_handle(pd->data);
- repodata_set_str(pd->data, h, PRODUCT_UPDATES_REPOID, repoid);
- repodata_add_flexarray(pd->data, pd->handle, PRODUCT_UPDATES, h);
- }
- break;
- }
- default:
- break;
- }
-}
-
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- Solvable *s = pd->solvable;
-
-#if 0
- fprintf(stderr, "end: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
-#if 0
- fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-#endif
- return;
- }
-
- pd->depth--;
- pd->statedepth--;
-
- switch (pd->state)
- {
- case STATE_PRODUCT:
- /* product done, finish solvable */
- if (pd->ctime)
- repodata_set_num(pd->data, pd->handle, SOLVABLE_INSTALLTIME, pd->ctime);
-
- if (pd->basename)
- repodata_set_str(pd->data, pd->handle, PRODUCT_REFERENCEFILE, pd->basename);
-
- /* this is where <productsdir>/baseproduct points to */
- if (pd->currentproduct == pd->baseproduct)
- repodata_set_str(pd->data, pd->handle, PRODUCT_TYPE, "base");
-
- if (pd->tmprel)
- {
- if (pd->tmpvers)
- s->evr = makeevr(pd->pool, join2(&pd->jd, pd->tmpvers, "-", pd->tmprel));
- else
- {
- fprintf(stderr, "Seen <release> but no <version>\n");
- }
- }
- else if (pd->tmpvers)
- s->evr = makeevr(pd->pool, pd->tmpvers); /* just version, no release */
- pd->tmpvers = solv_free((void *)pd->tmpvers);
- pd->tmprel = solv_free((void *)pd->tmprel);
- if (!s->arch)
- s->arch = ARCH_NOARCH;
- if (!s->evr)
- s->evr = ID_EMPTY;
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
- pd->solvable = 0;
- break;
- case STATE_VENDOR:
- s->vendor = pool_str2id(pd->pool, pd->content, 1);
- break;
- case STATE_NAME:
- s->name = pool_str2id(pd->pool, join2(&pd->jd, "product", ":", pd->content), 1);
- break;
- case STATE_VERSION:
- pd->tmpvers = solv_strdup(pd->content);
- break;
- case STATE_RELEASE:
- pd->tmprel = solv_strdup(pd->content);
- break;
- case STATE_ARCH:
- s->arch = pool_str2id(pd->pool, pd->content, 1);
- break;
- case STATE_PRODUCTLINE:
- repodata_set_str(pd->data, pd->handle, PRODUCT_PRODUCTLINE, pd->content);
- break;
- case STATE_UPDATEREPOKEY:
- /** obsolete **/
- break;
- case STATE_SUMMARY:
- repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_SUMMARY, pd->tmplang, 1), pd->content);
- break;
- case STATE_SHORTSUMMARY:
- repodata_set_str(pd->data, pd->handle, PRODUCT_SHORTLABEL, pd->content);
- break;
- case STATE_DESCRIPTION:
- repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_DESCRIPTION, pd->tmplang, 1), pd->content);
- break;
- case STATE_URL:
- if (pd->urltype)
- {
- repodata_add_poolstr_array(pd->data, pd->handle, PRODUCT_URL, pd->content);
- repodata_add_idarray(pd->data, pd->handle, PRODUCT_URL_TYPE, pd->urltype);
- }
- break;
- case STATE_TARGET:
- repodata_set_str(pd->data, pd->handle, PRODUCT_REGISTER_TARGET, pd->content);
- break;
- case STATE_REGRELEASE:
- repodata_set_str(pd->data, pd->handle, PRODUCT_REGISTER_RELEASE, pd->content);
- break;
- case STATE_REGFLAVOR:
- repodata_set_str(pd->data, pd->handle, PRODUCT_REGISTER_FLAVOR, pd->content);
- break;
- case STATE_CPEID:
- if (*pd->content)
- repodata_set_str(pd->data, pd->handle, SOLVABLE_CPEID, pd->content);
- break;
- case STATE_ENDOFLIFE:
- if (*pd->content)
- {
- time_t t = datestr2timestamp(pd->content);
- if (t)
- repodata_set_num(pd->data, pd->handle, PRODUCT_ENDOFLIFE, (unsigned long long)t);
- }
- break;
- default:
- break;
- }
-
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
-
-#if 0
- fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
-#endif
-}
-
-
-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 = solv_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
-
-
-/*
- * add single product to repo
- *
- */
-
-static void
-add_code11_product(struct parsedata *pd, FILE *fp)
-{
- char buf[BUFF_SIZE];
- int l;
- struct stat st;
- XML_Parser parser;
-
- if (!fstat(fileno(fp), &st))
- {
- pd->currentproduct = st.st_ino;
- pd->ctime = (unsigned int)st.st_ctime;
- }
- else
- {
- pd->currentproduct = pd->baseproduct + 1; /* make it != baseproduct if stat fails */
- pool_error(pd->pool, 0, "fstat: %s", strerror(errno));
- pd->ctime = 0;
- }
-
- 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)
- {
- pool_debug(pd->pool, SOLV_ERROR, "%s: %s at line %u:%u\n", pd->filename, XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- XML_ParserFree(parser);
- if (pd->solvable)
- {
- repo_free_solvable(pd->repo, pd->solvable - pd->pool->solvables, 1);
- pd->solvable = 0;
- }
- return;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
-}
-
-
-int
-repo_add_code11_products(Repo *repo, const char *dirpath, int flags)
-{
- Repodata *data;
- struct parsedata pd;
- struct stateswitch *sw;
- DIR *dir;
- int i;
-
- data = repo_add_repodata(repo, flags);
-
- memset(&pd, 0, sizeof(pd));
- pd.repo = repo;
- pd.pool = repo->pool;
- pd.data = data;
-
- pd.content = solv_malloc(256);
- pd.acontent = 256;
-
- 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;
- }
-
- if (flags & REPO_USE_ROOTDIR)
- dirpath = pool_prepend_rootdir(repo->pool, dirpath);
- dir = opendir(dirpath);
- if (dir)
- {
- struct dirent *entry;
- struct stat st;
- char *fullpath;
-
- /* check for <productsdir>/baseproduct on code11 and remember its target inode */
- if (stat(join2(&pd.jd, dirpath, "/", "baseproduct"), &st) == 0) /* follow symlink */
- pd.baseproduct = st.st_ino;
- else
- pd.baseproduct = 0;
-
- while ((entry = readdir(dir)))
- {
- int len = strlen(entry->d_name);
- FILE *fp;
- if (len <= 5 || strcmp(entry->d_name + len - 5, ".prod") != 0)
- continue;
- fullpath = join2(&pd.jd, dirpath, "/", entry->d_name);
- fp = fopen(fullpath, "r");
- if (!fp)
- {
- pool_error(repo->pool, 0, "%s: %s", fullpath, strerror(errno));
- continue;
- }
- pd.filename = fullpath;
- pd.basename = entry->d_name;
- add_code11_product(&pd, fp);
- fclose(fp);
- }
- closedir(dir);
- }
- solv_free(pd.content);
- join_freemem(&pd.jd);
- if (flags & REPO_USE_ROOTDIR)
- solv_free((char *)dirpath);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-
-/******************************************************************************************/
-
-
-/*
- * read all installed products
- *
- * try proddir (reading all .xml files from this directory) first
- * if not available, assume non-code11 layout and parse /etc/xyz-release
- *
- * parse each one as a product
- */
-
-/* Oh joy! Three parsers for the price of one! */
-
-int
-repo_add_products(Repo *repo, const char *proddir, int flags)
-{
- const char *fullpath;
- DIR *dir;
-
- if (proddir)
- {
- dir = opendir(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(repo->pool, proddir) : proddir);
- if (dir)
- {
- /* assume code11 stype products */
- closedir(dir);
- return repo_add_code11_products(repo, proddir, flags);
- }
- }
-
- /* code11 didn't work, try old code10 zyppdb */
- fullpath = "/var/lib/zypp/db/products";
- if (flags & REPO_USE_ROOTDIR)
- fullpath = pool_prepend_rootdir_tmp(repo->pool, fullpath);
- dir = opendir(fullpath);
- if (dir)
- {
- closedir(dir);
- /* assume code10 style products */
- return repo_add_zyppdb_products(repo, "/var/lib/zypp/db/products", flags);
- }
-
- /* code10/11 didn't work, try -release files parsing */
- fullpath = "/etc";
- if (flags & REPO_USE_ROOTDIR)
- fullpath = pool_prepend_rootdir_tmp(repo->pool, fullpath);
- dir = opendir(fullpath);
- if (dir)
- {
- closedir(dir);
- return repo_add_releasefile_products(repo, "/etc", flags);
- }
-
- /* no luck. check if the rootdir exists */
- fullpath = pool_get_rootdir(repo->pool);
- if (fullpath && *fullpath)
- {
- dir = opendir(fullpath);
- if (!dir)
- return pool_error(repo->pool, -1, "%s: %s", fullpath, strerror(errno));
- closedir(dir);
- }
-
- /* the least we can do... */
- if (!(flags & REPO_NO_INTERNALIZE) && (flags & REPO_REUSE_REPODATA) != 0)
- repodata_internalize(repo_last_repodata(repo));
- return 0;
-}
-
-/* EOF */
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_code11_products(Repo *repo, const char *dirpath, int flags);
-extern int repo_add_products(Repo *repo, const char *proddir, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2007-2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo_pubkey
- *
- * support for pubkey reading
- *
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <assert.h>
-#include <stdint.h>
-#include <errno.h>
-#include <dirent.h>
-
-#include <rpm/rpmio.h>
-#include <rpm/rpmpgp.h>
-#ifndef RPM5
-#include <rpm/header.h>
-#endif
-#include <rpm/rpmdb.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "hash.h"
-#include "util.h"
-#include "queue.h"
-#include "chksum.h"
-#include "repo_rpmdb.h"
-#include "repo_pubkey.h"
-#ifdef ENABLE_PGPVRFY
-#include "solv_pgpvrfy.h"
-#endif
-
-static void
-setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
-{
- if (str[solv_validutf8(str)])
- {
- char *ustr = solv_latin1toutf8(str); /* not utf8, assume latin1 */
- repodata_set_str(repodata, handle, tag, ustr);
- solv_free(ustr);
- }
- else
- repodata_set_str(repodata, handle, tag, str);
-}
-
-static char *
-r64dec1(char *p, unsigned int *vp, int *eofp)
-{
- int i, x;
- unsigned int v = 0;
-
- for (i = 0; i < 4; )
- {
- x = *p++;
- if (!x)
- return 0;
- if (x >= 'A' && x <= 'Z')
- x -= 'A';
- else if (x >= 'a' && x <= 'z')
- x -= 'a' - 26;
- else if (x >= '0' && x <= '9')
- x -= '0' - 52;
- else if (x == '+')
- x = 62;
- else if (x == '/')
- x = 63;
- else if (x == '=')
- {
- x = 0;
- if (i == 0)
- {
- *eofp = 3;
- *vp = 0;
- return p - 1;
- }
- *eofp += 1;
- }
- else
- continue;
- v = v << 6 | x;
- i++;
- }
- *vp = v;
- return p;
-}
-
-static unsigned int
-crc24(unsigned char *p, int len)
-{
- unsigned int crc = 0xb704ce;
- int i;
-
- while (len--)
- {
- crc ^= (*p++) << 16;
- for (i = 0; i < 8; i++)
- if ((crc <<= 1) & 0x1000000)
- crc ^= 0x1864cfb;
- }
- return crc & 0xffffff;
-}
-
-static int
-unarmor(char *pubkey, unsigned char **pktp, int *pktlp, const char *startstr, const char *endstr)
-{
- char *p, *pubkeystart = pubkey;
- int l, eof;
- unsigned char *buf, *bp;
- unsigned int v;
-
- *pktp = 0;
- *pktlp = 0;
- if (!pubkey)
- return 0;
- l = strlen(startstr);
- while (strncmp(pubkey, startstr, l) != 0)
- {
- pubkey = strchr(pubkey, '\n');
- if (!pubkey)
- return 0;
- pubkey++;
- }
- pubkey = strchr(pubkey, '\n');
- if (!pubkey++)
- return 0;
- /* skip header lines */
- for (;;)
- {
- while (*pubkey == ' ' || *pubkey == '\t')
- pubkey++;
- if (*pubkey == '\n')
- break;
- pubkey = strchr(pubkey, '\n');
- if (!pubkey++)
- return 0;
- }
- pubkey++;
- p = strchr(pubkey, '=');
- if (!p)
- return 0;
- l = p - pubkey;
- bp = buf = solv_malloc(l * 3 / 4 + 4);
- eof = 0;
- while (!eof)
- {
- pubkey = r64dec1(pubkey, &v, &eof);
- if (!pubkey)
- {
- solv_free(buf);
- return 0;
- }
- *bp++ = v >> 16;
- *bp++ = v >> 8;
- *bp++ = v;
- }
- while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
- pubkey++;
- bp -= eof;
- if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
- {
- solv_free(buf);
- return 0;
- }
- if (v != crc24(buf, bp - buf))
- {
- solv_free(buf);
- return 0;
- }
- while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
- pubkey++;
- if (strncmp(pubkey, endstr, strlen(endstr)) != 0)
- {
- solv_free(buf);
- return 0;
- }
- p = strchr(pubkey, '\n');
- if (!p)
- p = pubkey + strlen(pubkey);
- *pktp = buf;
- *pktlp = bp - buf;
- return (p ? p + 1 : pubkey + strlen(pubkey)) - pubkeystart;
-}
-
-#define ARMOR_NLAFTER 16
-
-static char *
-armor(unsigned char *pkt, int pktl, const char *startstr, const char *endstr, const char *version)
-{
- static const char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- char *str = solv_malloc(strlen(startstr) + strlen(endstr) + strlen(version) + (pktl / 3) * 4 + (pktl / (ARMOR_NLAFTER * 3)) + 30);
- char *p = str;
- int a, b, c, i;
- unsigned int v;
-
- v = crc24(pkt, pktl);
- sprintf(p, "%s\nVersion: %s\n\n", startstr, version);
- p += strlen(p);
- for (i = -1; pktl > 0; pktl -= 3)
- {
- if (++i == ARMOR_NLAFTER)
- {
- i = 0;
- *p++ = '\n';
- }
- a = *pkt++;
- b = pktl > 1 ? *pkt++ : 0;
- c = pktl > 2 ? *pkt++ : 0;
- *p++ = bintoasc[a >> 2];
- *p++ = bintoasc[(a & 3) << 4 | b >> 4];
- *p++ = pktl > 1 ? bintoasc[(b & 15) << 2 | c >> 6] : '=';
- *p++ = pktl > 2 ? bintoasc[c & 63] : '=';
- }
- *p++ = '\n';
- *p++ = '=';
- *p++ = bintoasc[v >> 18 & 0x3f];
- *p++ = bintoasc[v >> 12 & 0x3f];
- *p++ = bintoasc[v >> 6 & 0x3f];
- *p++ = bintoasc[v & 0x3f];
- sprintf(p, "\n%s\n", endstr);
- return str;
-}
-
-/* internal representation of a signature */
-struct pgpsig {
- int type;
- Id hashalgo;
- unsigned char issuer[8];
- int haveissuer;
- unsigned int created;
- unsigned int expires;
- unsigned int keyexpires;
- unsigned char *sigdata;
- int sigdatal;
- int mpioff;
-};
-
-static Id
-pgphashalgo2type(int algo)
-{
- if (algo == 1)
- return REPOKEY_TYPE_MD5;
- if (algo == 2)
- return REPOKEY_TYPE_SHA1;
- if (algo == 8)
- return REPOKEY_TYPE_SHA256;
- if (algo == 9)
- return REPOKEY_TYPE_SHA384;
- if (algo == 10)
- return REPOKEY_TYPE_SHA512;
- if (algo == 11)
- return REPOKEY_TYPE_SHA224;
- return 0;
-}
-
-/* hash the pubkey/userid data for self-sig verification
- * hash the final trailer
- * create a "sigdata" block suitable for a call to solv_pgpverify */
-static void
-pgpsig_makesigdata(struct pgpsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl, Chksum *h)
-{
- int type = sig->type;
- unsigned char b[6];
- const unsigned char *cs;
- int csl;
-
- if (!h || sig->mpioff < 2 || l <= sig->mpioff)
- return;
- if ((type >= 0x10 && type <= 0x13) || type == 0x1f || type == 0x18 || type == 0x20 || type == 0x28)
- {
- b[0] = 0x99;
- b[1] = pubkeyl >> 8;
- b[2] = pubkeyl;
- solv_chksum_add(h, b, 3);
- solv_chksum_add(h, pubkey, pubkeyl);
- }
- if ((type >= 0x10 && type <= 0x13))
- {
- if (p[0] != 3)
- {
- b[0] = 0xb4;
- b[1] = useridl >> 24;
- b[2] = useridl >> 16;
- b[3] = useridl >> 8;
- b[4] = useridl;
- solv_chksum_add(h, b, 5);
- }
- solv_chksum_add(h, userid, useridl);
- }
- /* add trailer */
- if (p[0] == 3)
- solv_chksum_add(h, p + 2, 5);
- else
- {
- int hl = 6 + (p[4] << 8 | p[5]);
- solv_chksum_add(h, p, hl);
- b[0] = 4;
- b[1] = 0xff;
- b[2] = hl >> 24;
- b[3] = hl >> 16;
- b[4] = hl >> 8;
- b[5] = hl;
- solv_chksum_add(h, b, 6);
- }
- cs = solv_chksum_get(h, &csl);
- if (cs[0] == p[sig->mpioff - 2] && cs[1] == p[sig->mpioff - 1])
- {
- int ml = l - sig->mpioff;
- sig->sigdata = solv_malloc(2 + csl + ml);
- sig->sigdatal = 2 + csl + ml;
- sig->sigdata[0] = p[0] == 3 ? p[15] : p[2];
- sig->sigdata[1] = p[0] == 3 ? p[16] : p[3];
- memcpy(sig->sigdata + 2, cs, csl);
- memcpy(sig->sigdata + 2 + csl, p + sig->mpioff, ml);
- }
-}
-
-/* parse the header of a subpacket contained in a signature packet
- * returns: length of the packet header, 0 if there was an error
- * *pktlp is set to the packet length, the tag is the first byte.
- */
-static inline int
-parsesubpkglength(unsigned char *q, int ql, int *pktlp)
-{
- int x, sl, hl;
- /* decode sub-packet length, ql must be > 0 */
- x = *q++;
- if (x < 192)
- {
- sl = x;
- hl = 1;
- }
- else if (x == 255)
- {
- if (ql < 5 || q[0] != 0)
- return 0;
- sl = q[1] << 16 | q[2] << 8 | q[3];
- hl = 5;
- }
- else
- {
- if (ql < 2)
- return 0;
- sl = ((x - 192) << 8) + q[0] + 192;
- hl = 2;
- }
- if (!sl || ql < sl + hl) /* sub pkg tag is included in length, i.e. sl must not be zero */
- return 0;
- *pktlp = sl;
- return hl;
-}
-
-/* parse a signature packet, initializing the pgpsig struct */
-static void
-pgpsig_init(struct pgpsig *sig, unsigned char *p, int l)
-{
- memset(sig, 0, sizeof(*sig));
- sig->type = -1;
- if (p[0] == 3)
- {
- /* printf("V3 signature packet\n"); */
- if (l <= 19 || p[1] != 5)
- return;
- sig->type = p[2];
- sig->haveissuer = 1;
- memcpy(sig->issuer, p + 7, 8);
- sig->created = p[3] << 24 | p[4] << 16 | p[5] << 8 | p[6];
- sig->hashalgo = p[16];
- sig->mpioff = 19;
- }
- else if (p[0] == 4)
- {
- int j, ql, x;
- unsigned char *q;
-
- /* printf("V4 signature packet\n"); */
- if (l < 6)
- return;
- sig->type = p[1];
- sig->hashalgo = p[3];
- q = p + 4;
- sig->keyexpires = -1;
- for (j = 0; q && j < 2; j++)
- {
- if (q + 2 > p + l)
- {
- q = 0;
- break;
- }
- ql = q[0] << 8 | q[1];
- q += 2;
- if (q + ql > p + l)
- {
- q = 0;
- break;
- }
- while (ql > 0)
- {
- int sl, hl;
- hl = parsesubpkglength(q, ql, &sl);
- if (!hl)
- {
- q = 0;
- break;
- }
- q += hl;
- ql -= hl;
- x = q[0] & 127; /* strip critical bit */
- /* printf("%d SIGSUB %d %d\n", j, x, sl); */
- if (x == 16 && sl == 9 && !sig->haveissuer)
- {
- sig->haveissuer = 1;
- memcpy(sig->issuer, q + 1, 8);
- }
- if (x == 2 && j == 0)
- sig->created = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
- if (x == 3 && j == 0)
- sig->expires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
- if (x == 9 && j == 0)
- sig->keyexpires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
- q += sl;
- ql -= sl;
- }
- }
- if (q && q - p + 2 < l)
- sig->mpioff = q - p + 2;
- }
-}
-
-/* parse a pgp packet header
- * returns: length of the packet header, 0 if there was an error
- * *tagp and *pktlp is set to the packet tag and the packet length
- */
-static int
-parsepkgheader(unsigned char *p, int pl, int *tagp, int *pktlp)
-{
- unsigned char *op = p;
- int x, l;
-
- if (!pl)
- return 0;
- x = *p++;
- pl--;
- if (!(x & 128) || pl <= 0)
- return 0;
- if ((x & 64) == 0)
- {
- *tagp = (x & 0x3c) >> 2; /* old format */
- x = 1 << (x & 3);
- if (x > 4 || pl < x || (x == 4 && p[0]))
- return 0;
- pl -= x;
- for (l = 0; x--;)
- l = l << 8 | *p++;
- }
- else
- {
- *tagp = (x & 0x3f); /* new format */
- x = *p++;
- pl--;
- if (x < 192)
- l = x;
- else if (x >= 192 && x < 224)
- {
- if (pl <= 0)
- return 0;
- l = ((x - 192) << 8) + *p++ + 192;
- pl--;
- }
- else if (x == 255)
- {
- if (pl <= 4 || p[0] != 0) /* sanity: p[0] must be zero */
- return 0;
- l = p[1] << 16 | p[2] << 8 | p[3];
- p += 4;
- pl -= 4;
- }
- else
- return 0;
- }
- if (l > pl)
- return 0;
- *pktlp = l;
- return p - op;
-}
-
-/* parse the first pubkey (possible creating new packages for the subkeys)
- * returns the number of parsed bytes.
- * if flags contains ADD_WITH_SUBKEYS, all subkeys will be added as new
- * solvables as well */
-static int
-parsepubkey(Solvable *s, Repodata *data, unsigned char *p, int pl, int flags)
-{
- Repo *repo = s->repo;
- unsigned char *pstart = p;
- int tag, l;
- unsigned char keyid[8];
- char subkeyofstr[17];
- unsigned int kcr = 0, maxex = 0, maxsigcr = 0;
- unsigned char *pubkey = 0;
- int pubkeyl = 0;
- int insubkey = 0;
- unsigned char *userid = 0;
- int useridl = 0;
- unsigned char *pubdata = 0;
- int pubdatal = 0;
-
- *subkeyofstr = 0;
- for (; ; p += l, pl -= l)
- {
- int hl = parsepkgheader(p, pl, &tag, &l);
- if (!hl || (pubkey && (tag == 6 || tag == 14)))
- {
- /* finish old key */
- if (kcr)
- repodata_set_num(data, s - repo->pool->solvables, SOLVABLE_BUILDTIME, kcr);
- if (maxex && maxex != -1)
- repodata_set_num(data, s - repo->pool->solvables, PUBKEY_EXPIRES, maxex);
- s->name = pool_str2id(s->repo->pool, insubkey ? "gpg-subkey" : "gpg-pubkey", 1);
- s->evr = 1;
- s->arch = 1;
- if (userid && useridl)
- {
- char *useridstr = solv_malloc(useridl + 1);
- memcpy(useridstr, userid, useridl);
- useridstr[useridl] = 0;
- setutf8string(data, s - repo->pool->solvables, SOLVABLE_SUMMARY, useridstr);
- free(useridstr);
- }
- if (pubdata)
- {
- char keyidstr[17];
- char evr[8 + 1 + 8 + 1];
- solv_bin2hex(keyid, 8, keyidstr);
- repodata_set_str(data, s - repo->pool->solvables, PUBKEY_KEYID, keyidstr);
- /* build rpm-style evr */
- strcpy(evr, keyidstr + 8);
- sprintf(evr + 8, "-%08x", maxsigcr);
- s->evr = pool_str2id(repo->pool, evr, 1);
- }
- if (insubkey && *subkeyofstr)
- repodata_set_str(data, s - repo->pool->solvables, PUBKEY_SUBKEYOF, subkeyofstr);
- if (pubdata) /* set data blob */
- repodata_set_binary(data, s - repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
- if (!pl)
- break;
- if (!hl)
- {
- p = 0; /* parse error */
- break;
- }
- if (tag == 6 || (tag == 14 && !(flags & ADD_WITH_SUBKEYS)))
- break;
- if (tag == 14 && pubdata && !insubkey)
- solv_bin2hex(keyid, 8, subkeyofstr);
- /* create new solvable for subkey */
- s = pool_id2solvable(repo->pool, repo_add_solvable(repo));
- }
- p += hl;
- pl -= hl;
- if (!pubkey && tag != 6)
- continue;
- if (tag == 6 || (tag == 14 && (flags & ADD_WITH_SUBKEYS) != 0)) /* Public-Key Packet */
- {
- if (tag == 6)
- {
- pubkey = solv_memdup(p, l);
- pubkeyl = l;
- }
- else
- insubkey = 1;
- pubdata = 0;
- pubdatal = 0;
- if (p[0] == 3 && l >= 10)
- {
- unsigned int ex;
- Chksum *h;
- maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
- ex = 0;
- if (p[5] || p[6])
- {
- ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
- if (ex > maxex)
- maxex = ex;
- }
- memset(keyid, 0, 8);
- if (p[7] == 1) /* RSA */
- {
- int ql, ql2;
- unsigned char fp[16];
- char fpx[32 + 1];
- unsigned char *q;
-
- ql = ((p[8] << 8 | p[9]) + 7) / 8; /* length of public modulus */
- if (ql >= 8 && 10 + ql + 2 <= l)
- {
- memcpy(keyid, p + 10 + ql - 8, 8); /* keyid is last 64 bits of public modulus */
- q = p + 10 + ql;
- ql2 = ((q[0] << 8 | q[1]) + 7) / 8; /* length of encryption exponent */
- if (10 + ql + 2 + ql2 <= l)
- {
- /* fingerprint is the md5 over the two MPI bodies */
- h = solv_chksum_create(REPOKEY_TYPE_MD5);
- solv_chksum_add(h, p + 10, ql);
- solv_chksum_add(h, q + 2, ql2);
- solv_chksum_free(h, fp);
- solv_bin2hex(fp, 16, fpx);
- repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
- }
- }
- pubdata = p + 7;
- pubdatal = l - 7;
- }
- }
- else if (p[0] == 4 && l >= 6)
- {
- Chksum *h;
- unsigned char hdr[3];
- unsigned char fp[20];
- char fpx[40 + 1];
-
- maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
- hdr[0] = 0x99;
- hdr[1] = l >> 8;
- hdr[2] = l;
- /* fingerprint is the sha1 over the packet */
- h = solv_chksum_create(REPOKEY_TYPE_SHA1);
- solv_chksum_add(h, hdr, 3);
- solv_chksum_add(h, p, l);
- solv_chksum_free(h, fp);
- solv_bin2hex(fp, 20, fpx);
- repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
- memcpy(keyid, fp + 12, 8); /* keyid is last 64 bits of fingerprint */
- pubdata = p + 5;
- pubdatal = l - 5;
- }
- }
- if (tag == 2) /* Signature Packet */
- {
- struct pgpsig sig;
- Id htype;
- if (!pubdata)
- continue;
- pgpsig_init(&sig, p, l);
- if (!sig.haveissuer || !((sig.type >= 0x10 && sig.type <= 0x13) || sig.type == 0x1f))
- continue;
- if (sig.type >= 0x10 && sig.type <= 0x13 && !userid)
- continue;
- htype = pgphashalgo2type(sig.hashalgo);
- if (htype && sig.mpioff)
- {
- Chksum *h = solv_chksum_create(htype);
- pgpsig_makesigdata(&sig, p, l, pubkey, pubkeyl, userid, useridl, h);
- solv_chksum_free(h, 0);
- }
- if (!memcmp(keyid, sig.issuer, 8))
- {
-#ifdef ENABLE_PGPVRFY
- /* found self sig, verify */
- if (solv_pgpvrfy(pubdata, pubdatal, sig.sigdata, sig.sigdatal))
-#endif
- {
- if (sig.keyexpires && maxex != -1)
- {
- if (sig.keyexpires == -1)
- maxex = -1;
- else if (sig.keyexpires + kcr > maxex)
- maxex = sig.keyexpires + kcr;
- }
- if (sig.created > maxsigcr)
- maxsigcr = sig.created;
- }
- }
- else if (flags & ADD_WITH_KEYSIGNATURES)
- {
- char issuerstr[17];
- Id shandle = repodata_new_handle(data);
- solv_bin2hex(sig.issuer, 8, issuerstr);
- repodata_set_str(data, shandle, SIGNATURE_ISSUER, issuerstr);
- if (sig.created)
- repodata_set_num(data, shandle, SIGNATURE_TIME, sig.created);
- if (sig.expires)
- repodata_set_num(data, shandle, SIGNATURE_EXPIRES, sig.created + sig.expires);
- if (sig.sigdata)
- repodata_set_binary(data, shandle, SIGNATURE_DATA, sig.sigdata, sig.sigdatal);
- repodata_add_flexarray(data, s - s->repo->pool->solvables, PUBKEY_SIGNATURES, shandle);
- }
- solv_free(sig.sigdata);
- }
- if (tag == 13 && !insubkey) /* User ID Packet */
- {
- userid = solv_realloc(userid, l);
- if (l)
- memcpy(userid, p, l);
- useridl = l;
- }
- }
- solv_free(pubkey);
- solv_free(userid);
- return p ? p - pstart : 0;
-}
-
-
-#ifdef ENABLE_RPMDB
-
-/* this is private to rpm, but rpm lacks an interface to retrieve
- * the values. Sigh. */
-struct pgpDigParams_s {
- const char * userid;
- const unsigned char * hash;
-#ifndef HAVE_PGPDIGGETPARAMS
- const char * params[4];
-#endif
- unsigned char tag;
- unsigned char version; /*!< version number. */
- unsigned char time[4]; /*!< time that the key was created. */
- unsigned char pubkey_algo; /*!< public key algorithm. */
- unsigned char hash_algo;
- unsigned char sigtype;
- unsigned char hashlen;
- unsigned char signhash16[2];
- unsigned char signid[8];
- unsigned char saved;
-};
-
-#ifndef HAVE_PGPDIGGETPARAMS
-struct pgpDig_s {
- struct pgpDigParams_s signature;
- struct pgpDigParams_s pubkey;
-};
-#endif
-
-
-/* only rpm knows how to do the release calculation, we don't dare
- * to recreate all the bugs in libsolv */
-static void
-parsepubkey_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
-{
- Pool *pool = s->repo->pool;
- struct pgpDigParams_s *digpubkey;
- pgpDig dig = 0;
- char keyid[16 + 1];
- char evrbuf[8 + 1 + 8 + 1];
- unsigned int btime;
-
-#ifndef RPM5
- dig = pgpNewDig();
-#else
- dig = pgpDigNew(RPMVSF_DEFAULT, 0);
-#endif
- (void) pgpPrtPkts(pkts, pktsl, dig, 0);
-#ifdef HAVE_PGPDIGGETPARAMS
- digpubkey = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY);
-#else
- digpubkey = &dig->pubkey;
-#endif
- if (digpubkey)
- {
- btime = digpubkey->time[0] << 24 | digpubkey->time[1] << 16 | digpubkey->time[2] << 8 | digpubkey->time[3];
- solv_bin2hex(digpubkey->signid, 8, keyid);
- solv_bin2hex(digpubkey->signid + 4, 4, evrbuf);
- evrbuf[8] = '-';
- solv_bin2hex(digpubkey->time, 4, evrbuf + 9);
- s->evr = pool_str2id(pool, evrbuf, 1);
- repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyid);
- if (digpubkey->userid)
- setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, digpubkey->userid);
- if (btime)
- repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
- }
-#ifndef RPM5
- (void)pgpFreeDig(dig);
-#else
- (void)pgpDigFree(dig);
-#endif
-}
-
-#endif /* ENABLE_RPMDB */
-
-/* parse an ascii armored pubkey
- * adds multiple pubkeys if ADD_MULTIPLE_PUBKEYS is set */
-static int
-pubkey2solvable(Pool *pool, Id p, Repodata *data, char *pubkey, int flags)
-{
- unsigned char *pkts, *pkts_orig;
- int pktsl, pl = 0, tag, l, hl;
-
- if (!unarmor(pubkey, &pkts, &pktsl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----"))
- {
- pool_error(pool, 0, "unarmor failure");
- return 0;
- }
- pkts_orig = pkts;
- tag = 6;
- while (pktsl)
- {
- if (tag == 6)
- {
- setutf8string(data, p, SOLVABLE_DESCRIPTION, pubkey);
- pl = parsepubkey(pool->solvables + p, data, pkts, pktsl, flags);
-#ifdef ENABLE_RPMDB
- parsepubkey_rpm(pool->solvables + p, data, pkts, pktsl);
-#endif
- if (!pl || !(flags & ADD_MULTIPLE_PUBKEYS))
- break;
- }
- pkts += pl;
- pktsl -= pl;
- hl = parsepkgheader(pkts, pktsl, &tag, &l);
- if (!hl)
- break;
- pl = l + hl;
- if (tag == 6)
- p = repo_add_solvable(pool->solvables[p].repo);
- }
- solv_free((void *)pkts_orig);
- return 1;
-}
-
-#ifdef ENABLE_RPMDB
-
-int
-repo_add_rpmdb_pubkeys(Repo *repo, int flags)
-{
- Pool *pool = repo->pool;
- Queue q;
- int i;
- char *str;
- Repodata *data;
- const char *rootdir = 0;
- void *state;
-
- data = repo_add_repodata(repo, flags);
- if (flags & REPO_USE_ROOTDIR)
- rootdir = pool_get_rootdir(pool);
- state = rpm_state_create(repo->pool, rootdir);
- queue_init(&q);
- rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
- for (i = 0; i < q.count; i++)
- {
- Id p, p2;
- void *handle;
- unsigned long long itime;
-
- handle = rpm_byrpmdbid(state, q.elements[i]);
- if (!handle)
- continue;
- str = rpm_query(handle, SOLVABLE_DESCRIPTION);
- if (!str)
- continue;
- p = repo_add_solvable(repo);
- if (!pubkey2solvable(pool, p, data, str, flags))
- {
- solv_free(str);
- repo_free_solvable(repo, p, 1);
- continue;
- }
- solv_free(str);
- itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
- for (p2 = p; p2 < pool->nsolvables; p2++)
- {
- if (itime)
- repodata_set_num(data, p2, SOLVABLE_INSTALLTIME, itime);
- if (!repo->rpmdbid)
- repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
- repo->rpmdbid[p2 - repo->start] = q.elements[i];
- }
- }
- queue_free(&q);
- rpm_state_free(state);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-#endif
-
-static char *
-solv_slurp(FILE *fp, int *lenp)
-{
- int l, ll;
- char *buf = 0;
- int bufl = 0;
-
- for (l = 0; ; l += ll)
- {
- if (bufl - l < 4096)
- {
- bufl += 4096;
- buf = solv_realloc(buf, bufl);
- }
- ll = fread(buf + l, 1, bufl - l, fp);
- if (ll < 0)
- {
- buf = solv_free(buf);
- l = 0;
- break;
- }
- if (ll == 0)
- {
- buf[l] = 0; /* always zero-terminate */
- break;
- }
- }
- if (lenp)
- *lenp = l;
- return buf;
-}
-
-Id
-repo_add_pubkey(Repo *repo, const char *keyfile, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- Id p;
- char *buf;
- FILE *fp;
-
- data = repo_add_repodata(repo, flags);
- buf = 0;
- if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, keyfile) : keyfile, "r")) == 0)
- {
- pool_error(pool, -1, "%s: %s", keyfile, strerror(errno));
- return 0;
- }
- if ((buf = solv_slurp(fp, 0)) == 0)
- {
- pool_error(pool, -1, "%s: %s", keyfile, strerror(errno));
- fclose(fp);
- return 0;
- }
- fclose(fp);
- p = repo_add_solvable(repo);
- if (!pubkey2solvable(pool, p, data, buf, flags))
- {
- repo_free_solvable(repo, p, 1);
- solv_free(buf);
- return 0;
- }
- if (!(flags & REPO_NO_LOCATION))
- {
- Id p2;
- for (p2 = p; p2 < pool->nsolvables; p2++)
- repodata_set_location(data, p2, 0, 0, keyfile);
- }
- solv_free(buf);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return p;
-}
-
-static int
-is_sig_packet(unsigned char *sig, int sigl)
-{
- if (!sigl)
- return 0;
- if ((sig[0] & 0x80) == 0 || (sig[0] & 0x40 ? sig[0] & 0x3f : sig[0] >> 2 & 0x0f) != 2)
- return 0;
- return 1;
-}
-
-static int
-is_pubkey_packet(unsigned char *pkt, int pktl)
-{
- if (!pktl)
- return 0;
- if ((pkt[0] & 0x80) == 0 || (pkt[0] & 0x40 ? pkt[0] & 0x3f : pkt[0] >> 2 & 0x0f) != 6)
- return 0;
- return 1;
-}
-
-static void
-add_one_pubkey(Pool *pool, Repo *repo, Repodata *data, unsigned char *pbuf, int pbufl, int flags)
-{
- Id p = repo_add_solvable(repo);
- char *solvversion = pool_tmpjoin(pool, "libsolv-", LIBSOLV_VERSION_STRING, 0);
- char *descr = armor(pbuf, pbufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----", solvversion);
- setutf8string(data, p, SOLVABLE_DESCRIPTION, descr);
- parsepubkey(pool->solvables + p, data, pbuf, pbufl, flags);
-#ifdef ENABLE_RPMDB
- parsepubkey_rpm(pool->solvables + p, data, pbuf, pbufl);
-#endif
-}
-
-int
-repo_add_keyring(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- unsigned char *buf, *p, *pbuf;
- int bufl, l, pl, pbufl;
-
- data = repo_add_repodata(repo, flags);
- buf = (unsigned char *)solv_slurp(fp, &bufl);
- if (buf && !is_pubkey_packet(buf, bufl))
- {
- /* assume ascii armored */
- unsigned char *nbuf = 0, *ubuf;
- int nl, ubufl;
- bufl = 0;
- for (l = 0; (nl = unarmor((char *)buf + l, &ubuf, &ubufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----")) != 0; l += nl)
- {
- /* found another block. concat. */
- nbuf = solv_realloc(nbuf, bufl + ubufl);
- if (ubufl)
- memcpy(nbuf + bufl, ubuf, ubufl);
- bufl += ubufl;
- solv_free(ubuf);
- }
- solv_free(buf);
- buf = nbuf;
- }
- /* now split into pubkey parts, ignoring the packets we don't know */
- pbuf = 0;
- pbufl = 0;
- for (p = buf; bufl; p += pl, bufl -= pl)
- {
- int tag;
- int hl = parsepkgheader(p, bufl, &tag, &pl);
- if (!hl)
- break;
- pl += hl;
- if (tag == 6)
- {
- /* found new pubkey! flush old */
- if (pbufl)
- {
- add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
- pbuf = solv_free(pbuf);
- pbufl = 0;
- }
- }
- if (tag != 6 && !pbufl)
- continue;
- if (tag != 6 && tag != 2 && tag != 13 && tag != 14 && tag != 17)
- continue;
- /* we want that packet. concat. */
- pbuf = solv_realloc(pbuf, pbufl + pl);
- memcpy(pbuf + pbufl, p, pl);
- pbufl += pl;
- }
- if (pbufl)
- add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
- solv_free(pbuf);
- solv_free(buf);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-int
-repo_add_keydir(Repo *repo, const char *keydir, const char *suffix, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i, nent, sl;
- struct dirent **namelist;
- char *rkeydir;
-
- data = repo_add_repodata(repo, flags);
- nent = scandir(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, keydir) : keydir, &namelist, 0, alphasort);
- if (nent == -1)
- return pool_error(pool, -1, "%s: %s", keydir, strerror(errno));
- rkeydir = pool_prepend_rootdir(pool, keydir);
- sl = suffix ? strlen(suffix) : 0;
- for (i = 0; i < nent; i++)
- {
- const char *dn = namelist[i]->d_name;
- int l;
- if (*dn == '.' && !(flags & ADD_KEYDIR_WITH_DOTFILES))
- continue;
- l = strlen(dn);
- if (sl && (l < sl || strcmp(dn + l - sl, suffix) != 0))
- continue;
- repo_add_pubkey(repo, pool_tmpjoin(pool, rkeydir, "/", dn), flags | REPO_REUSE_REPODATA);
- }
- solv_free(rkeydir);
- for (i = 0; i < nent; i++)
- solv_free(namelist[i]);
- solv_free(namelist);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-Solvsig *
-solvsig_create(FILE *fp)
-{
- Solvsig *ss;
- unsigned char *sig;
- int sigl, hl, tag, pktl;
- struct pgpsig pgpsig;
-
- if ((sig = (unsigned char *)solv_slurp(fp, &sigl)) == 0)
- return 0;
- if (!is_sig_packet(sig, sigl))
- {
- /* not a raw sig, check armored */
- unsigned char *nsig;
- if (!unarmor((char *)sig, &nsig, &sigl, "-----BEGIN PGP SIGNATURE-----", "-----END PGP SIGNATURE-----"))
- {
- solv_free(sig);
- return 0;
- }
- solv_free(sig);
- sig = nsig;
- if (!is_sig_packet(sig, sigl))
- {
- solv_free(sig);
- return 0;
- }
- }
- hl = parsepkgheader(sig, sigl, &tag, &pktl);
- if (!hl || tag != 2 || !pktl)
- {
- solv_free(sig);
- return 0;
- }
- pgpsig_init(&pgpsig, sig + hl, pktl);
- if (pgpsig.type != 0 || !pgpsig.haveissuer)
- {
- solv_free(sig);
- return 0;
- }
- ss = solv_calloc(1, sizeof(*ss));
- ss->sigpkt = solv_memdup(sig + hl, pktl);
- ss->sigpktl = pktl;
- solv_free(sig);
- solv_bin2hex(pgpsig.issuer, 8, ss->keyid);
- ss->htype = pgphashalgo2type(pgpsig.hashalgo);
- ss->created = pgpsig.created;
- ss->expires = pgpsig.expires;
- return ss;
-}
-
-void
-solvsig_free(Solvsig *ss)
-{
- solv_free(ss->sigpkt);
- solv_free(ss);
-}
-
-static int
-repo_find_all_pubkeys_cmp(const void *va, const void *vb, void *dp)
-{
- Pool *pool = dp;
- Id a = *(Id *)va;
- Id b = *(Id *)vb;
- /* cannot use evrcmp, as rpm says '0' > 'a' */
- return strcmp(pool_id2str(pool, pool->solvables[b].evr), pool_id2str(pool, pool->solvables[a].evr));
-}
-
-void
-repo_find_all_pubkeys(Repo *repo, const char *keyid, Queue *q)
-{
- Id p;
- Solvable *s;
-
- queue_empty(q);
- if (!keyid)
- return;
- queue_init(q);
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- const char *kidstr, *evr = pool_id2str(s->repo->pool, s->evr);
-
- if (!evr || strncmp(evr, keyid + 8, 8) != 0)
- continue;
- kidstr = solvable_lookup_str(s, PUBKEY_KEYID);
- if (kidstr && !strcmp(kidstr, keyid))
- queue_push(q, p);
- }
- if (q->count > 1)
- solv_sort(q->elements, q->count, sizeof(Id), repo_find_all_pubkeys_cmp, repo->pool);
-}
-
-Id
-repo_find_pubkey(Repo *repo, const char *keyid)
-{
- Queue q;
- Id p;
- queue_init(&q);
- repo_find_all_pubkeys(repo, keyid, &q);
- p = q.count ? q.elements[0] : 0;
- queue_free(&q);
- return p;
-}
-
-#ifdef ENABLE_PGPVRFY
-
-/* warning: does not check key expiry/revokation, same as with gpgv or rpm */
-/* returns the Id of the pubkey that verified the signature */
-Id
-repo_verify_sigdata(Repo *repo, unsigned char *sigdata, int sigdatal, const char *keyid)
-{
- Id p;
- Queue q;
- int i;
-
- if (!sigdata || !keyid)
- return 0;
- queue_init(&q);
- repo_find_all_pubkeys(repo, keyid, &q);
- for (i = 0; i < q.count; i++)
- {
- int pubdatal;
- const unsigned char *pubdata = repo_lookup_binary(repo, q.elements[i], PUBKEY_DATA, &pubdatal);
- if (pubdata && solv_pgpvrfy(pubdata, pubdatal, sigdata, sigdatal))
- break;
- }
- p = i < q.count? q.elements[i] : 0;
- queue_free(&q);
- return p;
-}
-
-Id
-solvsig_verify(Solvsig *ss, Repo *repo, Chksum *chk)
-{
- struct pgpsig pgpsig;
- void *chk2;
- Id p;
-
- if (!chk || solv_chksum_isfinished(chk))
- return 0;
- pgpsig_init(&pgpsig, ss->sigpkt, ss->sigpktl);
- chk2 = solv_chksum_create_clone(chk);
- pgpsig_makesigdata(&pgpsig, ss->sigpkt, ss->sigpktl, 0, 0, 0, 0, chk2);
- solv_chksum_free(chk2, 0);
- if (!pgpsig.sigdata)
- return 0;
- p = repo_verify_sigdata(repo, pgpsig.sigdata, pgpsig.sigdatal, ss->keyid);
- solv_free(pgpsig.sigdata);
- return p;
-}
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (c) 2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include "repo.h"
-#include "chksum.h"
-
-#define ADD_KEYDIR_WITH_DOTFILES (1 << 8)
-#define ADD_WITH_SUBKEYS (1 << 9)
-#define ADD_MULTIPLE_PUBKEYS (1 << 10)
-#define ADD_WITH_KEYSIGNATURES (1 << 11)
-
-extern int repo_add_rpmdb_pubkeys(Repo *repo, int flags);
-extern Id repo_add_pubkey(Repo *repo, const char *keyfile, int flags);
-extern int repo_add_keyring(Repo *repo, FILE *fp, int flags);
-extern int repo_add_keydir(Repo *repo, const char *keydir, const char *suffix, int flags);
-
-/* signature parsing */
-typedef struct _solvsig {
- unsigned char *sigpkt;
- int sigpktl;
- Id htype;
- unsigned int created;
- unsigned int expires;
- char keyid[17];
-} Solvsig;
-
-Solvsig *solvsig_create(FILE *fp);
-void solvsig_free(Solvsig *ss);
-Id solvsig_verify(Solvsig *ss, Repo *repo, Chksum *chk);
-
-Id repo_verify_sigdata(Repo *repo, unsigned char *sigdata, int sigdatal, const char *keyid);
-Id repo_find_pubkey(Repo *repo, const char *keyid);
-void repo_find_all_pubkeys(Repo *repo, const char *keyid, Queue *q);
-
+++ /dev/null
-/*
- * repo_products.c
- *
- * Parses all files below 'proddir'
- * See http://en.opensuse.org/Product_Management/Code11
- *
- *
- * Copyright (c) 2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#define DISABLE_SPLIT
-#include "tools_util.h"
-#include "repo_releasefile_products.h"
-
-#define BUFF_SIZE 8192
-
-struct parsedata {
- Repo *repo;
- struct joindata jd;
-};
-
-static void
-add_releasefile_product(struct parsedata *pd, FILE *fp)
-{
- Repo *repo = pd->repo;
- Pool *pool = repo->pool;
- char buf[BUFF_SIZE];
- Id name = 0;
- Id arch = 0;
- Id version = 0;
- int lnum = 0; /* line number */
- char *ptr, *ptr1;
-
- /* parse /etc/<xyz>-release file */
- while (fgets(buf, sizeof(buf), fp))
- {
- /* remove trailing \n */
- int l = strlen(buf);
- if (l && buf[l - 1] == '\n')
- buf[--l] = 0;
- ++lnum;
-
- if (lnum == 1)
- {
- /* 1st line, <name> [(<arch>)] */
- ptr = strchr(buf, '(');
- if (ptr)
- {
- ptr1 = ptr - 1;
- *ptr++ = 0;
- }
- else
- ptr1 = buf + l - 1;
-
- /* track back until non-blank, non-digit */
- while (ptr1 > buf
- && (*ptr1 == ' ' || isdigit(*ptr1) || *ptr1 == '.'))
- --ptr1;
- *(++ptr1) = 0;
- name = pool_str2id(pool, join2(&pd->jd, "product", ":", buf), 1);
-
- if (ptr)
- {
- /* have arch */
- char *ptr1 = strchr(ptr, ')');
- if (ptr1)
- {
- *ptr1 = 0;
- /* downcase arch */
- ptr1 = ptr;
- while (*ptr1)
- {
- if (isupper(*ptr1))
- *ptr1 = tolower(*ptr1);
- ++ptr1;
- }
- arch = pool_str2id(pool, ptr, 1);
- }
- }
- }
- else if (strncmp(buf, "VERSION", 7) == 0)
- {
- ptr = strchr(buf + 7, '=');
- if (ptr)
- {
- while (*++ptr == ' ')
- ;
- version = makeevr(pool, ptr);
- }
- }
- }
- if (name)
- {
- Solvable *s = pool_id2solvable(pool, repo_add_solvable(repo));
- s->name = name;
- s->evr = version ? version : ID_EMPTY;
- s->arch = arch ? arch : ARCH_NOARCH;
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- }
-}
-
-
-int
-repo_add_releasefile_products(Repo *repo, const char *dirpath, int flags)
-{
- DIR *dir;
- struct dirent *entry;
- FILE *fp;
- char *fullpath;
- struct parsedata pd;
-
- if (!dirpath)
- dirpath = "/etc";
- if (flags & REPO_USE_ROOTDIR)
- dirpath = pool_prepend_rootdir(repo->pool, dirpath);
- dir = opendir(dirpath);
- if (!dir)
- {
- if (flags & REPO_USE_ROOTDIR)
- solv_free((char *)dirpath);
- return 0;
- }
-
- memset(&pd, 0, sizeof(pd));
- pd.repo = repo;
- while ((entry = readdir(dir)))
- {
- int len = strlen(entry->d_name);
- if (len > 8 && !strcmp(entry->d_name + len - 8, "-release"))
- {
- /* skip /etc/lsb-release, thats not a product per-se */
- if (strcmp(entry->d_name, "lsb-release") == 0)
- continue;
- fullpath = join2(&pd.jd, dirpath, "/", entry->d_name);
- if ((fp = fopen(fullpath, "r")) == 0)
- {
- pool_error(repo->pool, 0, "%s: %s", fullpath, strerror(errno));
- continue;
- }
- add_releasefile_product(&pd, fp);
- fclose(fp);
- }
- }
- closedir(dir);
- join_freemem(&pd.jd);
- if (flags & REPO_USE_ROOTDIR)
- solv_free((char *)dirpath);
-
- if (!(flags & REPO_NO_INTERNALIZE) && (flags & REPO_REUSE_REPODATA) != 0)
- repodata_internalize(repo_last_repodata(repo));
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_releasefile_products(Repo *repo, const char *dirpath, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define DO_ARRAY 1
-
-#define _GNU_SOURCE
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <expat.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "repo_repomdxml.h"
-
-/*
-<repomd>
-
- <!-- these tags are available in create repo > 0.9.6 -->
- <revision>timestamp_or_arbitrary_user_supplied_string</revision>
- <tags>
- <content>opensuse</content>
- <content>i386</content>
- <content>other string</content>
- <distro cpeid="cpe://o:opensuse_project:opensuse:11">openSUSE 11.0</distro>
- </tags>
- <!-- end -->
-
- <data type="primary">
- <location href="repodata/primary.xml.gz"/>
- <checksum type="sha">e9162516fa25fec8d60caaf4682d2e49967786cc</checksum>
- <timestamp>1215708444</timestamp>
- <open-checksum type="sha">c796c48184cd5abc260e4ba929bdf01be14778a7</open-checksum>
- </data>
- <data type="filelists">
- <location href="repodata/filelists.xml.gz"/>
- <checksum type="sha">1c638295c49e9707c22810004ebb0799791fcf45</checksum>
- <timestamp>1215708445</timestamp>
- <open-checksum type="sha">54a40d5db3df0813b8acbe58cea616987eb9dc16</open-checksum>
- </data>
- <data type="other">
- <location href="repodata/other.xml.gz"/>
- <checksum type="sha">a81ef39eaa70e56048f8351055119d8c82af2491</checksum>
- <timestamp>1215708447</timestamp>
- <open-checksum type="sha">4d1ee867c8864025575a2fb8fde3b85371d51978</open-checksum>
- </data>
- <data type="deltainfo">
- <location href="repodata/deltainfo.xml.gz"/>
- <checksum type="sha">5880cfa5187026a24a552d3c0650904a44908c28</checksum>
- <timestamp>1215708447</timestamp>
- <open-checksum type="sha">7c964a2c3b17df5bfdd962c3be952c9ca6978d8b</open-checksum>
- </data>
- <data type="updateinfo">
- <location href="repodata/updateinfo.xml.gz"/>
- <checksum type="sha">4097f7e25c7bb0770ae31b2471a9c8c077ee904b</checksum>
- <timestamp>1215708447</timestamp>
- <open-checksum type="sha">24f8252f3dd041e37e7c3feb2d57e02b4422d316</open-checksum>
- </data>
- <data type="diskusage">
- <location href="repodata/diskusage.xml.gz"/>
- <checksum type="sha">4097f7e25c7bb0770ae31b2471a9c8c077ee904b</checksum>
- <timestamp>1215708447</timestamp>
- <open-checksum type="sha">24f8252f3dd041e37e7c3feb2d57e02b4422d316</open-checksum>
- </data>
-</repomd>
-
-support also extension suseinfo format
-<suseinfo>
- <expire>timestamp</expire>
- <products>
- <id>...</id>
- </products>
- <kewwords>
- <k>...</k>
- </keywords>
-</suseinfo>
-
-*/
-
-enum state {
- STATE_START,
- /* extension tags */
- STATE_SUSEINFO,
- STATE_EXPIRE,
- STATE_KEYWORDS,
- STATE_KEYWORD,
-
- /* normal repomd.xml */
- STATE_REPOMD,
- STATE_REVISION,
- STATE_TAGS,
- STATE_REPO,
- STATE_CONTENT,
- STATE_DISTRO,
- STATE_UPDATES,
- STATE_DATA,
- STATE_LOCATION,
- STATE_CHECKSUM,
- STATE_TIMESTAMP,
- STATE_OPENCHECKSUM,
- STATE_SIZE,
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-/* !! must be sorted by first column !! */
-static struct stateswitch stateswitches[] = {
- /* suseinfo tags */
- { STATE_START, "repomd", STATE_REPOMD, 0 },
- { STATE_START, "suseinfo", STATE_SUSEINFO, 0 },
- /* we support the tags element in suseinfo in case
- createrepo version does not support it yet */
- { STATE_SUSEINFO, "tags", STATE_TAGS, 0 },
- { STATE_SUSEINFO, "expire", STATE_EXPIRE, 1 },
- { STATE_SUSEINFO, "keywords", STATE_KEYWORDS, 0 },
- /* keywords is the suse extension equivalent of
- tags/content when this one was not yet available.
- therefore we parse both */
- { STATE_KEYWORDS, "k", STATE_KEYWORD, 1 },
- /* standard tags */
- { STATE_REPOMD, "revision", STATE_REVISION, 1 },
- { STATE_REPOMD, "tags", STATE_TAGS, 0 },
- { STATE_REPOMD, "data", STATE_DATA, 0 },
-
- { STATE_TAGS, "repo", STATE_REPO, 1 },
- { STATE_TAGS, "content", STATE_CONTENT, 1 },
- { STATE_TAGS, "distro", STATE_DISTRO, 1 },
- /* this tag is only valid in suseinfo.xml for now */
- { STATE_TAGS, "updates", STATE_UPDATES, 1 },
-
- { STATE_DATA, "location", STATE_LOCATION, 0 },
- { STATE_DATA, "checksum", STATE_CHECKSUM, 1 },
- { STATE_DATA, "timestamp", STATE_TIMESTAMP, 1 },
- { STATE_DATA, "open-checksum", STATE_OPENCHECKSUM, 1 },
- { STATE_DATA, "size", STATE_SIZE, 1 },
- { NUMSTATES }
-};
-
-
-struct parsedata {
- int ret;
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
- Pool *pool;
- Repo *repo;
- Repodata *data;
-
- XML_Parser *parser;
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
- int timestamp;
- /* handles for collection
- structures */
- /* repo updates */
- Id ruhandle;
- /* repo products */
- Id rphandle;
- /* repo data handle */
- Id rdhandle;
-
- Id chksumtype;
-};
-
-/*
- * find attribute
- */
-
-static inline const char *
-find_attr(const char *txt, const char **atts)
-{
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, txt))
- return atts[1];
- }
- return 0;
-}
-
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- /*Pool *pool = pd->pool;*/
- struct stateswitch *sw;
-
-#if 0
- fprintf(stderr, "start: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
-
- pd->depth++;
- if (!pd->swtab[pd->state])
- return;
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
- if (!strcmp(sw->ename, name))
- break;
-
- if (sw->from != pd->state)
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#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_REPOMD:
- {
- const char *updstr;
-
- /* this should be OBSOLETE soon */
- updstr = find_attr("updates", atts);
- if (updstr)
- {
- char *value = solv_strdup(updstr);
- char *fvalue = value; /* save the first */
- while (value)
- {
- char *p = strchr(value, ',');
- if (*p)
- *p++ = 0;
- if (*value)
- repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_UPDATES, value);
- value = p;
- }
- free(fvalue);
- }
- break;
- }
- case STATE_DISTRO:
- {
- /* this is extra metadata about the product this repository
- was designed for */
- const char *cpeid = find_attr("cpeid", atts);
- pd->rphandle = repodata_new_handle(pd->data);
- /* set the cpeid for the product
- the label is set in the content of the tag */
- if (cpeid)
- repodata_set_poolstr(pd->data, pd->rphandle, REPOSITORY_PRODUCT_CPEID, cpeid);
- break;
- }
- case STATE_UPDATES:
- {
- /* this is extra metadata about the product this repository
- was designed for */
- const char *cpeid = find_attr("cpeid", atts);
- pd->ruhandle = repodata_new_handle(pd->data);
- /* set the cpeid for the product
- the label is set in the content of the tag */
- if (cpeid)
- repodata_set_poolstr(pd->data, pd->ruhandle, REPOSITORY_PRODUCT_CPEID, cpeid);
- break;
- }
- case STATE_DATA:
- {
- const char *type= find_attr("type", atts);
- pd->rdhandle = repodata_new_handle(pd->data);
- if (type)
- repodata_set_poolstr(pd->data, pd->rdhandle, REPOSITORY_REPOMD_TYPE, type);
- break;
- }
- case STATE_LOCATION:
- {
- const char *href = find_attr("href", atts);
- if (href)
- repodata_set_str(pd->data, pd->rdhandle, REPOSITORY_REPOMD_LOCATION, href);
- break;
- }
- case STATE_CHECKSUM:
- case STATE_OPENCHECKSUM:
- {
- const char *type= find_attr("type", atts);
- pd->chksumtype = type && *type ? solv_chksum_str2type(type) : 0;
- if (!pd->chksumtype)
- pd->ret = pool_error(pd->pool, -1, "line %d: unknown checksum type: %s", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), type ? type : "NULL");
- break;
- }
- default:
- break;
- }
- return;
-}
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- /* Pool *pool = pd->pool; */
-
-#if 0
- fprintf(stderr, "endElement: %s\n", name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
-#if 0
- fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-#endif
- return;
- }
-
- pd->depth--;
- pd->statedepth--;
- switch (pd->state)
- {
- case STATE_REPOMD:
- if (pd->timestamp > 0)
- repodata_set_num(pd->data, SOLVID_META, REPOSITORY_TIMESTAMP, pd->timestamp);
- break;
- case STATE_DATA:
- if (pd->rdhandle)
- repodata_add_flexarray(pd->data, SOLVID_META, REPOSITORY_REPOMD, pd->rdhandle);
- pd->rdhandle = 0;
- break;
-
- case STATE_CHECKSUM:
- case STATE_OPENCHECKSUM:
- if (!pd->chksumtype)
- break;
- if (strlen(pd->content) != 2 * solv_chksum_len(pd->chksumtype))
- pd->ret = pool_error(pd->pool, -1, "line %d: invalid checksum length for %s", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), solv_chksum_type2str(pd->chksumtype));
- else
- repodata_set_checksum(pd->data, pd->rdhandle, pd->state == STATE_CHECKSUM ? REPOSITORY_REPOMD_CHECKSUM : REPOSITORY_REPOMD_OPENCHECKSUM, pd->chksumtype, pd->content);
- break;
-
- case STATE_TIMESTAMP:
- {
- /**
- * we want to look for the newest timestamp
- * of all resources to save it as the time
- * the metadata was generated
- */
- int timestamp = atoi(pd->content);
- if (timestamp)
- repodata_set_num(pd->data, pd->rdhandle, REPOSITORY_REPOMD_TIMESTAMP, timestamp);
- if (timestamp > pd->timestamp)
- pd->timestamp = timestamp;
- break;
- }
- case STATE_EXPIRE:
- {
- int expire = atoi(pd->content);
- if (expire > 0)
- repodata_set_num(pd->data, SOLVID_META, REPOSITORY_EXPIRE, expire);
- break;
- }
- /* repomd.xml content and suseinfo.xml keywords are equivalent */
- case STATE_CONTENT:
- case STATE_KEYWORD:
- if (*pd->content)
- repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_KEYWORDS, pd->content);
- break;
- case STATE_REVISION:
- if (*pd->content)
- repodata_set_str(pd->data, SOLVID_META, REPOSITORY_REVISION, pd->content);
- break;
- case STATE_DISTRO:
- /* distro tag is used in repomd.xml to say the product this repo is
- made for */
- if (*pd->content)
- repodata_set_str(pd->data, pd->rphandle, REPOSITORY_PRODUCT_LABEL, pd->content);
- repodata_add_flexarray(pd->data, SOLVID_META, REPOSITORY_DISTROS, pd->rphandle);
- break;
- case STATE_UPDATES:
- /* updates tag is used in suseinfo.xml to say the repo updates a product
- however it s not yet a tag standarized for repomd.xml */
- if (*pd->content)
- repodata_set_str(pd->data, pd->ruhandle, REPOSITORY_PRODUCT_LABEL, pd->content);
- repodata_add_flexarray(pd->data, SOLVID_META, REPOSITORY_UPDATES, pd->ruhandle);
- break;
- case STATE_REPO:
- if (*pd->content)
- repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_REPOID, pd->content);
- break;
- case STATE_SIZE:
- if (*pd->content)
- repodata_set_num(pd->data, pd->rdhandle, REPOSITORY_REPOMD_SIZE, strtoull(pd->content, 0, 10));
- break;
- default:
- break;
- }
-
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
-
- return;
-}
-
-
-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
-
-int
-repo_add_repomdxml(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- struct parsedata pd;
- Repodata *data;
- char buf[BUFF_SIZE];
- int i, l;
- struct stateswitch *sw;
- XML_Parser parser;
-
- data = repo_add_repodata(repo, flags);
-
- memset(&pd, 0, sizeof(pd));
- pd.timestamp = 0;
- 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.repo = repo;
- pd.data = data;
-
- pd.content = malloc(256);
- pd.acontent = 256;
- pd.lcontent = 0;
- parser = XML_ParserCreate(NULL);
- XML_SetUserData(parser, &pd);
- pd.parser = &parser;
- 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)
- {
- pd.ret = pool_error(pool, -1, "repo_repomdxml: %s at line %u:%u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- break;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
-
- free(pd.content);
- return pd.ret;
-}
-
-/* EOF */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_repomdxml(Repo *repo, FILE *fp, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2007-2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo_rpmdb
- *
- * convert rpm db to repo
- *
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <assert.h>
-#include <stdint.h>
-#include <errno.h>
-
-#include <rpm/rpmio.h>
-#include <rpm/rpmpgp.h>
-#ifndef RPM5
-#include <rpm/header.h>
-#endif
-#include <rpm/rpmdb.h>
-
-#ifndef DB_CREATE
-# if defined(SUSE) || defined(HAVE_RPM_DB_H)
-# include <rpm/db.h>
-# else
-# include <db.h>
-# endif
-#endif
-
-#include "pool.h"
-#include "repo.h"
-#include "hash.h"
-#include "util.h"
-#include "queue.h"
-#include "chksum.h"
-#include "repo_rpmdb.h"
-#include "repo_solv.h"
-#ifdef ENABLE_COMPLEX_DEPS
-#include "pool_parserpmrichdep.h"
-#endif
-
-/* 3: added triggers */
-/* 4: fixed triggers */
-/* 5: fixed checksum copying */
-#define RPMDB_COOKIE_VERSION 5
-
-#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_BUILDHOST 1007
-#define TAG_INSTALLTIME 1008
-#define TAG_SIZE 1009
-#define TAG_DISTRIBUTION 1010
-#define TAG_VENDOR 1011
-#define TAG_LICENSE 1014
-#define TAG_PACKAGER 1015
-#define TAG_GROUP 1016
-#define TAG_URL 1020
-#define TAG_ARCH 1022
-#define TAG_FILESIZES 1028
-#define TAG_FILEMODES 1030
-#define TAG_FILEMD5S 1035
-#define TAG_FILELINKTOS 1036
-#define TAG_FILEFLAGS 1037
-#define TAG_SOURCERPM 1044
-#define TAG_PROVIDENAME 1047
-#define TAG_REQUIREFLAGS 1048
-#define TAG_REQUIRENAME 1049
-#define TAG_REQUIREVERSION 1050
-#define TAG_NOSOURCE 1051
-#define TAG_NOPATCH 1052
-#define TAG_CONFLICTFLAGS 1053
-#define TAG_CONFLICTNAME 1054
-#define TAG_CONFLICTVERSION 1055
-#define TAG_TRIGGERNAME 1066
-#define TAG_TRIGGERVERSION 1067
-#define TAG_TRIGGERFLAGS 1068
-#define TAG_CHANGELOGTIME 1080
-#define TAG_CHANGELOGNAME 1081
-#define TAG_CHANGELOGTEXT 1082
-#define TAG_OBSOLETENAME 1090
-#define TAG_FILEDEVICES 1095
-#define TAG_FILEINODES 1096
-#define TAG_SOURCEPACKAGE 1106
-#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_PAYLOADFORMAT 1124
-#define TAG_PATCHESNAME 1133
-#define TAG_FILECOLORS 1140
-#define TAG_OLDSUGGESTSNAME 1156
-#define TAG_OLDSUGGESTSVERSION 1157
-#define TAG_OLDSUGGESTSFLAGS 1158
-#define TAG_OLDENHANCESNAME 1159
-#define TAG_OLDENHANCESVERSION 1160
-#define TAG_OLDENHANCESFLAGS 1161
-
-/* rpm5 tags */
-#define TAG_DISTEPOCH 1218
-
-/* rpm4 tags */
-#define TAG_LONGFILESIZES 5008
-#define TAG_LONGSIZE 5009
-#define TAG_RECOMMENDNAME 5046
-#define TAG_RECOMMENDVERSION 5047
-#define TAG_RECOMMENDFLAGS 5048
-#define TAG_SUGGESTNAME 5049
-#define TAG_SUGGESTVERSION 5050
-#define TAG_SUGGESTFLAGS 5051
-#define TAG_SUPPLEMENTNAME 5052
-#define TAG_SUPPLEMENTVERSION 5053
-#define TAG_SUPPLEMENTFLAGS 5054
-#define TAG_ENHANCENAME 5055
-#define TAG_ENHANCEVERSION 5056
-#define TAG_ENHANCEFLAGS 5057
-
-/* signature tags */
-#define TAG_SIGBASE 256
-#define TAG_SIGMD5 (TAG_SIGBASE + 5)
-#define TAG_SHA1HEADER (TAG_SIGBASE + 13)
-
-#define SIGTAG_SIZE 1000
-#define SIGTAG_PGP 1002 /* RSA signature */
-#define SIGTAG_MD5 1004 /* header+payload md5 checksum */
-#define SIGTAG_GPG 1005 /* DSA signature */
-
-#define DEP_LESS (1 << 1)
-#define DEP_GREATER (1 << 2)
-#define DEP_EQUAL (1 << 3)
-#define DEP_STRONG (1 << 27)
-#define DEP_PRE_IN ((1 << 6) | (1 << 9) | (1 << 10))
-#define DEP_PRE_UN ((1 << 6) | (1 << 11) | (1 << 12))
-
-#define FILEFLAG_GHOST (1 << 6)
-
-
-#ifdef RPM5
-# define RPM_INDEX_SIZE 4 /* just the rpmdbid */
-#else
-# define RPM_INDEX_SIZE 8 /* rpmdbid + array index */
-#endif
-
-
-typedef struct rpmhead {
- int cnt;
- int dcnt;
- unsigned char *dp;
- int forcebinary; /* sigh, see rh#478907 */
- unsigned char data[1];
-} RpmHead;
-
-
-static inline unsigned char *
-headfindtag(RpmHead *h, int tag)
-{
- unsigned int i;
- 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])
- return d;
- return 0;
-}
-
-static int
-headexists(RpmHead *h, int tag)
-{
- return headfindtag(h, tag) ? 1 : 0;
-}
-
-static unsigned int *
-headint32array(RpmHead *h, int tag, int *cnt)
-{
- unsigned int i, o, *r;
- unsigned char *d = headfindtag(h, tag);
-
- if (!d || 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 = solv_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;
-}
-
-/* returns the first entry of an integer array */
-static unsigned int
-headint32(RpmHead *h, int tag)
-{
- unsigned int i, o;
- unsigned char *d = headfindtag(h, tag);
-
- if (!d || 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 (i == 0 || o + 4 * i > h->dcnt)
- return 0;
- d = h->dp + o;
- return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
-}
-
-static unsigned long long *
-headint64array(RpmHead *h, int tag, int *cnt)
-{
- unsigned int i, o;
- unsigned long long *r;
- unsigned char *d = headfindtag(h, tag);
-
- if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 5)
- 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 + 8 * i > h->dcnt)
- return 0;
- d = h->dp + o;
- r = solv_calloc(i ? i : 1, sizeof(unsigned long long));
- if (cnt)
- *cnt = i;
- for (o = 0; o < i; o++, d += 8)
- {
- unsigned int x = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
- r[o] = (unsigned long long)x << 32 | (d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
- }
- return r;
-}
-
-/* returns the first entry of an 64bit integer array */
-static unsigned long long
-headint64(RpmHead *h, int tag)
-{
- unsigned int i, o;
- unsigned char *d = headfindtag(h, tag);
- if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 5)
- return 0;
- o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
- i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
- if (i == 0 || o + 8 * i > h->dcnt)
- return 0;
- d = h->dp + o;
- i = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
- return (unsigned long long)i << 32 | (d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
-}
-
-static unsigned int *
-headint16array(RpmHead *h, int tag, int *cnt)
-{
- unsigned int i, o, *r;
- unsigned char *d = headfindtag(h, tag);
-
- if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 3)
- 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 = solv_calloc(i ? i : 1, sizeof(unsigned int));
- if (cnt)
- *cnt = i;
- for (o = 0; o < i; o++, d += 2)
- r[o] = d[0] << 8 | d[1];
- return r;
-}
-
-static char *
-headstring(RpmHead *h, int tag)
-{
- unsigned int o;
- unsigned char *d = headfindtag(h, tag);
- /* 6: STRING, 9: I18NSTRING */
- if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || (d[7] != 6 && d[7] != 9))
- return 0;
- o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
- if (o >= h->dcnt)
- return 0;
- return (char *)h->dp + o;
-}
-
-static char **
-headstringarray(RpmHead *h, int tag, int *cnt)
-{
- unsigned int i, o;
- unsigned char *d = headfindtag(h, tag);
- char **r;
-
- if (!d || 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 = solv_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)
- {
- solv_free(r);
- return 0;
- }
- }
- return r;
-}
-
-static unsigned char *
-headbinary(RpmHead *h, int tag, unsigned int *sizep)
-{
- unsigned int i, o;
- unsigned char *d = headfindtag(h, tag);
- if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 7)
- 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 > h->dcnt || o + i < o || o + i > h->dcnt)
- return 0;
- if (sizep)
- *sizep = i;
- return h->dp + o;
-}
-
-static char *headtoevr(RpmHead *h)
-{
- unsigned int epoch;
- char *version, *v;
- char *release;
- char *evr;
- char *distepoch;
-
- version = headstring(h, TAG_VERSION);
- release = headstring(h, TAG_RELEASE);
- epoch = headint32(h, TAG_EPOCH);
- if (!version || !release)
- {
- fprintf(stderr, "headtoevr: bad rpm header\n");
- return 0;
- }
- for (v = version; *v >= '0' && *v <= '9'; v++)
- ;
- if (epoch || (v != version && *v == ':'))
- {
- char epochbuf[11]; /* 32bit decimal will fit in */
- sprintf(epochbuf, "%u", epoch);
- evr = solv_malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
- sprintf(evr, "%s:%s-%s", epochbuf, version, release);
- }
- else
- {
- evr = solv_malloc(strlen(version) + 1 + strlen(release) + 1);
- sprintf(evr, "%s-%s", version, release);
- }
- distepoch = headstring(h, TAG_DISTEPOCH);
- if (distepoch && *distepoch)
- {
- int l = strlen(evr);
- evr = solv_realloc(evr, l + strlen(distepoch) + 2);
- evr[l++] = ':';
- strcpy(evr + l, distepoch);
- }
- return evr;
-}
-
-
-static void
-setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
-{
- if (str[solv_validutf8(str)])
- {
- char *ustr = solv_latin1toutf8(str); /* not utf8, assume latin1 */
- repodata_set_str(repodata, handle, tag, ustr);
- solv_free(ustr);
- }
- else
- repodata_set_str(repodata, handle, tag, str);
-}
-
-/*
- * strong: 0: ignore strongness
- * 1: filter to strong
- * 2: filter to weak
- */
-static unsigned int
-makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf, int flags)
-{
- char **n, **v;
- unsigned int *f;
- int i, cc, nc, vc, fc;
- int haspre, premask;
- unsigned int olddeps;
- Id *ida;
- int strong = 0;
-
- n = headstringarray(rpmhead, tagn, &nc);
- if (!n)
- {
- switch (tagn)
- {
- case TAG_SUGGESTNAME:
- tagn = TAG_OLDSUGGESTSNAME;
- tagv = TAG_OLDSUGGESTSVERSION;
- tagf = TAG_OLDSUGGESTSFLAGS;
- strong = -1;
- break;
- case TAG_ENHANCENAME:
- tagn = TAG_OLDENHANCESNAME;
- tagv = TAG_OLDENHANCESVERSION;
- tagf = TAG_OLDENHANCESFLAGS;
- strong = -1;
- break;
- case TAG_RECOMMENDNAME:
- tagn = TAG_OLDSUGGESTSNAME;
- tagv = TAG_OLDSUGGESTSVERSION;
- tagf = TAG_OLDSUGGESTSFLAGS;
- strong = 1;
- break;
- case TAG_SUPPLEMENTNAME:
- tagn = TAG_OLDENHANCESNAME;
- tagv = TAG_OLDENHANCESVERSION;
- tagf = TAG_OLDENHANCESFLAGS;
- strong = 1;
- break;
- default:
- return 0;
- }
- n = headstringarray(rpmhead, tagn, &nc);
- }
- if (!n || !nc)
- return 0;
- vc = fc = 0;
- v = headstringarray(rpmhead, tagv, &vc);
- f = headint32array(rpmhead, tagf, &fc);
- if (!v || !f || nc != vc || nc != fc)
- {
- char *pkgname = rpm_query(rpmhead, 0);
- pool_error(pool, 0, "bad dependency entries for %s: %d %d %d", pkgname ? pkgname : "<NULL>", nc, vc, fc);
- solv_free(pkgname);
- solv_free(n);
- solv_free(v);
- solv_free(f);
- return 0;
- }
-
- cc = nc;
- haspre = 0; /* add no prereq marker */
- premask = tagn == TAG_REQUIRENAME ? DEP_PRE_IN | DEP_PRE_UN : 0;
- if ((flags & RPM_ADD_NO_RPMLIBREQS) || strong)
- {
- /* we do filtering */
- cc = 0;
- for (i = 0; i < nc; i++)
- {
- if (strong && (f[i] & DEP_STRONG) != (strong < 0 ? 0 : DEP_STRONG))
- continue;
- if ((flags & RPM_ADD_NO_RPMLIBREQS) != 0)
- if (!strncmp(n[i], "rpmlib(", 7))
- continue;
- if ((f[i] & premask) != 0)
- haspre = 1;
- cc++;
- }
- }
- else if (premask)
- {
- /* no filtering, just look for the first prereq */
- for (i = 0; i < nc; i++)
- if ((f[i] & premask) != 0)
- {
- haspre = 1;
- break;
- }
- }
- if (cc == 0)
- {
- solv_free(n);
- solv_free(v);
- solv_free(f);
- return 0;
- }
- cc += haspre; /* add slot for the prereq marker */
- olddeps = repo_reserve_ids(repo, 0, cc);
- ida = repo->idarraydata + olddeps;
- for (i = 0; ; i++)
- {
- Id id;
- if (i == nc)
- {
- if (haspre != 1)
- break;
- haspre = 2; /* pass two: prereqs */
- i = 0;
- *ida++ = SOLVABLE_PREREQMARKER;
- }
- if (strong && (f[i] & DEP_STRONG) != (strong < 0 ? 0 : DEP_STRONG))
- continue;
- if (haspre)
- {
- if (haspre == 1 && (f[i] & premask) != 0)
- continue;
- if (haspre == 2 && (f[i] & premask) == 0)
- continue;
- }
- if ((flags & RPM_ADD_NO_RPMLIBREQS) != 0)
- if (!strncmp(n[i], "rpmlib(", 7))
- continue;
-#ifdef ENABLE_COMPLEX_DEPS
- if ((f[i] & (DEP_LESS|DEP_EQUAL|DEP_GREATER)) == 0 && n[i][0] == '(')
- {
- id = pool_parserpmrichdep(pool, n[i]);
- if (id)
- *ida++ = id;
- else
- cc--;
- continue;
- }
-#endif
- id = pool_str2id(pool, n[i], 1);
- if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
- {
- Id evr;
- int fl = 0;
- if ((f[i] & DEP_LESS) != 0)
- fl |= REL_LT;
- if ((f[i] & DEP_EQUAL) != 0)
- fl |= REL_EQ;
- if ((f[i] & DEP_GREATER) != 0)
- fl |= REL_GT;
- if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
- evr = pool_str2id(pool, v[i] + 2, 1);
- else
- evr = pool_str2id(pool, v[i], 1);
- id = pool_rel2id(pool, id, evr, fl, 1);
- }
- *ida++ = id;
- }
- *ida++ = 0;
- repo->idarraysize += cc + 1;
- solv_free(n);
- solv_free(v);
- solv_free(f);
- return olddeps;
-}
-
-
-static void
-adddudata(Repodata *data, Id handle, RpmHead *rpmhead, char **dn, unsigned int *di, int fc, int dc)
-{
- Id did;
- int i, fszc;
- unsigned int *fkb, *fn, *fsz, *fm, *fino;
- unsigned long long *fsz64;
- unsigned int inotest[256], inotestok;
-
- if (!fc)
- return;
- if ((fsz64 = headint64array(rpmhead, TAG_LONGFILESIZES, &fszc)) != 0)
- {
- /* convert to kbyte */
- fsz = solv_malloc2(fszc, sizeof(*fsz));
- for (i = 0; i < fszc; i++)
- fsz[i] = fsz64[i] ? fsz64[i] / 1024 + 1 : 0;
- solv_free(fsz64);
- }
- else if ((fsz = headint32array(rpmhead, TAG_FILESIZES, &fszc)) != 0)
- {
- /* convert to kbyte */
- for (i = 0; i < fszc; i++)
- if (fsz[i])
- fsz[i] = fsz[i] / 1024 + 1;
- }
- else
- return;
- if (fc != fszc)
- {
- solv_free(fsz);
- return;
- }
-
- /* stupid rpm records sizes of directories, so we have to check the mode */
- fm = headint16array(rpmhead, TAG_FILEMODES, &fszc);
- if (!fm || fc != fszc)
- {
- solv_free(fsz);
- solv_free(fm);
- return;
- }
- fino = headint32array(rpmhead, TAG_FILEINODES, &fszc);
- if (!fino || fc != fszc)
- {
- solv_free(fsz);
- solv_free(fm);
- solv_free(fino);
- return;
- }
-
- /* kill hardlinked entries */
- inotestok = 0;
- if (fc < sizeof(inotest))
- {
- /* quick test just hashing the inode numbers */
- memset(inotest, 0, sizeof(inotest));
- for (i = 0; i < fc; i++)
- {
- int off, bit;
- if (fsz[i] == 0 || !S_ISREG(fm[i]))
- continue; /* does not matter */
- off = (fino[i] >> 5) & (sizeof(inotest)/sizeof(*inotest) - 1);
- bit = 1 << (fino[i] & 31);
- if ((inotest[off] & bit) != 0)
- break;
- inotest[off] |= bit;
- }
- if (i == fc)
- inotestok = 1; /* no conflict found */
- }
- if (!inotestok)
- {
- /* hardlinked files are possible, check ino/dev pairs */
- unsigned int *fdev = headint32array(rpmhead, TAG_FILEDEVICES, &fszc);
- unsigned int *fx, j;
- unsigned int mask, hash, hh;
- if (!fdev || fc != fszc)
- {
- solv_free(fsz);
- solv_free(fm);
- solv_free(fdev);
- solv_free(fino);
- return;
- }
- mask = fc;
- while ((mask & (mask - 1)) != 0)
- mask = mask & (mask - 1);
- mask <<= 2;
- if (mask > sizeof(inotest)/sizeof(*inotest))
- fx = solv_calloc(mask, sizeof(unsigned int));
- else
- {
- fx = inotest;
- memset(fx, 0, mask * sizeof(unsigned int));
- }
- mask--;
- for (i = 0; i < fc; i++)
- {
- if (fsz[i] == 0 || !S_ISREG(fm[i]))
- continue;
- hash = (fino[i] + fdev[i] * 31) & mask;
- hh = 7;
- while ((j = fx[hash]) != 0)
- {
- if (fino[j - 1] == fino[i] && fdev[j - 1] == fdev[i])
- {
- fsz[i] = 0; /* kill entry */
- break;
- }
- hash = (hash + hh++) & mask;
- }
- if (!j)
- fx[hash] = i + 1;
- }
- if (fx != inotest)
- solv_free(fx);
- solv_free(fdev);
- }
- solv_free(fino);
-
- /* sum up inode count and kbytes for each directory */
- fn = solv_calloc(dc, sizeof(unsigned int));
- fkb = solv_calloc(dc, sizeof(unsigned int));
- for (i = 0; i < fc; i++)
- {
- if (di[i] >= dc)
- continue; /* corrupt entry */
- fn[di[i]]++;
- if (fsz[i] == 0 || !S_ISREG(fm[i]))
- continue;
- fkb[di[i]] += fsz[i];
- }
- solv_free(fsz);
- solv_free(fm);
- /* commit */
- for (i = 0; i < dc; i++)
- {
- if (!fn[i])
- continue;
- if (!*dn[i])
- {
- Solvable *s = data->repo->pool->solvables + handle;
- if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
- did = repodata_str2dir(data, "/usr/src", 1);
- else
- continue; /* work around rpm bug */
- }
- else
- did = repodata_str2dir(data, dn[i], 1);
- repodata_add_dirnumnum(data, handle, SOLVABLE_DISKUSAGE, did, fkb[i], fn[i]);
- }
- solv_free(fn);
- solv_free(fkb);
-}
-
-static int
-is_filtered(const char *dir)
-{
- if (!dir)
- return 1;
- /* the dirs always have a trailing / in rpm */
- if (strstr(dir, "bin/"))
- return 0;
- if (!strncmp(dir, "/etc/", 5))
- return 0;
- if (!strcmp(dir, "/usr/lib/"))
- return 2;
- return 1;
-}
-
-static void
-addfilelist(Repodata *data, Id handle, RpmHead *rpmhead, int flags)
-{
- char **bn;
- char **dn;
- unsigned int *di;
- int bnc, dnc, dic;
- int i;
- Id lastdid = 0;
- unsigned int lastdii = -1;
- int lastfiltered = 0;
-
- if (!data)
- return;
- bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
- if (!bn)
- return;
- dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
- if (!dn)
- {
- solv_free(bn);
- return;
- }
- di = headint32array(rpmhead, TAG_DIRINDEXES, &dic);
- if (!di)
- {
- solv_free(bn);
- solv_free(dn);
- return;
- }
- if (bnc != dic)
- {
- pool_error(data->repo->pool, 0, "bad filelist");
- return;
- }
-
- adddudata(data, handle, rpmhead, dn, di, bnc, dnc);
-
- for (i = 0; i < bnc; i++)
- {
- Id did;
- char *b = bn[i];
-
- if (di[i] == lastdii)
- did = lastdid;
- else
- {
- if (di[i] >= dnc)
- continue; /* corrupt entry */
- lastdii = di[i];
- if ((flags & RPM_ADD_FILTERED_FILELIST) != 0)
- {
- lastfiltered = is_filtered(dn[di[i]]);
- if (lastfiltered == 1)
- continue;
- }
- did = repodata_str2dir(data, dn[lastdii], 1);
- if (!did)
- did = repodata_str2dir(data, "/", 1);
- lastdid = did;
- }
- if (b && *b == '/') /* work around rpm bug */
- b++;
- if (lastfiltered)
- {
- if (lastfiltered != 2 || strcmp(b, "sendmail"))
- continue;
- }
- repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, b);
- }
- solv_free(bn);
- solv_free(dn);
- solv_free(di);
-}
-
-static void
-addchangelog(Repodata *data, Id handle, RpmHead *rpmhead)
-{
- char **cn;
- char **cx;
- unsigned int *ct;
- int i, cnc, cxc, ctc;
- Queue hq;
-
- ct = headint32array(rpmhead, TAG_CHANGELOGTIME, &ctc);
- cx = headstringarray(rpmhead, TAG_CHANGELOGTEXT, &cxc);
- cn = headstringarray(rpmhead, TAG_CHANGELOGNAME, &cnc);
- if (!ct || !cx || !cn || !ctc || ctc != cxc || ctc != cnc)
- {
- solv_free(ct);
- solv_free(cx);
- solv_free(cn);
- return;
- }
- queue_init(&hq);
- for (i = 0; i < ctc; i++)
- {
- Id h = repodata_new_handle(data);
- if (ct[i])
- repodata_set_num(data, h, SOLVABLE_CHANGELOG_TIME, ct[i]);
- if (cn[i])
- setutf8string(data, h, SOLVABLE_CHANGELOG_AUTHOR, cn[i]);
- if (cx[i])
- setutf8string(data, h, SOLVABLE_CHANGELOG_TEXT, cx[i]);
- queue_push(&hq, h);
- }
- for (i = 0; i < hq.count; i++)
- repodata_add_flexarray(data, handle, SOLVABLE_CHANGELOG, hq.elements[i]);
- queue_free(&hq);
- solv_free(ct);
- solv_free(cx);
- solv_free(cn);
-}
-
-static void
-set_description_author(Repodata *data, Id handle, char *str)
-{
- char *aut, *p;
- for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
- if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
- break;
- if (aut)
- {
- /* oh my, found SUSE special author section */
- int l = aut - str;
- str = solv_strdup(str);
- aut = str + l;
- str[l] = 0;
- while (l > 0 && str[l - 1] == '\n')
- str[--l] = 0;
- if (l)
- setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
- p = aut + 19;
- aut = str; /* copy over */
- while (*p == ' ' || *p == '\n')
- p++;
- while (*p)
- {
- if (*p == '\n')
- {
- *aut++ = *p++;
- while (*p == ' ')
- p++;
- continue;
- }
- *aut++ = *p++;
- }
- while (aut != str && aut[-1] == '\n')
- aut--;
- *aut = 0;
- if (*str)
- setutf8string(data, handle, SOLVABLE_AUTHORS, str);
- free(str);
- }
- else if (*str)
- setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
-}
-
-static int
-rpm2solv(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead, int flags)
-{
- char *name;
- char *evr;
- char *sourcerpm;
-
- name = headstring(rpmhead, TAG_NAME);
- if (!name)
- {
- pool_error(pool, 0, "package has no name");
- return 0;
- }
- if (!strcmp(name, "gpg-pubkey"))
- return 0;
- s->name = pool_str2id(pool, name, 1);
- sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
- if (sourcerpm || (rpmhead->forcebinary && !headexists(rpmhead, TAG_SOURCEPACKAGE)))
- s->arch = pool_str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
- else
- {
- if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
- s->arch = ARCH_NOSRC;
- else
- s->arch = ARCH_SRC;
- }
- if (!s->arch)
- s->arch = ARCH_NOARCH;
- evr = headtoevr(rpmhead);
- s->evr = pool_str2id(pool, evr, 1);
- s->vendor = pool_str2id(pool, headstring(rpmhead, TAG_VENDOR), 1);
-
- s->provides = makedeps(pool, repo, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
- if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- s->requires = makedeps(pool, repo, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, flags);
- s->conflicts = makedeps(pool, repo, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
- s->obsoletes = makedeps(pool, repo, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
-
- s->recommends = makedeps(pool, repo, rpmhead, TAG_RECOMMENDNAME, TAG_RECOMMENDVERSION, TAG_RECOMMENDFLAGS, 0);
- s->suggests = makedeps(pool, repo, rpmhead, TAG_SUGGESTNAME, TAG_SUGGESTVERSION, TAG_SUGGESTFLAGS, 0);
- s->supplements = makedeps(pool, repo, rpmhead, TAG_SUPPLEMENTNAME, TAG_SUPPLEMENTVERSION, TAG_SUPPLEMENTFLAGS, 0);
- s->enhances = makedeps(pool, repo, rpmhead, TAG_ENHANCENAME, TAG_ENHANCEVERSION, TAG_ENHANCEFLAGS, 0);
-
- s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
- s->conflicts = repo_fix_conflicts(repo, s->conflicts);
-
- if (data)
- {
- Id handle;
- char *str;
- unsigned int u32;
- unsigned long long u64;
-
- handle = s - pool->solvables;
- str = headstring(rpmhead, TAG_SUMMARY);
- if (str)
- setutf8string(data, handle, SOLVABLE_SUMMARY, str);
- str = headstring(rpmhead, TAG_DESCRIPTION);
- if (str)
- set_description_author(data, handle, str);
- str = headstring(rpmhead, TAG_GROUP);
- if (str)
- repodata_set_poolstr(data, handle, SOLVABLE_GROUP, str);
- str = headstring(rpmhead, TAG_LICENSE);
- if (str)
- repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, str);
- str = headstring(rpmhead, TAG_URL);
- if (str)
- repodata_set_str(data, handle, SOLVABLE_URL, str);
- str = headstring(rpmhead, TAG_DISTRIBUTION);
- if (str)
- repodata_set_poolstr(data, handle, SOLVABLE_DISTRIBUTION, str);
- str = headstring(rpmhead, TAG_PACKAGER);
- if (str)
- repodata_set_poolstr(data, handle, SOLVABLE_PACKAGER, str);
- if ((flags & RPM_ADD_WITH_PKGID) != 0)
- {
- unsigned char *chksum;
- unsigned int chksumsize;
- chksum = headbinary(rpmhead, TAG_SIGMD5, &chksumsize);
- if (chksum && chksumsize == 16)
- repodata_set_bin_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, chksum);
- }
- if ((flags & RPM_ADD_WITH_HDRID) != 0)
- {
- str = headstring(rpmhead, TAG_SHA1HEADER);
- if (str && strlen(str) == 40)
- repodata_set_checksum(data, handle, SOLVABLE_HDRID, REPOKEY_TYPE_SHA1, str);
- else if (str && strlen(str) == 64)
- repodata_set_checksum(data, handle, SOLVABLE_HDRID, REPOKEY_TYPE_SHA256, str);
- }
- u32 = headint32(rpmhead, TAG_BUILDTIME);
- if (u32)
- repodata_set_num(data, handle, SOLVABLE_BUILDTIME, u32);
- u32 = headint32(rpmhead, TAG_INSTALLTIME);
- if (u32)
- repodata_set_num(data, handle, SOLVABLE_INSTALLTIME, u32);
- u64 = headint64(rpmhead, TAG_LONGSIZE);
- if (u64)
- repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, u64);
- else
- {
- u32 = headint32(rpmhead, TAG_SIZE);
- if (u32)
- repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, u32);
- }
- if (sourcerpm)
- repodata_set_sourcepkg(data, handle, sourcerpm);
- if ((flags & RPM_ADD_TRIGGERS) != 0)
- {
- Id id, lastid;
- unsigned int ida = makedeps(pool, repo, rpmhead, TAG_TRIGGERNAME, TAG_TRIGGERVERSION, TAG_TRIGGERFLAGS, 0);
-
- lastid = 0;
- for (; (id = repo->idarraydata[ida]) != 0; ida++)
- {
- /* we currently do not support rel ids in incore data, so
- * strip off versioning information */
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- id = rd->name;
- }
- if (id == lastid)
- continue;
- repodata_add_idarray(data, handle, SOLVABLE_TRIGGERS, id);
- lastid = id;
- }
- }
- if ((flags & RPM_ADD_NO_FILELIST) == 0)
- addfilelist(data, handle, rpmhead, flags);
- if ((flags & RPM_ADD_WITH_CHANGELOG) != 0)
- addchangelog(data, handle, rpmhead);
- }
- solv_free(evr);
- return 1;
-}
-
-
-/******************************************************************/
-/* Rpm Database stuff
- */
-
-struct rpmdbstate {
- Pool *pool;
- char *rootdir;
-
- RpmHead *rpmhead; /* header storage space */
- int rpmheadsize;
-
- int dbopened;
- DB_ENV *dbenv; /* database environment */
- DB *db; /* packages database */
- int byteswapped; /* endianess of packages database */
- int is_ostree; /* read-only db that lives in /usr/share/rpm */
-};
-
-struct rpmdbentry {
- Id rpmdbid;
- Id nameoff;
-};
-
-#define ENTRIES_BLOCK 255
-#define NAMEDATA_BLOCK 1023
-
-
-static inline Id db2rpmdbid(unsigned char *db, int byteswapped)
-{
-#ifdef RPM5
- return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
-#else
-# if defined(WORDS_BIGENDIAN)
- if (!byteswapped)
-# else
- if (byteswapped)
-# endif
- return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
- else
- return db[3] << 24 | db[2] << 16 | db[1] << 8 | db[0];
-#endif
-}
-
-static inline void rpmdbid2db(unsigned char *db, Id id, int byteswapped)
-{
-#ifdef RPM5
- db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
-#else
-# if defined(WORDS_BIGENDIAN)
- if (!byteswapped)
-# else
- if (byteswapped)
-# endif
- db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
- else
- db[3] = id >> 24, db[2] = id >> 16, db[1] = id >> 8, db[0] = id;
-#endif
-}
-
-#ifdef FEDORA
-int
-serialize_dbenv_ops(struct rpmdbstate *state)
-{
- char lpath[PATH_MAX];
- mode_t oldmask;
- int fd;
- struct flock fl;
-
- snprintf(lpath, PATH_MAX, "%s/var/lib/rpm/.dbenv.lock", state->rootdir ? state->rootdir : "");
- oldmask = umask(022);
- fd = open(lpath, (O_RDWR|O_CREAT), 0644);
- umask(oldmask);
- if (fd < 0)
- return -1;
- memset(&fl, 0, sizeof(fl));
- fl.l_type = F_WRLCK;
- fl.l_whence = SEEK_SET;
- for (;;)
- {
- if (fcntl(fd, F_SETLKW, &fl) != -1)
- return fd;
- if (errno != EINTR)
- break;
- }
- close(fd);
- return -1;
-}
-#endif
-
-/* should look in /usr/lib/rpm/macros instead, but we want speed... */
-static int
-opendbenv(struct rpmdbstate *state)
-{
- const char *rootdir = state->rootdir;
- char dbpath[PATH_MAX];
- DB_ENV *dbenv = 0;
- int r;
-
- if (db_env_create(&dbenv, 0))
- return pool_error(state->pool, 0, "db_env_create: %s", strerror(errno));
-#if defined(FEDORA) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
- dbenv->set_thread_count(dbenv, 8);
-#endif
- snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm", rootdir ? rootdir : "");
- if (access(dbpath, W_OK) == -1)
- {
- snprintf(dbpath, PATH_MAX, "%s/usr/share/rpm/Packages", rootdir ? rootdir : "");
- if (access(dbpath, R_OK) == 0)
- state->is_ostree = 1;
- snprintf(dbpath, PATH_MAX, "%s%s", rootdir ? rootdir : "", state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
- r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
- }
- else
- {
-#ifdef FEDORA
- int serialize_fd = serialize_dbenv_ops(state);
- r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL, 0644);
- if (serialize_fd >= 0)
- close(serialize_fd);
-#else
- r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
-#endif
- }
- if (r)
- {
- pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
- dbenv->close(dbenv, 0);
- return 0;
- }
- state->dbenv = dbenv;
- return 1;
-}
-
-static void
-closedbenv(struct rpmdbstate *state)
-{
-#ifdef FEDORA
- uint32_t eflags = 0;
-#endif
-
- if (!state->dbenv)
- return;
-#ifdef FEDORA
- (void)state->dbenv->get_open_flags(state->dbenv, &eflags);
- if (!(eflags & DB_PRIVATE))
- {
- int serialize_fd = serialize_dbenv_ops(state);
- state->dbenv->close(state->dbenv, 0);
- if (serialize_fd >= 0)
- close(serialize_fd);
- }
- else
- state->dbenv->close(state->dbenv, 0);
-#else
- state->dbenv->close(state->dbenv, 0);
-#endif
- state->dbenv = 0;
-}
-
-static int
-openpkgdb(struct rpmdbstate *state)
-{
- if (state->dbopened)
- return state->dbopened > 0 ? 1 : 0;
- state->dbopened = -1;
- if (!state->dbenv && !opendbenv(state))
- return 0;
- if (db_create(&state->db, state->dbenv, 0))
- {
- pool_error(state->pool, 0, "db_create: %s", strerror(errno));
- state->db = 0;
- closedbenv(state);
- return 0;
- }
- if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
- {
- pool_error(state->pool, 0, "db->open Packages: %s", strerror(errno));
- state->db->close(state->db, 0);
- state->db = 0;
- closedbenv(state);
- return 0;
- }
- if (state->db->get_byteswapped(state->db, &state->byteswapped))
- {
- pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
- state->db->close(state->db, 0);
- state->db = 0;
- closedbenv(state);
- return 0;
- }
- state->dbopened = 1;
- return 1;
-}
-
-/* get the rpmdbids of all installed packages from the Name index database.
- * This is much faster then querying the big Packages database */
-static struct rpmdbentry *
-getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap)
-{
- DB_ENV *dbenv = 0;
- DB *db = 0;
- DBC *dbc = 0;
- int byteswapped;
- DBT dbkey;
- DBT dbdata;
- unsigned char *dp;
- int dl;
- Id nameoff;
-
- char *namedata = 0;
- int namedatal = 0;
- struct rpmdbentry *entries = 0;
- int nentries = 0;
-
- *nentriesp = 0;
- if (namedatap)
- *namedatap = 0;
-
- if (!state->dbenv && !opendbenv(state))
- return 0;
- dbenv = state->dbenv;
- if (db_create(&db, dbenv, 0))
- {
- pool_error(state->pool, 0, "db_create: %s", strerror(errno));
- return 0;
- }
- if (db->open(db, 0, index, 0, DB_UNKNOWN, DB_RDONLY, 0664))
- {
- pool_error(state->pool, 0, "db->open %s: %s", index, strerror(errno));
- db->close(db, 0);
- return 0;
- }
- if (db->get_byteswapped(db, &byteswapped))
- {
- pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
- db->close(db, 0);
- return 0;
- }
- if (db->cursor(db, NULL, &dbc, 0))
- {
- pool_error(state->pool, 0, "db->cursor: %s", strerror(errno));
- db->close(db, 0);
- return 0;
- }
- memset(&dbkey, 0, sizeof(dbkey));
- memset(&dbdata, 0, sizeof(dbdata));
- if (match)
- {
- dbkey.data = (void *)match;
- dbkey.size = strlen(match);
- }
- while (dbc->c_get(dbc, &dbkey, &dbdata, match ? DB_SET : DB_NEXT) == 0)
- {
- if (!match && dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
- continue;
- dl = dbdata.size;
- dp = dbdata.data;
- nameoff = namedatal;
- if (namedatap)
- {
- namedata = solv_extend(namedata, namedatal, dbkey.size + 1, 1, NAMEDATA_BLOCK);
- memcpy(namedata + namedatal, dbkey.data, dbkey.size);
- namedata[namedatal + dbkey.size] = 0;
- namedatal += dbkey.size + 1;
- }
- while(dl >= RPM_INDEX_SIZE)
- {
- entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
- entries[nentries].rpmdbid = db2rpmdbid(dp, byteswapped);
- entries[nentries].nameoff = nameoff;
- nentries++;
- dp += RPM_INDEX_SIZE;
- dl -= RPM_INDEX_SIZE;
- }
- if (match)
- break;
- }
- dbc->c_close(dbc);
- db->close(db, 0);
- /* make sure that enteries is != 0 if there was no error */
- if (!entries)
- entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
- *nentriesp = nentries;
- if (namedatap)
- *namedatap = namedata;
- return entries;
-}
-
-/* retrive header by rpmdbid */
-static int
-getrpmdbid(struct rpmdbstate *state, Id rpmdbid)
-{
- unsigned char buf[16];
- DBT dbkey;
- DBT dbdata;
- RpmHead *rpmhead;
-
- if (!rpmdbid)
- {
- pool_error(state->pool, 0, "illegal rpmdbid");
- return -1;
- }
- if (state->dbopened != 1 && !openpkgdb(state))
- return -1;
- rpmdbid2db(buf, rpmdbid, state->byteswapped);
- memset(&dbkey, 0, sizeof(dbkey));
- memset(&dbdata, 0, sizeof(dbdata));
- dbkey.data = buf;
- dbkey.size = 4;
- dbdata.data = 0;
- dbdata.size = 0;
- if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
- return 0;
- if (dbdata.size < 8)
- {
- pool_error(state->pool, 0, "corrupt rpm database (size)");
- return -1;
- }
- if (dbdata.size > state->rpmheadsize)
- {
- state->rpmheadsize = dbdata.size + 128;
- state->rpmhead = solv_realloc(state->rpmhead, sizeof(*rpmhead) + state->rpmheadsize);
- }
- rpmhead = state->rpmhead;
- memcpy(buf, dbdata.data, 8);
- rpmhead->forcebinary = 1;
- 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 > dbdata.size)
- {
- pool_error(state->pool, 0, "corrupt rpm database (data size)");
- return -1;
- }
- memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
- rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
- return 1;
-}
-
-/* retrive header by berkeleydb cursor */
-static Id
-getrpmcursor(struct rpmdbstate *state, DBC *dbc)
-{
- unsigned char buf[16];
- DBT dbkey;
- DBT dbdata;
- RpmHead *rpmhead;
- Id dbid;
-
- memset(&dbkey, 0, sizeof(dbkey));
- memset(&dbdata, 0, sizeof(dbdata));
- while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
- {
- if (dbkey.size != 4)
- return pool_error(state->pool, -1, "corrupt Packages database (key size)");
- dbid = db2rpmdbid(dbkey.data, state->byteswapped);
- if (dbid == 0) /* the join key */
- continue;
- if (dbdata.size < 8)
- return pool_error(state->pool, -1, "corrupt rpm database (size %u)\n", dbdata.size);
- if (dbdata.size > state->rpmheadsize)
- {
- state->rpmheadsize = dbdata.size + 128;
- state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
- }
- rpmhead = state->rpmhead;
- memcpy(buf, dbdata.data, 8);
- rpmhead->forcebinary = 1;
- 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 > dbdata.size)
- return pool_error(state->pool, -1, "corrupt rpm database (data size)\n");
- memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
- rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
- return dbid;
- }
- return 0;
-}
-
-static void
-freestate(struct rpmdbstate *state)
-{
- /* close down */
- if (!state)
- return;
- if (state->db)
- state->db->close(state->db, 0);
- if (state->dbenv)
- closedbenv(state);
- if (state->rootdir)
- solv_free(state->rootdir);
- solv_free(state->rpmhead);
-}
-
-void *
-rpm_state_create(Pool *pool, const char *rootdir)
-{
- struct rpmdbstate *state;
- state = solv_calloc(1, sizeof(*state));
- state->pool = pool;
- if (rootdir)
- state->rootdir = solv_strdup(rootdir);
- return state;
-}
-
-void *
-rpm_state_free(void *state)
-{
- freestate(state);
- return solv_free(state);
-}
-
-static int
-count_headers(struct rpmdbstate *state)
-{
- Pool *pool = state->pool;
- char dbpath[PATH_MAX];
- struct stat statbuf;
- DB *db = 0;
- DBC *dbc = 0;
- int count = 0;
- DBT dbkey;
- DBT dbdata;
-
- snprintf(dbpath, PATH_MAX, "%s%s/Name", state->rootdir ? state->rootdir : "", state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
- if (stat(dbpath, &statbuf))
- return 0;
- memset(&dbkey, 0, sizeof(dbkey));
- memset(&dbdata, 0, sizeof(dbdata));
- if (db_create(&db, state->dbenv, 0))
- {
- pool_error(pool, 0, "db_create: %s", strerror(errno));
- return 0;
- }
- if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
- {
- pool_error(pool, 0, "db->open Name: %s", strerror(errno));
- db->close(db, 0);
- return 0;
- }
- if (db->cursor(db, NULL, &dbc, 0))
- {
- db->close(db, 0);
- pool_error(pool, 0, "db->cursor: %s", strerror(errno));
- return 0;
- }
- while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
- count += dbdata.size / RPM_INDEX_SIZE;
- dbc->c_close(dbc);
- db->close(db, 0);
- return count;
-}
-
-/******************************************************************/
-
-static Offset
-copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
-{
- int cc;
- Id *ida, *from;
- Offset ido;
-
- if (!fromoff)
- return 0;
- from = fromrepo->idarraydata + fromoff;
- for (ida = from, cc = 0; *ida; ida++, cc++)
- ;
- if (cc == 0)
- return 0;
- ido = repo_reserve_ids(repo, 0, cc);
- ida = repo->idarraydata + ido;
- memcpy(ida, from, (cc + 1) * sizeof(Id));
- repo->idarraysize += cc + 1;
- return ido;
-}
-
-#define COPYDIR_DIRCACHE_SIZE 512
-
-static Id copydir_complex(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache);
-
-static inline Id
-copydir(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache)
-{
- if (cache && cache[did & 255] == did)
- return cache[(did & 255) + 256];
- return copydir_complex(pool, data, fromdata, did, cache);
-}
-
-static Id
-copydir_complex(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache)
-{
- Id parent = dirpool_parent(&fromdata->dirpool, did);
- Id compid = dirpool_compid(&fromdata->dirpool, did);
- if (parent)
- parent = copydir(pool, data, fromdata, parent, cache);
- if (data->localpool || fromdata->localpool)
- compid = repodata_translate_id(data, fromdata, compid, 1);
- compid = dirpool_add_dir(&data->dirpool, parent, compid, 1);
- if (cache)
- {
- cache[did & 255] = did;
- cache[(did & 255) + 256] = compid;
- }
- return compid;
-}
-
-struct solvable_copy_cbdata {
- Repodata *data;
- Id handle;
- Id subhandle;
- Id *dircache;
-};
-
-static int
-solvable_copy_cb(void *vcbdata, Solvable *r, Repodata *fromdata, Repokey *key, KeyValue *kv)
-{
- struct solvable_copy_cbdata *cbdata = vcbdata;
- Id id, keyname;
- Repodata *data = cbdata->data;
- Id handle = cbdata->handle;
- Pool *pool = data->repo->pool;
-
- keyname = key->name;
- switch(key->type)
- {
- case REPOKEY_TYPE_ID:
- case REPOKEY_TYPE_CONSTANTID:
- case REPOKEY_TYPE_IDARRAY: /* used for triggers */
- id = kv->id;
- if (data->localpool || fromdata->localpool)
- id = repodata_translate_id(data, fromdata, id, 1);
- if (key->type == REPOKEY_TYPE_ID)
- repodata_set_id(data, handle, keyname, id);
- else if (key->type == REPOKEY_TYPE_CONSTANTID)
- repodata_set_constantid(data, handle, keyname, id);
- else
- repodata_add_idarray(data, handle, keyname, id);
- break;
- case REPOKEY_TYPE_STR:
- repodata_set_str(data, handle, keyname, kv->str);
- break;
- case REPOKEY_TYPE_VOID:
- repodata_set_void(data, handle, keyname);
- break;
- case REPOKEY_TYPE_NUM:
- repodata_set_num(data, handle, keyname, SOLV_KV_NUM64(kv));
- break;
- case REPOKEY_TYPE_CONSTANT:
- repodata_set_constant(data, handle, keyname, kv->num);
- break;
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- id = kv->id;
- id = copydir(pool, data, fromdata, id, cbdata->dircache);
- repodata_add_dirnumnum(data, handle, keyname, id, kv->num, kv->num2);
- break;
- case REPOKEY_TYPE_DIRSTRARRAY:
- id = kv->id;
- id = copydir(pool, data, fromdata, id, cbdata->dircache);
- repodata_add_dirstr(data, handle, keyname, id, kv->str);
- break;
- case REPOKEY_TYPE_FLEXARRAY:
- if (kv->eof == 2)
- {
- assert(cbdata->subhandle);
- cbdata->handle = cbdata->subhandle;
- cbdata->subhandle = 0;
- break;
- }
- if (!kv->entry)
- {
- assert(!cbdata->subhandle);
- cbdata->subhandle = cbdata->handle;
- }
- cbdata->handle = repodata_new_handle(data);
- repodata_add_flexarray(data, cbdata->subhandle, keyname, cbdata->handle);
- break;
- default:
- if (solv_chksum_len(key->type))
- {
- repodata_set_bin_checksum(data, handle, keyname, key->type, (const unsigned char *)kv->str);
- break;
- }
- break;
- }
- return 0;
-}
-
-static void
-solvable_copy(Solvable *s, Solvable *r, Repodata *data, Id *dircache)
-{
- int p, i;
- Repo *repo = s->repo;
- Pool *pool = repo->pool;
- Repo *fromrepo = r->repo;
- struct solvable_copy_cbdata cbdata;
-
- /* copy solvable data */
- s->name = r->name;
- s->evr = r->evr;
- s->arch = r->arch;
- s->vendor = r->vendor;
- s->provides = copydeps(pool, repo, r->provides, fromrepo);
- s->requires = copydeps(pool, repo, r->requires, fromrepo);
- s->conflicts = copydeps(pool, repo, r->conflicts, fromrepo);
- s->obsoletes = copydeps(pool, repo, r->obsoletes, fromrepo);
- s->recommends = copydeps(pool, repo, r->recommends, fromrepo);
- s->suggests = copydeps(pool, repo, r->suggests, fromrepo);
- s->supplements = copydeps(pool, repo, r->supplements, fromrepo);
- s->enhances = copydeps(pool, repo, r->enhances, fromrepo);
-
- /* copy all attributes */
- if (!data)
- return;
- cbdata.data = data;
- cbdata.handle = s - pool->solvables;
- cbdata.subhandle = 0;
- cbdata.dircache = dircache;
- p = r - fromrepo->pool->solvables;
-#if 0
- repo_search(fromrepo, p, 0, 0, SEARCH_NO_STORAGE_SOLVABLE | SEARCH_SUB | SEARCH_ARRAYSENTINEL, solvable_copy_cb, &cbdata);
-#else
- FOR_REPODATAS(fromrepo, i, data)
- {
- if (p >= data->start && p < data->end)
- repodata_search(data, p, 0, SEARCH_SUB | SEARCH_ARRAYSENTINEL, solvable_copy_cb, &cbdata);
- cbdata.dircache = 0; /* only for first repodata */
- }
-#endif
-}
-
-/* used to sort entries by package name that got returned in some database order */
-static int
-rpmids_sort_cmp(const void *va, const void *vb, void *dp)
-{
- struct rpmdbentry const *a = va, *b = vb;
- char *namedata = dp;
- int r;
- r = strcmp(namedata + a->nameoff, namedata + b->nameoff);
- if (r)
- return r;
- return a->rpmdbid - b->rpmdbid;
-}
-
-static int
-pkgids_sort_cmp(const void *va, const void *vb, void *dp)
-{
- Repo *repo = dp;
- Pool *pool = repo->pool;
- Solvable *a = pool->solvables + *(Id *)va;
- Solvable *b = pool->solvables + *(Id *)vb;
- Id *rpmdbid;
-
- if (a->name != b->name)
- return strcmp(pool_id2str(pool, a->name), pool_id2str(pool, b->name));
- rpmdbid = repo->rpmdbid;
- return rpmdbid[(a - pool->solvables) - repo->start] - rpmdbid[(b - pool->solvables) - repo->start];
-}
-
-static void
-swap_solvables(Repo *repo, Repodata *data, Id pa, Id pb)
-{
- Pool *pool = repo->pool;
- Solvable tmp;
-
- tmp = pool->solvables[pa];
- pool->solvables[pa] = pool->solvables[pb];
- pool->solvables[pb] = tmp;
- if (repo->rpmdbid)
- {
- Id tmpid = repo->rpmdbid[pa - repo->start];
- repo->rpmdbid[pa - repo->start] = repo->rpmdbid[pb - repo->start];
- repo->rpmdbid[pb - repo->start] = tmpid;
- }
- /* only works if nothing is already internalized! */
- if (data)
- repodata_swap_attrs(data, pa, pb);
-}
-
-static void
-mkrpmdbcookie(struct stat *st, unsigned char *cookie, int flags)
-{
- int f = 0;
- memset(cookie, 0, 32);
- cookie[3] = RPMDB_COOKIE_VERSION;
- memcpy(cookie + 16, &st->st_ino, sizeof(st->st_ino));
- memcpy(cookie + 24, &st->st_dev, sizeof(st->st_dev));
- if ((flags & RPM_ADD_WITH_PKGID) != 0)
- f |= 1;
- if ((flags & RPM_ADD_WITH_HDRID) != 0)
- f |= 2;
- if ((flags & RPM_ADD_WITH_CHANGELOG) != 0)
- f |= 4;
- if ((flags & RPM_ADD_NO_FILELIST) == 0)
- f |= 8;
- if ((flags & RPM_ADD_NO_RPMLIBREQS) != 0)
- cookie[1] = 1;
- cookie[0] = f;
-}
-
-/*
- * read rpm db as repo
- *
- */
-
-int
-repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
-{
- Pool *pool = repo->pool;
- char dbpath[PATH_MAX];
- struct stat packagesstat;
- unsigned char newcookie[32];
- const unsigned char *oldcookie = 0;
- Id oldcookietype = 0;
- Repodata *data;
- int count = 0, done = 0;
- struct rpmdbstate state;
- int i;
- Solvable *s;
- unsigned int now;
-
- now = solv_timems(0);
- memset(&state, 0, sizeof(state));
- state.pool = pool;
- if (flags & REPO_USE_ROOTDIR)
- state.rootdir = solv_strdup(pool_get_rootdir(pool));
-
- data = repo_add_repodata(repo, flags);
-
- if (ref && !(ref->nsolvables && ref->rpmdbid && ref->pool == repo->pool))
- {
- if ((flags & RPMDB_EMPTY_REFREPO) != 0)
- repo_empty(ref, 1);
- ref = 0;
- }
-
- if (!opendbenv(&state))
- {
- solv_free(state.rootdir);
- return -1;
- }
-
- /* XXX: should get ro lock of Packages database! */
- snprintf(dbpath, PATH_MAX, "%s%s/Packages", state.rootdir ? state.rootdir : "", state.is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
- if (stat(dbpath, &packagesstat))
- {
- pool_error(pool, -1, "%s: %s", dbpath, strerror(errno));
- freestate(&state);
- return -1;
- }
- mkrpmdbcookie(&packagesstat, newcookie, flags);
- repodata_set_bin_checksum(data, SOLVID_META, REPOSITORY_RPMDBCOOKIE, REPOKEY_TYPE_SHA256, newcookie);
-
- if (ref)
- oldcookie = repo_lookup_bin_checksum(ref, SOLVID_META, REPOSITORY_RPMDBCOOKIE, &oldcookietype);
- if (!ref || !oldcookie || oldcookietype != REPOKEY_TYPE_SHA256 || memcmp(oldcookie, newcookie, 32) != 0)
- {
- int solvstart = 0, solvend = 0;
- Id dbid;
- DBC *dbc = 0;
-
- if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
- repo_empty(ref, 1); /* get it out of the way */
- if ((flags & RPMDB_REPORT_PROGRESS) != 0)
- count = count_headers(&state);
- if (!openpkgdb(&state))
- {
- freestate(&state);
- return -1;
- }
- if (state.db->cursor(state.db, NULL, &dbc, 0))
- {
- freestate(&state);
- return pool_error(pool, -1, "db->cursor failed");
- }
- i = 0;
- s = 0;
- while ((dbid = getrpmcursor(&state, dbc)) != 0)
- {
- if (dbid == -1)
- {
- dbc->c_close(dbc);
- freestate(&state);
- return -1;
- }
- if (!s)
- {
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- if (!solvstart)
- solvstart = s - pool->solvables;
- solvend = s - pool->solvables + 1;
- }
- if (!repo->rpmdbid)
- repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
- repo->rpmdbid[(s - pool->solvables) - repo->start] = dbid;
- if (rpm2solv(pool, repo, data, s, state.rpmhead, flags | RPM_ADD_TRIGGERS))
- {
- i++;
- s = 0;
- }
- else
- {
- /* We can reuse this solvable, but make sure it's still
- associated with this repo. */
- memset(s, 0, sizeof(*s));
- s->repo = repo;
- }
- if ((flags & RPMDB_REPORT_PROGRESS) != 0)
- {
- if (done < count)
- done++;
- if (done < count && (done - 1) * 100 / count != done * 100 / count)
- pool_debug(pool, SOLV_ERROR, "%%%% %d\n", done * 100 / count);
- }
- }
- dbc->c_close(dbc);
- if (s)
- {
- /* oops, could not reuse. free it instead */
- repo_free_solvable(repo, s - pool->solvables, 1);
- solvend--;
- s = 0;
- }
- /* now sort all solvables in the new solvstart..solvend block */
- if (solvend - solvstart > 1)
- {
- Id *pkgids = solv_malloc2(solvend - solvstart, sizeof(Id));
- for (i = solvstart; i < solvend; i++)
- pkgids[i - solvstart] = i;
- solv_sort(pkgids, solvend - solvstart, sizeof(Id), pkgids_sort_cmp, repo);
- /* adapt order */
- for (i = solvstart; i < solvend; i++)
- {
- int j = pkgids[i - solvstart];
- while (j < i)
- j = pkgids[i - solvstart] = pkgids[j - solvstart];
- if (j != i)
- swap_solvables(repo, data, i, j);
- }
- solv_free(pkgids);
- }
- }
- else
- {
- Id dircache[COPYDIR_DIRCACHE_SIZE]; /* see copydir */
- struct rpmdbentry *entries = 0, *rp;
- int nentries = 0;
- char *namedata = 0;
- unsigned int refmask, h;
- Id id, *refhash;
- int res;
-
- memset(dircache, 0, sizeof(dircache));
-
- /* get ids of installed rpms */
- entries = getinstalledrpmdbids(&state, "Name", 0, &nentries, &namedata);
- if (!entries)
- {
- freestate(&state);
- return -1;
- }
-
- /* sort by name */
- if (nentries > 1)
- solv_sort(entries, nentries, sizeof(*entries), rpmids_sort_cmp, namedata);
-
- /* create hash from dbid to ref */
- refmask = mkmask(ref->nsolvables);
- refhash = solv_calloc(refmask + 1, sizeof(Id));
- for (i = 0; i < ref->end - ref->start; i++)
- {
- if (!ref->rpmdbid[i])
- continue;
- h = ref->rpmdbid[i] & refmask;
- while (refhash[h])
- h = (h + 317) & refmask;
- refhash[h] = i + 1; /* make it non-zero */
- }
-
- /* count the misses, they will cost us time */
- if ((flags & RPMDB_REPORT_PROGRESS) != 0)
- {
- for (i = 0, rp = entries; i < nentries; i++, rp++)
- {
- if (refhash)
- {
- Id dbid = rp->rpmdbid;
- h = dbid & refmask;
- while ((id = refhash[h]))
- {
- if (ref->rpmdbid[id - 1] == dbid)
- break;
- h = (h + 317) & refmask;
- }
- if (id)
- continue;
- }
- count++;
- }
- }
-
- if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
- s = pool_id2solvable(pool, repo_add_solvable_block_before(repo, nentries, ref));
- else
- s = pool_id2solvable(pool, repo_add_solvable_block(repo, nentries));
- if (!repo->rpmdbid)
- repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
-
- for (i = 0, rp = entries; i < nentries; i++, rp++, s++)
- {
- Id dbid = rp->rpmdbid;
- repo->rpmdbid[(s - pool->solvables) - repo->start] = rp->rpmdbid;
- 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 (r->repo == ref)
- {
- solvable_copy(s, r, data, dircache);
- continue;
- }
- }
- }
- res = getrpmdbid(&state, dbid);
- if (res <= 0)
- {
- if (!res)
- pool_error(pool, -1, "inconsistent rpm database, key %d not found. run 'rpm --rebuilddb' to fix.", dbid);
- freestate(&state);
- solv_free(entries);
- solv_free(namedata);
- solv_free(refhash);
- return -1;
- }
- rpm2solv(pool, repo, data, s, state.rpmhead, flags | RPM_ADD_TRIGGERS);
- if ((flags & RPMDB_REPORT_PROGRESS) != 0)
- {
- if (done < count)
- done++;
- if (done < count && (done - 1) * 100 / count != done * 100 / count)
- pool_debug(pool, SOLV_ERROR, "%%%% %d\n", done * 100 / count);
- }
- }
-
- solv_free(entries);
- solv_free(namedata);
- solv_free(refhash);
- if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
- repo_empty(ref, 1);
- }
-
- freestate(&state);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- if ((flags & RPMDB_REPORT_PROGRESS) != 0)
- pool_debug(pool, SOLV_ERROR, "%%%% 100\n");
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_rpmdb took %d ms\n", solv_timems(now));
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
- return 0;
-}
-
-int
-repo_add_rpmdb_reffp(Repo *repo, FILE *fp, int flags)
-{
- int res;
- Repo *ref = 0;
-
- if (!fp)
- return repo_add_rpmdb(repo, 0, flags);
- ref = repo_create(repo->pool, "add_rpmdb_reffp");
- if (repo_add_solv(ref, fp, 0) != 0)
- {
- repo_free(ref, 1);
- ref = 0;
- }
- if (ref && ref->start == ref->end)
- {
- repo_free(ref, 1);
- ref = 0;
- }
- if (ref)
- repo_disable_paging(ref);
- res = repo_add_rpmdb(repo, ref, flags | RPMDB_EMPTY_REFREPO);
- if (ref)
- repo_free(ref, 1);
- return res;
-}
-
-static inline unsigned int
-getu32(const unsigned char *dp)
-{
- return dp[0] << 24 | dp[1] << 16 | dp[2] << 8 | dp[3];
-}
-
-
-Id
-repo_add_rpm(Repo *repo, const char *rpm, int flags)
-{
- unsigned int sigdsize, sigcnt, l;
- Pool *pool = repo->pool;
- Solvable *s;
- RpmHead *rpmhead = 0;
- int rpmheadsize = 0;
- char *payloadformat;
- FILE *fp;
- unsigned char lead[4096];
- int headerstart, headerend;
- struct stat stb;
- Repodata *data;
- unsigned char pkgid[16];
- unsigned char leadsigid[16];
- unsigned char hdrid[32];
- int pkgidtype, leadsigidtype, hdridtype;
- Id chksumtype = 0;
- Chksum *chksumh = 0;
- Chksum *leadsigchksumh = 0;
- int forcebinary = 0;
-
- data = repo_add_repodata(repo, flags);
-
- if ((flags & RPM_ADD_WITH_SHA256SUM) != 0)
- chksumtype = REPOKEY_TYPE_SHA256;
- else if ((flags & RPM_ADD_WITH_SHA1SUM) != 0)
- chksumtype = REPOKEY_TYPE_SHA1;
-
- if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, rpm) : rpm, "r")) == 0)
- {
- pool_error(pool, -1, "%s: %s", rpm, strerror(errno));
- return 0;
- }
- if (fstat(fileno(fp), &stb))
- {
- pool_error(pool, -1, "fstat: %s", strerror(errno));
- fclose(fp);
- return 0;
- }
- if (chksumtype)
- chksumh = solv_chksum_create(chksumtype);
- if ((flags & RPM_ADD_WITH_LEADSIGID) != 0)
- leadsigchksumh = solv_chksum_create(REPOKEY_TYPE_MD5);
- if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
- {
- pool_error(pool, -1, "%s: not a rpm", rpm);
- fclose(fp);
- return 0;
- }
- forcebinary = lead[6] != 0 || lead[7] != 1;
- if (chksumh)
- solv_chksum_add(chksumh, lead, 96 + 16);
- if (leadsigchksumh)
- solv_chksum_add(leadsigchksumh, lead, 96 + 16);
- if (lead[78] != 0 || lead[79] != 5)
- {
- pool_error(pool, -1, "%s: not a rpm v5 header", rpm);
- fclose(fp);
- return 0;
- }
- if (getu32(lead + 96) != 0x8eade801)
- {
- pool_error(pool, -1, "%s: bad signature header", rpm);
- fclose(fp);
- return 0;
- }
- sigcnt = getu32(lead + 96 + 8);
- sigdsize = getu32(lead + 96 + 12);
- if (sigcnt >= 0x100000 || sigdsize >= 0x100000)
- {
- pool_error(pool, -1, "%s: bad signature header", rpm);
- fclose(fp);
- return 0;
- }
- sigdsize += sigcnt * 16;
- sigdsize = (sigdsize + 7) & ~7;
- headerstart = 96 + 16 + sigdsize;
- pkgidtype = leadsigidtype = hdridtype = 0;
- if ((flags & (RPM_ADD_WITH_PKGID | RPM_ADD_WITH_HDRID)) != 0)
- {
- /* extract pkgid or hdrid from the signature header */
- if (sigdsize > rpmheadsize)
- {
- rpmheadsize = sigdsize + 128;
- rpmhead = solv_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
- }
- if (fread(rpmhead->data, sigdsize, 1, fp) != 1)
- {
- pool_error(pool, -1, "%s: unexpected EOF", rpm);
- fclose(fp);
- return 0;
- }
- if (chksumh)
- solv_chksum_add(chksumh, rpmhead->data, sigdsize);
- if (leadsigchksumh)
- solv_chksum_add(leadsigchksumh, rpmhead->data, sigdsize);
- rpmhead->forcebinary = 0;
- rpmhead->cnt = sigcnt;
- rpmhead->dcnt = sigdsize - sigcnt * 16;
- rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
- if ((flags & RPM_ADD_WITH_PKGID) != 0)
- {
- unsigned char *chksum;
- unsigned int chksumsize;
- chksum = headbinary(rpmhead, SIGTAG_MD5, &chksumsize);
- if (chksum && chksumsize == 16)
- {
- pkgidtype = REPOKEY_TYPE_MD5;
- memcpy(pkgid, chksum, 16);
- }
- }
- if ((flags & RPM_ADD_WITH_HDRID) != 0)
- {
- const char *str = headstring(rpmhead, TAG_SHA1HEADER);
- if (str && strlen(str) == 40)
- {
- if (solv_hex2bin(&str, hdrid, 20) == 20)
- hdridtype = REPOKEY_TYPE_SHA1;
- }
- else if (str && strlen(str) == 64)
- {
- if (solv_hex2bin(&str, hdrid, 32) == 32)
- hdridtype = REPOKEY_TYPE_SHA256;
- }
- }
- }
- else
- {
- /* just skip the signature header */
- while (sigdsize)
- {
- l = sigdsize > 4096 ? 4096 : sigdsize;
- if (fread(lead, l, 1, fp) != 1)
- {
- pool_error(pool, -1, "%s: unexpected EOF", rpm);
- fclose(fp);
- return 0;
- }
- if (chksumh)
- solv_chksum_add(chksumh, lead, l);
- if (leadsigchksumh)
- solv_chksum_add(leadsigchksumh, lead, l);
- sigdsize -= l;
- }
- }
- if (leadsigchksumh)
- {
- leadsigchksumh = solv_chksum_free(leadsigchksumh, leadsigid);
- leadsigidtype = REPOKEY_TYPE_MD5;
- }
- if (fread(lead, 16, 1, fp) != 1)
- {
- pool_error(pool, -1, "%s: unexpected EOF", rpm);
- fclose(fp);
- return 0;
- }
- if (chksumh)
- solv_chksum_add(chksumh, lead, 16);
- if (getu32(lead) != 0x8eade801)
- {
- pool_error(pool, -1, "%s: bad header", rpm);
- fclose(fp);
- return 0;
- }
- sigcnt = getu32(lead + 8);
- sigdsize = getu32(lead + 12);
- if (sigcnt >= 0x100000 || sigdsize >= 0x2000000)
- {
- pool_error(pool, -1, "%s: bad header", rpm);
- fclose(fp);
- return 0;
- }
- l = sigdsize + sigcnt * 16;
- headerend = headerstart + 16 + l;
- if (l > rpmheadsize)
- {
- rpmheadsize = l + 128;
- rpmhead = solv_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
- }
- if (fread(rpmhead->data, l, 1, fp) != 1)
- {
- pool_error(pool, -1, "%s: unexpected EOF", rpm);
- fclose(fp);
- return 0;
- }
- if (chksumh)
- solv_chksum_add(chksumh, rpmhead->data, l);
- rpmhead->forcebinary = forcebinary;
- rpmhead->cnt = sigcnt;
- rpmhead->dcnt = sigdsize;
- rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
- if (headexists(rpmhead, TAG_PATCHESNAME))
- {
- /* this is a patch rpm, ignore */
- pool_error(pool, -1, "%s: is patch rpm", rpm);
- fclose(fp);
- solv_chksum_free(chksumh, 0);
- solv_free(rpmhead);
- return 0;
- }
- payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
- if (payloadformat && !strcmp(payloadformat, "drpm"))
- {
- /* this is a delta rpm */
- pool_error(pool, -1, "%s: is delta rpm", rpm);
- fclose(fp);
- solv_chksum_free(chksumh, 0);
- solv_free(rpmhead);
- return 0;
- }
- if (chksumh)
- while ((l = fread(lead, 1, sizeof(lead), fp)) > 0)
- solv_chksum_add(chksumh, lead, l);
- fclose(fp);
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- if (!rpm2solv(pool, repo, data, s, rpmhead, flags & ~(RPM_ADD_WITH_HDRID | RPM_ADD_WITH_PKGID)))
- {
- repo_free_solvable(repo, s - pool->solvables, 1);
- solv_chksum_free(chksumh, 0);
- solv_free(rpmhead);
- return 0;
- }
- if (!(flags & REPO_NO_LOCATION))
- repodata_set_location(data, s - pool->solvables, 0, 0, rpm);
- if (S_ISREG(stb.st_mode))
- repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
- repodata_set_num(data, s - pool->solvables, SOLVABLE_HEADEREND, headerend);
- if (pkgidtype)
- repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, pkgidtype, pkgid);
- if (hdridtype)
- repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_HDRID, hdridtype, hdrid);
- if (leadsigidtype)
- repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_LEADSIGID, leadsigidtype, leadsigid);
- if (chksumh)
- {
- repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, chksumtype, solv_chksum_get(chksumh, 0));
- chksumh = solv_chksum_free(chksumh, 0);
- }
- solv_free(rpmhead);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return s - pool->solvables;
-}
-
-Id
-repo_add_rpm_handle(Repo *repo, void *rpmhandle, int flags)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- RpmHead *rpmhead = rpmhandle;
- Solvable *s;
- char *payloadformat;
-
- data = repo_add_repodata(repo, flags);
- if (headexists(rpmhead, TAG_PATCHESNAME))
- {
- pool_error(pool, -1, "is a patch rpm");
- return 0;
- }
- payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
- if (payloadformat && !strcmp(payloadformat, "drpm"))
- {
- /* this is a delta rpm */
- pool_error(pool, -1, "is a delta rpm");
- return 0;
- }
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- if (!rpm2solv(pool, repo, data, s, rpmhead, flags))
- {
- repo_free_solvable(repo, s - pool->solvables, 1);
- return 0;
- }
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return s - pool->solvables;
-}
-
-static inline void
-linkhash(const char *lt, char *hash)
-{
- unsigned int r = 0;
- const unsigned char *str = (const unsigned char *)lt;
- int l, c;
-
- l = strlen(lt);
- while ((c = *str++) != 0)
- r += (r << 3) + c;
- sprintf(hash, "%08x%08x%08x%08x", r, l, 0, 0);
-}
-
-void
-rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, const char *, struct filelistinfo *), void *cbdata)
-{
- RpmHead *rpmhead = rpmhandle;
- char **bn;
- char **dn;
- char **md = 0;
- char **lt = 0;
- unsigned int *di, diidx;
- unsigned int *co = 0;
- unsigned int *ff = 0;
- unsigned int lastdir;
- int lastdirl;
- unsigned int *fm;
- int cnt, dcnt, cnt2;
- int i, l1, l;
- char *space = 0;
- int spacen = 0;
- char md5[33];
- struct filelistinfo info;
-
- dn = headstringarray(rpmhead, TAG_DIRNAMES, &dcnt);
- if (!dn)
- return;
- if ((flags & RPM_ITERATE_FILELIST_ONLYDIRS) != 0)
- {
- for (i = 0; i < dcnt; i++)
- (*cb)(cbdata, dn[i], 0);
- solv_free(dn);
- return;
- }
- bn = headstringarray(rpmhead, TAG_BASENAMES, &cnt);
- if (!bn)
- {
- solv_free(dn);
- return;
- }
- di = headint32array(rpmhead, TAG_DIRINDEXES, &cnt2);
- if (!di || cnt != cnt2)
- {
- solv_free(di);
- solv_free(bn);
- solv_free(dn);
- return;
- }
- fm = headint16array(rpmhead, TAG_FILEMODES, &cnt2);
- if (!fm || cnt != cnt2)
- {
- solv_free(fm);
- solv_free(di);
- solv_free(bn);
- solv_free(dn);
- return;
- }
- if ((flags & RPM_ITERATE_FILELIST_WITHMD5) != 0)
- {
- md = headstringarray(rpmhead, TAG_FILEMD5S, &cnt2);
- if (!md || cnt != cnt2)
- {
- solv_free(md);
- solv_free(fm);
- solv_free(di);
- solv_free(bn);
- solv_free(dn);
- return;
- }
- }
- if ((flags & RPM_ITERATE_FILELIST_WITHCOL) != 0)
- {
- co = headint32array(rpmhead, TAG_FILECOLORS, &cnt2);
- if (!co || cnt != cnt2)
- {
- solv_free(co);
- solv_free(md);
- solv_free(fm);
- solv_free(di);
- solv_free(bn);
- solv_free(dn);
- return;
- }
- }
- if ((flags & RPM_ITERATE_FILELIST_NOGHOSTS) != 0)
- {
- ff = headint32array(rpmhead, TAG_FILEFLAGS, &cnt2);
- if (!ff || cnt != cnt2)
- {
- solv_free(ff);
- solv_free(co);
- solv_free(md);
- solv_free(fm);
- solv_free(di);
- solv_free(bn);
- solv_free(dn);
- return;
- }
- }
- lastdir = dcnt;
- lastdirl = 0;
- memset(&info, 0, sizeof(info));
- for (i = 0; i < cnt; i++)
- {
- if (ff && (ff[i] & FILEFLAG_GHOST) != 0)
- continue;
- diidx = di[i];
- if (diidx >= dcnt)
- continue;
- l1 = lastdir == diidx ? lastdirl : strlen(dn[diidx]);
- l = l1 + strlen(bn[i]) + 1;
- if (l > spacen)
- {
- spacen = l + 16;
- space = solv_realloc(space, spacen);
- }
- if (lastdir != diidx)
- {
- strcpy(space, dn[diidx]);
- lastdir = diidx;
- lastdirl = l1;
- }
- strcpy(space + l1, bn[i]);
- info.diridx = diidx;
- info.dirlen = l1;
- if (fm)
- info.mode = fm[i];
- if (md)
- {
- info.digest = md[i];
- if (fm && S_ISLNK(fm[i]))
- {
- info.digest = 0;
- if (!lt)
- {
- lt = headstringarray(rpmhead, TAG_FILELINKTOS, &cnt2);
- if (cnt != cnt2)
- lt = solv_free(lt);
- }
- if (lt)
- {
- linkhash(lt[i], md5);
- info.digest = md5;
- }
- }
- if (!info.digest)
- {
- sprintf(md5, "%08x%08x%08x%08x", (fm[i] >> 12) & 65535, 0, 0, 0);
- info.digest = md5;
- }
- }
- if (co)
- info.color = co[i];
- (*cb)(cbdata, space, &info);
- }
- solv_free(space);
- solv_free(lt);
- solv_free(md);
- solv_free(fm);
- solv_free(di);
- solv_free(bn);
- solv_free(dn);
- solv_free(co);
- solv_free(ff);
-}
-
-char *
-rpm_query(void *rpmhandle, Id what)
-{
- const char *name, *arch, *sourcerpm;
- char *evr, *r;
- int l;
-
- RpmHead *rpmhead = rpmhandle;
- r = 0;
- switch (what)
- {
- case 0:
- name = headstring(rpmhead, TAG_NAME);
- if (!name)
- name = "";
- sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
- if (sourcerpm || (rpmhead->forcebinary && !headexists(rpmhead, TAG_SOURCEPACKAGE)))
- arch = headstring(rpmhead, TAG_ARCH);
- else
- {
- if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
- arch = "nosrc";
- else
- arch = "src";
- }
- if (!arch)
- arch = "noarch";
- evr = headtoevr(rpmhead);
- l = strlen(name) + 1 + strlen(evr ? evr : "") + 1 + strlen(arch) + 1;
- r = solv_malloc(l);
- sprintf(r, "%s-%s.%s", name, evr ? evr : "", arch);
- solv_free(evr);
- break;
- case SOLVABLE_NAME:
- name = headstring(rpmhead, TAG_NAME);
- r = solv_strdup(name);
- break;
- case SOLVABLE_SUMMARY:
- name = headstring(rpmhead, TAG_SUMMARY);
- r = solv_strdup(name);
- break;
- case SOLVABLE_DESCRIPTION:
- name = headstring(rpmhead, TAG_DESCRIPTION);
- r = solv_strdup(name);
- break;
- case SOLVABLE_EVR:
- r = headtoevr(rpmhead);
- break;
- }
- return r;
-}
-
-unsigned long long
-rpm_query_num(void *rpmhandle, Id what, unsigned long long notfound)
-{
- RpmHead *rpmhead = rpmhandle;
- unsigned int u32;
-
- switch (what)
- {
- case SOLVABLE_INSTALLTIME:
- u32 = headint32(rpmhead, TAG_INSTALLTIME);
- return u32 ? u32 : notfound;
- }
- return notfound;
-}
-
-int
-rpm_installedrpmdbids(void *rpmstate, const char *index, const char *match, Queue *rpmdbidq)
-{
- struct rpmdbentry *entries;
- int nentries, i;
-
- entries = getinstalledrpmdbids(rpmstate, index ? index : "Name", match, &nentries, 0);
- if (rpmdbidq)
- {
- queue_empty(rpmdbidq);
- for (i = 0; i < nentries; i++)
- queue_push(rpmdbidq, entries[i].rpmdbid);
- }
- solv_free(entries);
- return nentries;
-}
-
-void *
-rpm_byrpmdbid(void *rpmstate, Id rpmdbid)
-{
- struct rpmdbstate *state = rpmstate;
- int r;
-
- r = getrpmdbid(state, rpmdbid);
- if (!r)
- pool_error(state->pool, 0, "header #%d not in database", rpmdbid);
- return r <= 0 ? 0 : state->rpmhead;
-}
-
-void *
-rpm_byfp(void *rpmstate, FILE *fp, const char *name)
-{
- struct rpmdbstate *state = rpmstate;
- /* int headerstart, headerend; */
- RpmHead *rpmhead;
- unsigned int sigdsize, sigcnt, l;
- unsigned char lead[4096];
- int forcebinary = 0;
-
- if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
- {
- pool_error(state->pool, 0, "%s: not a rpm", name);
- return 0;
- }
- forcebinary = lead[6] != 0 || lead[7] != 1;
- if (lead[78] != 0 || lead[79] != 5)
- {
- pool_error(state->pool, 0, "%s: not a V5 header", name);
- return 0;
- }
- if (getu32(lead + 96) != 0x8eade801)
- {
- pool_error(state->pool, 0, "%s: bad signature header", name);
- return 0;
- }
- sigcnt = getu32(lead + 96 + 8);
- sigdsize = getu32(lead + 96 + 12);
- if (sigcnt >= 0x100000 || sigdsize >= 0x100000)
- {
- pool_error(state->pool, 0, "%s: bad signature header", name);
- return 0;
- }
- sigdsize += sigcnt * 16;
- sigdsize = (sigdsize + 7) & ~7;
- /* headerstart = 96 + 16 + sigdsize; */
- while (sigdsize)
- {
- l = sigdsize > 4096 ? 4096 : sigdsize;
- if (fread(lead, l, 1, fp) != 1)
- {
- pool_error(state->pool, 0, "%s: unexpected EOF", name);
- return 0;
- }
- sigdsize -= l;
- }
- if (fread(lead, 16, 1, fp) != 1)
- {
- pool_error(state->pool, 0, "%s: unexpected EOF", name);
- return 0;
- }
- if (getu32(lead) != 0x8eade801)
- {
- pool_error(state->pool, 0, "%s: bad header", name);
- return 0;
- }
- sigcnt = getu32(lead + 8);
- sigdsize = getu32(lead + 12);
- if (sigcnt >= 0x100000 || sigdsize >= 0x2000000)
- {
- pool_error(state->pool, 0, "%s: bad header", name);
- return 0;
- }
- l = sigdsize + sigcnt * 16;
- /* headerend = headerstart + 16 + l; */
- if (l > state->rpmheadsize)
- {
- state->rpmheadsize = l + 128;
- state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
- }
- rpmhead = state->rpmhead;
- if (fread(rpmhead->data, l, 1, fp) != 1)
- {
- pool_error(state->pool, 0, "%s: unexpected EOF", name);
- return 0;
- }
- rpmhead->forcebinary = forcebinary;
- rpmhead->cnt = sigcnt;
- rpmhead->dcnt = sigdsize;
- rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
- return rpmhead;
-}
-
-#ifdef ENABLE_RPMDB_BYRPMHEADER
-
-void *
-rpm_byrpmh(void *rpmstate, Header h)
-{
- struct rpmdbstate *state = rpmstate;
- const unsigned char *uh;
- unsigned int sigdsize, sigcnt, l;
- RpmHead *rpmhead;
-
-#ifndef RPM5
- uh = headerUnload(h);
-#else
- uh = headerUnload(h, NULL);
-#endif
- if (!uh)
- return 0;
- sigcnt = getu32(uh);
- sigdsize = getu32(uh + 4);
- l = sigdsize + sigcnt * 16;
- if (l > state->rpmheadsize)
- {
- state->rpmheadsize = l + 128;
- state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
- }
- rpmhead = state->rpmhead;
- memcpy(rpmhead->data, uh + 8, l - 8);
- free((void *)uh);
- rpmhead->forcebinary = 0;
- rpmhead->cnt = sigcnt;
- rpmhead->dcnt = sigdsize;
- rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
- return rpmhead;
-}
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (c) 2007-2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include "queue.h"
-#include "repo.h"
-
-struct headerToken_s;
-
-extern int repo_add_rpmdb(Repo *repo, Repo *ref, int flags);
-extern int repo_add_rpmdb_reffp(Repo *repo, FILE *reffp, int flags);
-extern Id repo_add_rpm(Repo *repo, const char *rpm, int flags);
-
-#define RPMDB_REPORT_PROGRESS (1 << 8)
-#define RPM_ADD_WITH_PKGID (1 << 9)
-#define RPM_ADD_NO_FILELIST (1 << 10)
-#define RPM_ADD_NO_RPMLIBREQS (1 << 11)
-#define RPM_ADD_WITH_SHA1SUM (1 << 12)
-#define RPM_ADD_WITH_SHA256SUM (1 << 13)
-#define RPM_ADD_TRIGGERS (1 << 14)
-#define RPM_ADD_WITH_HDRID (1 << 15)
-#define RPM_ADD_WITH_LEADSIGID (1 << 16)
-#define RPM_ADD_WITH_CHANGELOG (1 << 17)
-#define RPM_ADD_FILTERED_FILELIST (1 << 18)
-
-#define RPMDB_EMPTY_REFREPO (1 << 30) /* internal */
-
-#define RPM_ITERATE_FILELIST_ONLYDIRS (1 << 0)
-#define RPM_ITERATE_FILELIST_WITHMD5 (1 << 1)
-#define RPM_ITERATE_FILELIST_WITHCOL (1 << 2)
-#define RPM_ITERATE_FILELIST_NOGHOSTS (1 << 3)
-
-/* create and free internal state, rootdir is the rootdir of the rpm database */
-extern void *rpm_state_create(Pool *pool, const char *rootdir);
-extern void *rpm_state_free(void *rpmstate);
-
-/* return all matching rpmdbids */
-extern int rpm_installedrpmdbids(void *rpmstate, const char *index, const char *match, Queue *rpmdbidq);
-
-/* return handles to a rpm header */
-extern void *rpm_byrpmdbid(void *rpmstate, Id rpmdbid);
-extern void *rpm_byfp(void *rpmstate, FILE *fp, const char *name);
-extern void *rpm_byrpmh(void *rpmstate, struct headerToken_s *h);
-
-/* operations on a rpm header handle */
-
-struct filelistinfo {
- unsigned int dirlen;
- unsigned int diridx;
- const char *digest;
- unsigned int mode;
- unsigned int color;
-};
-
-extern char *rpm_query(void *rpmhandle, Id what);
-extern unsigned long long rpm_query_num(void *rpmhandle, Id what, unsigned long long notfound);
-extern void rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, const char *, struct filelistinfo *), void *cbdata);
-extern Id repo_add_rpm_handle(Repo *repo, void *rpmhandle, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <expat.h>
-
-#include "pool.h"
-#include "repo.h"
-#define DISABLE_SPLIT
-#include "tools_util.h"
-#include "repo_rpmmd.h"
-#include "chksum.h"
-#ifdef ENABLE_COMPLEX_DEPS
-#include "pool_parserpmrichdep.h"
-#endif
-
-enum state {
- STATE_START,
-
- STATE_SOLVABLE,
-
- STATE_NAME,
- STATE_ARCH,
- STATE_VERSION,
-
- /* package rpm-md */
- STATE_LOCATION,
- STATE_CHECKSUM,
- STATE_RPM_GROUP,
- STATE_RPM_LICENSE,
-
- /* resobject attributes */
- STATE_SUMMARY,
- STATE_DESCRIPTION,
- STATE_DISTRIBUTION,
- STATE_PACKAGER,
- STATE_URL,
- STATE_INSNOTIFY,
- STATE_DELNOTIFY,
- STATE_VENDOR,
- STATE_SIZE,
- STATE_TIME,
- STATE_DOWNLOADSIZE,
- STATE_INSTALLTIME,
- STATE_INSTALLONLY,
-
- /* Novell/SUSE extended attributes */
- STATE_EULA,
- STATE_KEYWORD,
- STATE_DISKUSAGE,
- STATE_DIRS,
- STATE_DIR,
-
- /* patch */
- STATE_ID,
- STATE_TIMESTAMP,
- STATE_AFFECTSPKG,
- STATE_REBOOTNEEDED,
-
- /* pattern attributes */
- STATE_CATEGORY, /* pattern and patches */
- STATE_ORDER,
- STATE_INCLUDES,
- STATE_INCLUDESENTRY,
- STATE_EXTENDS,
- STATE_EXTENDSENTRY,
- STATE_SCRIPT,
- STATE_ICON,
- STATE_USERVISIBLE,
- STATE_DEFAULT,
- STATE_INSTALL_TIME,
-
- /* product */
- STATE_RELNOTESURL,
- STATE_UPDATEURL,
- STATE_OPTIONALURL,
- STATE_FLAG,
-
- /* rpm-md dependencies inside the
- format tag */
- STATE_PROVIDES,
- STATE_REQUIRES,
- STATE_OBSOLETES,
- STATE_CONFLICTS,
- STATE_RECOMMENDS,
- STATE_SUPPLEMENTS,
- STATE_SUGGESTS,
- STATE_ENHANCES,
- STATE_FRESHENS,
- STATE_SOURCERPM,
- STATE_HEADERRANGE,
-
- STATE_PROVIDESENTRY,
- STATE_REQUIRESENTRY,
- STATE_OBSOLETESENTRY,
- STATE_CONFLICTSENTRY,
- STATE_RECOMMENDSENTRY,
- STATE_SUPPLEMENTSENTRY,
- STATE_SUGGESTSENTRY,
- STATE_ENHANCESENTRY,
- STATE_FRESHENSENTRY,
-
- STATE_FILE,
-
- STATE_CHANGELOG,
-
- /* general */
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-static struct stateswitch stateswitches[] = {
- /** fake tag used to enclose 2 different xml files in one **/
- { STATE_START, "rpmmd", STATE_START, 0 },
-
- /** tags for different package data, we just ignore the tag **/
- { STATE_START, "metadata", STATE_START, 0 },
- { STATE_START, "otherdata", STATE_START, 0 },
- { STATE_START, "filelists", STATE_START, 0 },
- { STATE_START, "diskusagedata", STATE_START, 0 },
- { STATE_START, "susedata", STATE_START, 0 },
-
- { STATE_START, "product", STATE_SOLVABLE, 0 },
- { STATE_START, "pattern", STATE_SOLVABLE, 0 },
- { STATE_START, "patch", STATE_SOLVABLE, 0 },
- { STATE_START, "package", STATE_SOLVABLE, 0 },
-
- { STATE_SOLVABLE, "name", STATE_NAME, 1 },
- { STATE_SOLVABLE, "arch", STATE_ARCH, 1 },
- { STATE_SOLVABLE, "version", STATE_VERSION, 0 },
-
- /* package attributes rpm-md */
- { STATE_SOLVABLE, "location", STATE_LOCATION, 0 },
- { STATE_SOLVABLE, "checksum", STATE_CHECKSUM, 1 },
-
- /* resobject attributes */
-
- { STATE_SOLVABLE, "summary", STATE_SUMMARY, 1 },
- { STATE_SOLVABLE, "description", STATE_DESCRIPTION, 1 },
- { STATE_SOLVABLE, "distribution", STATE_DISTRIBUTION, 1 },
- { STATE_SOLVABLE, "url", STATE_URL, 1 },
- { STATE_SOLVABLE, "packager", STATE_PACKAGER, 1 },
- { STATE_SOLVABLE, "vendor", STATE_VENDOR, 1 },
- { STATE_SOLVABLE, "size", STATE_SIZE, 0 },
- { STATE_SOLVABLE, "archive-size", STATE_DOWNLOADSIZE, 1 },
- { STATE_SOLVABLE, "install-time", STATE_INSTALLTIME, 1 },
- { STATE_SOLVABLE, "install-only", STATE_INSTALLONLY, 1 },
- { STATE_SOLVABLE, "time", STATE_TIME, 0 },
-
- /* extended Novell/SUSE attributes (susedata.xml) */
- { STATE_SOLVABLE, "eula", STATE_EULA, 1 },
- { STATE_SOLVABLE, "keyword", STATE_KEYWORD, 1 },
- { STATE_SOLVABLE, "diskusage", STATE_DISKUSAGE, 0 },
-
- /* pattern attribute */
- { STATE_SOLVABLE, "script", STATE_SCRIPT, 1 },
- { STATE_SOLVABLE, "icon", STATE_ICON, 1 },
- { STATE_SOLVABLE, "uservisible", STATE_USERVISIBLE, 1 },
- { STATE_SOLVABLE, "category", STATE_CATEGORY, 1 },
- { STATE_SOLVABLE, "order", STATE_ORDER, 1 },
- { STATE_SOLVABLE, "includes", STATE_INCLUDES, 0 },
- { STATE_SOLVABLE, "extends", STATE_EXTENDS, 0 },
- { STATE_SOLVABLE, "default", STATE_DEFAULT, 1 },
- { STATE_SOLVABLE, "install-time", STATE_INSTALL_TIME, 1 },
-
- /* product attributes */
- /* note the product type is an attribute */
- { STATE_SOLVABLE, "release-notes-url", STATE_RELNOTESURL, 1 },
- { STATE_SOLVABLE, "update-url", STATE_UPDATEURL, 1 },
- { STATE_SOLVABLE, "optional-url", STATE_OPTIONALURL, 1 },
- { STATE_SOLVABLE, "flag", STATE_FLAG, 1 },
-
- { STATE_SOLVABLE, "rpm:vendor", STATE_VENDOR, 1 },
- { STATE_SOLVABLE, "rpm:group", STATE_RPM_GROUP, 1 },
- { STATE_SOLVABLE, "rpm:license", STATE_RPM_LICENSE, 1 },
-
- /* rpm-md dependencies */
- { STATE_SOLVABLE, "rpm:provides", STATE_PROVIDES, 0 },
- { STATE_SOLVABLE, "rpm:requires", STATE_REQUIRES, 0 },
- { STATE_SOLVABLE, "rpm:obsoletes", STATE_OBSOLETES, 0 },
- { STATE_SOLVABLE, "rpm:conflicts", STATE_CONFLICTS, 0 },
- { STATE_SOLVABLE, "rpm:recommends", STATE_RECOMMENDS , 0 },
- { STATE_SOLVABLE, "rpm:supplements", STATE_SUPPLEMENTS, 0 },
- { STATE_SOLVABLE, "rpm:suggests", STATE_SUGGESTS, 0 },
- { STATE_SOLVABLE, "rpm:enhances", STATE_ENHANCES, 0 },
- { STATE_SOLVABLE, "rpm:freshens", STATE_FRESHENS, 0 },
- { STATE_SOLVABLE, "rpm:sourcerpm", STATE_SOURCERPM, 1 },
- { STATE_SOLVABLE, "rpm:header-range", STATE_HEADERRANGE, 0 },
- { STATE_SOLVABLE, "file", STATE_FILE, 1 },
- { STATE_SOLVABLE, "changelog", STATE_CHANGELOG, 1 },
-
- /* extended Novell/SUSE diskusage attributes (susedata.xml) */
- { STATE_DISKUSAGE, "dirs", STATE_DIRS, 0 },
- { STATE_DIRS, "dir", STATE_DIR, 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_INCLUDES, "item", STATE_INCLUDESENTRY, 0 },
- { STATE_EXTENDS, "item", STATE_EXTENDSENTRY, 0 },
-
- { NUMSTATES}
-};
-
-struct parsedata {
- int ret;
- Pool *pool;
- Repo *repo;
- Repodata *data;
- char *kind;
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
- Solvable *solvable;
- Offset freshens;
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
- struct joindata jd;
- /* temporal to store attribute tag language */
- const char *tmplang;
- Id chksumtype;
- Id handle;
- XML_Parser *parser;
- Id (*dirs)[3]; /* dirid, size, nfiles */
- int ndirs;
- const char *language; /* default language */
- Id langcache[ID_NUM_INTERNAL]; /* cache for the default language */
-
- Id lastdir;
- char *lastdirstr;
- int lastdirstrl;
-
- Id changelog_handle;
-
- /** Hash to maps checksums to solv */
- Stringpool cspool;
- /** Cache of known checksums to solvable id */
- Id *cscache;
- /* the current longest index in the table */
- int ncscache;
-};
-
-static Id
-langtag(struct parsedata *pd, Id tag, const char *language)
-{
- if (language)
- {
- if (!language[0] || !strcmp(language, "en"))
- return tag;
- return pool_id2langid(pd->pool, tag, language, 1);
- }
- if (!pd->language)
- return tag;
- if (tag >= ID_NUM_INTERNAL)
- return pool_id2langid(pd->pool, tag, pd->language, 1);
- if (!pd->langcache[tag])
- pd->langcache[tag] = pool_id2langid(pd->pool, tag, pd->language, 1);
- return pd->langcache[tag];
-}
-
-static int
-id3_cmp (const void *v1, const void *v2, void *dp)
-{
- Id *i1 = (Id*)v1;
- Id *i2 = (Id*)v2;
- return i1[0] - i2[0];
-}
-
-static void
-commit_diskusage (struct parsedata *pd, Id handle)
-{
- int i;
- Dirpool *dp = &pd->data->dirpool;
- /* Now sort in dirid order. This ensures that parents come before
- their children. */
- if (pd->ndirs > 1)
- solv_sort(pd->dirs, pd->ndirs, sizeof (pd->dirs[0]), id3_cmp, 0);
- /* Substract leaf numbers from all parents to make the numbers
- non-cumulative. This must be done post-order (i.e. all leafs
- adjusted before parents). We ensure this by starting at the end of
- the array moving to the start, hence seeing leafs before parents. */
- for (i = pd->ndirs; i--;)
- {
- Id p = dirpool_parent(dp, pd->dirs[i][0]);
- int j = i;
- for (; p; p = dirpool_parent(dp, p))
- {
- for (; j--;)
- if (pd->dirs[j][0] == p)
- break;
- if (j >= 0)
- {
- if (pd->dirs[j][1] < pd->dirs[i][1])
- pd->dirs[j][1] = 0;
- else
- pd->dirs[j][1] -= pd->dirs[i][1];
- if (pd->dirs[j][2] < pd->dirs[i][2])
- pd->dirs[j][2] = 0;
- else
- pd->dirs[j][2] -= pd->dirs[i][2];
- }
- else
- /* Haven't found this parent in the list, look further if
- we maybe find the parents parent. */
- j = i;
- }
- }
-#if 0
- char sbuf[1024];
- char *buf = sbuf;
- unsigned slen = sizeof (sbuf);
- for (i = 0; i < pd->ndirs; i++)
- {
- dir2str (attr, pd->dirs[i][0], &buf, &slen);
- fprintf (stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
- }
- if (buf != sbuf)
- free (buf);
-#endif
- for (i = 0; i < pd->ndirs; i++)
- if (pd->dirs[i][1] || pd->dirs[i][2])
- {
- repodata_add_dirnumnum(pd->data, handle, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
- }
- pd->ndirs = 0;
-}
-
-
-/*
- * makeevr_atts
- * parse 'epoch', 'ver' and 'rel', return evr Id
- *
- */
-
-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 && (!*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 = solv_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 pool_str2id(pool, pd->content, 1);
-}
-
-
-/*
- * find_attr
- * find value for xml attribute
- * I: txt, name of attribute
- * I: atts, list of key/value attributes
- * O: pointer to value of matching key, or NULL
- *
- */
-
-static inline const char *
-find_attr(const char *txt, const char **atts)
-{
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, txt))
- return atts[1];
- }
- return 0;
-}
-
-
-/*
- * dependency relations
- */
-
-static char *flagtab[] = {
- "GT",
- "EQ",
- "GE",
- "LT",
- "NE",
- "LE"
-};
-
-
-/*
- * adddep
- * parse attributes to reldep Id
- *
- */
-
-static unsigned int
-adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
-{
- Id id, marker;
- const char *n, *f, *k;
- const char **a;
-
- n = f = k = 0;
- marker = isreq ? -SOLVABLE_PREREQMARKER : 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')
- marker = SOLVABLE_PREREQMARKER;
- }
- 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 = solv_realloc(pd->content, l + 256);
- pd->acontent = l + 256;
- }
- sprintf(pd->content, "%s:%s", k, n);
- id = pool_str2id(pool, pd->content, 1);
- }
-#ifdef ENABLE_COMPLEX_DEPS
- else if (!f && n[0] == '(')
- {
- id = pool_parserpmrichdep(pool, n);
- if (!id)
- return olddeps;
- }
-#endif
- else
- id = pool_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 = pool_rel2id(pool, id, evr, flags, 1);
- }
-#if 0
- fprintf(stderr, "new dep %s\n", pool_dep2str(pool, id));
-#endif
- return repo_addid_dep(pd->repo, olddeps, id, marker);
-}
-
-
-/*
- * set_description_author
- *
- */
-static void
-set_description_author(Repodata *data, Id handle, char *str, struct parsedata *pd)
-{
- char *aut, *p;
-
- if (!str || !*str)
- return;
- for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
- if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
- break;
- if (aut)
- {
- /* oh my, found SUSE special author section */
- int l = aut - str;
- str[l] = 0;
- while (l > 0 && str[l - 1] == '\n')
- str[--l] = 0;
- if (l)
- repodata_set_str(data, handle, langtag(pd, SOLVABLE_DESCRIPTION, pd->tmplang), str);
- p = aut + 19;
- aut = str; /* copy over */
- while (*p == ' ' || *p == '\n')
- p++;
- while (*p)
- {
- if (*p == '\n')
- {
- *aut++ = *p++;
- while (*p == ' ')
- p++;
- continue;
- }
- *aut++ = *p++;
- }
- while (aut != str && aut[-1] == '\n')
- aut--;
- *aut = 0;
- if (*str)
- repodata_set_str(data, handle, SOLVABLE_AUTHORS, str);
- }
- else if (*str)
- repodata_set_str(data, handle, langtag(pd, SOLVABLE_DESCRIPTION, pd->tmplang), str);
-}
-
-
-/*-----------------------------------------------*/
-/* XML callbacks */
-
-/*
- * startElement
- * XML callback
- *
- */
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- struct stateswitch *sw;
- const char *str;
- Id handle = pd->handle;
- const char *pkgid;
-
- /* fprintf(stderr, "into %s, from %d, depth %d, statedepth %d\n", name, pd->state, pd->depth, pd->statedepth); */
-
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
-
- if (pd->state == STATE_START && !strcmp(name, "patterns"))
- return;
- if (pd->state == STATE_START && !strcmp(name, "products"))
- return;
-#if 0
- if (pd->state == STATE_START && !strcmp(name, "metadata"))
- return;
-#endif
- if (pd->state == STATE_SOLVABLE && !strcmp(name, "format"))
- return;
-
- pd->depth++;
- if (!pd->swtab[pd->state])
- return;
- 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;
-
- if (!s && pd->state != STATE_SOLVABLE)
- return;
-
- switch(pd->state)
- {
- case STATE_SOLVABLE:
- pd->kind = 0;
- if (name[2] == 't' && name[3] == 't')
- pd->kind = "pattern";
- else if (name[1] == 'r')
- pd->kind = "product";
- else if (name[2] == 't' && name[3] == 'c')
- pd->kind = "patch";
-
- /* to support extension metadata files like others.xml which
- have the following structure:
-
- <otherdata xmlns="http://linux.duke.edu/metadata/other"
- packages="101">
- <package pkgid="b78f8664cd90efe42e09a345e272997ef1b53c18"
- name="zaptel-kmp-default"
- arch="i586"><version epoch="0"
- ver="1.2.10_2.6.22_rc4_git6_2" rel="70"/>
- ...
-
- we need to check if the pkgid is there and if it matches
- an already seen package, that means we don't need to create
- a new solvable but just append the attributes to the existing
- one.
- */
- if ((pkgid = find_attr("pkgid", atts)) != NULL)
- {
- /* look at the checksum cache */
- Id index = stringpool_str2id(&pd->cspool, pkgid, 0);
- if (!index || index >= pd->ncscache || !pd->cscache[index])
- {
- pool_debug(pool, SOLV_WARN, "the repository specifies extra information about package with checksum '%s', which does not exist in the repository.\n", pkgid);
- pd->solvable = 0;
- pd->handle = 0;
- break;
- }
- pd->solvable = pool_id2solvable(pool, pd->cscache[index]);
- }
- else
- {
- /* this is a new package */
- pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
- pd->freshens = 0;
- }
- pd->handle = handle = pd->solvable - pool->solvables;
- if (pd->kind && pd->kind[1] == 'r')
- {
- /* products can have a type */
- const char *type = find_attr("type", atts);
- if (type && *type)
- repodata_set_str(pd->data, handle, PRODUCT_TYPE, type);
- }
-#if 0
- fprintf(stderr, "package #%d\n", pd->solvable - pool->solvables);
-#endif
-
- break;
- case STATE_VERSION:
- s->evr = makeevr_atts(pool, pd, atts);
- break;
- case STATE_PROVIDES:
- s->provides = 0;
- break;
- case STATE_PROVIDESENTRY:
- s->provides = adddep(pool, pd, s->provides, atts, 0);
- break;
- case STATE_REQUIRES:
- s->requires = 0;
- break;
- case STATE_REQUIRESENTRY:
- s->requires = adddep(pool, pd, s->requires, atts, 1);
- break;
- case STATE_OBSOLETES:
- s->obsoletes = 0;
- break;
- case STATE_OBSOLETESENTRY:
- s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
- break;
- case STATE_CONFLICTS:
- s->conflicts = 0;
- break;
- case STATE_CONFLICTSENTRY:
- s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
- break;
- case STATE_RECOMMENDS:
- s->recommends = 0;
- break;
- case STATE_RECOMMENDSENTRY:
- s->recommends = adddep(pool, pd, s->recommends, atts, 0);
- break;
- case STATE_SUPPLEMENTS:
- s->supplements= 0;
- break;
- case STATE_SUPPLEMENTSENTRY:
- s->supplements = adddep(pool, pd, s->supplements, atts, 0);
- break;
- case STATE_SUGGESTS:
- s->suggests = 0;
- break;
- case STATE_SUGGESTSENTRY:
- s->suggests = adddep(pool, pd, s->suggests, atts, 0);
- break;
- case STATE_ENHANCES:
- s->enhances = 0;
- break;
- case STATE_ENHANCESENTRY:
- s->enhances = adddep(pool, pd, s->enhances, atts, 0);
- break;
- case STATE_FRESHENS:
- pd->freshens = 0;
- break;
- case STATE_FRESHENSENTRY:
- pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
- break;
- case STATE_EULA:
- case STATE_SUMMARY:
- case STATE_CATEGORY:
- case STATE_DESCRIPTION:
- pd->tmplang = join_dup(&pd->jd, find_attr("lang", atts));
- break;
- case STATE_USERVISIBLE:
- repodata_set_void(pd->data, handle, SOLVABLE_ISVISIBLE);
- break;
- case STATE_INCLUDESENTRY:
- str = find_attr("pattern", atts);
- if (str)
- repodata_add_poolstr_array(pd->data, handle, SOLVABLE_INCLUDES, join2(&pd->jd, "pattern", ":", str));
- break;
- case STATE_EXTENDSENTRY:
- str = find_attr("pattern", atts);
- if (str)
- repodata_add_poolstr_array(pd->data, handle, SOLVABLE_EXTENDS, join2(&pd->jd, "pattern", ":", str));
- break;
- case STATE_LOCATION:
- str = find_attr("href", atts);
- if (str)
- {
- repodata_set_location(pd->data, handle, 0, 0, str);
- str = find_attr("xml:base", atts);
- if (str)
- repodata_set_poolstr(pd->data, handle, SOLVABLE_MEDIABASE, str);
- }
- break;
- case STATE_CHECKSUM:
- str = find_attr("type", atts);
- pd->chksumtype = str && *str ? solv_chksum_str2type(str) : 0;
- if (!pd->chksumtype)
- pd->ret = pool_error(pool, -1, "line %d: unknown checksum type: %s", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), str ? str : "NULL");
- break;
- case STATE_TIME:
- {
- unsigned int t;
- str = find_attr("build", atts);
- if (str && (t = atoi(str)) != 0)
- repodata_set_num(pd->data, handle, SOLVABLE_BUILDTIME, t);
- break;
- }
- case STATE_SIZE:
- if ((str = find_attr("installed", atts)) != 0)
- repodata_set_num(pd->data, handle, SOLVABLE_INSTALLSIZE, strtoull(str, 0, 10));
- if ((str = find_attr("package", atts)) != 0)
- repodata_set_num(pd->data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(str, 0, 10));
- break;
- case STATE_HEADERRANGE:
- {
- unsigned int end;
- str = find_attr("end", atts);
- if (str && (end = atoi(str)) != 0)
- repodata_set_num(pd->data, handle, SOLVABLE_HEADEREND, end);
- break;
- }
- /*
- <diskusage>
- <dirs>
- <dir name="/" size="56" count="11"/>
- <dir name="usr/" size="56" count="11"/>
- <dir name="usr/bin/" size="38" count="10"/>
- <dir name="usr/share/" size="18" count="1"/>
- <dir name="usr/share/doc/" size="18" count="1"/>
- </dirs>
- </diskusage>
- */
- case STATE_DISKUSAGE:
- {
- /* Really, do nothing, wait for <dir> tag */
- break;
- }
- case STATE_DIR:
- {
- long filesz = 0, filenum = 0;
- Id dirid;
- if ((str = find_attr("name", atts)) != 0)
- dirid = repodata_str2dir(pd->data, str, 1);
- else
- {
- pd->ret = pool_error(pool, -1, "<dir .../> tag without 'name' attribute");
- break;
- }
- if (!dirid)
- dirid = repodata_str2dir(pd->data, "/", 1);
- if ((str = find_attr("size", atts)) != 0)
- filesz = strtol(str, 0, 0);
- if ((str = find_attr("count", atts)) != 0)
- filenum = strtol(str, 0, 0);
- pd->dirs = solv_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
- pd->dirs[pd->ndirs][0] = dirid;
- pd->dirs[pd->ndirs][1] = filesz;
- pd->dirs[pd->ndirs][2] = filenum;
- pd->ndirs++;
- break;
- }
- case STATE_CHANGELOG:
- pd->changelog_handle = repodata_new_handle(pd->data);
- if ((str = find_attr("date", atts)) != 0)
- repodata_set_num(pd->data, pd->changelog_handle, SOLVABLE_CHANGELOG_TIME, strtoull(str, 0, 10));
- if ((str = find_attr("author", atts)) != 0)
- repodata_set_str(pd->data, pd->changelog_handle, SOLVABLE_CHANGELOG_AUTHOR, str);
- break;
- default:
- break;
- }
-}
-
-
-/*
- * endElement
- * XML callback
- *
- */
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- Repo *repo = pd->repo;
- Id handle = pd->handle;
- Id id;
- char *p;
-
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
- /* printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth); */
- return;
- }
-
- /* ignore patterns & metadata */
- if (pd->state == STATE_START && !strcmp(name, "patterns"))
- return;
- if (pd->state == STATE_START && !strcmp(name, "products"))
- return;
-#if 0
- if (pd->state == STATE_START && !strcmp(name, "metadata"))
- return;
-#endif
- if (pd->state == STATE_SOLVABLE && !strcmp(name, "format"))
- return;
-
- pd->depth--;
- pd->statedepth--;
-
-
- if (!s)
- {
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
- return;
- }
-
- switch (pd->state)
- {
- case STATE_SOLVABLE:
- if (pd->kind && !s->name) /* add namespace in case of NULL name */
- s->name = pool_str2id(pool, join2(&pd->jd, pd->kind, ":", 0), 1);
- if (!s->arch)
- s->arch = ARCH_NOARCH;
- if (!s->evr)
- s->evr = ID_EMPTY; /* some patterns have this */
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, pd->freshens);
- s->conflicts = repo_fix_conflicts(repo, s->conflicts);
- pd->freshens = 0;
- pd->kind = 0;
- pd->solvable = s = 0;
- break;
- case STATE_NAME:
- if (pd->kind)
- s->name = pool_str2id(pool, join2(&pd->jd, pd->kind, ":", pd->content), 1);
- else
- s->name = pool_str2id(pool, pd->content, 1);
- break;
- case STATE_ARCH:
- s->arch = pool_str2id(pool, pd->content, 1);
- break;
- case STATE_VENDOR:
- s->vendor = pool_str2id(pool, pd->content, 1);
- break;
- case STATE_RPM_GROUP:
- repodata_set_poolstr(pd->data, handle, SOLVABLE_GROUP, pd->content);
- break;
- case STATE_RPM_LICENSE:
- repodata_set_poolstr(pd->data, handle, SOLVABLE_LICENSE, pd->content);
- break;
- case STATE_CHECKSUM:
- {
- Id index;
-
- if (!pd->chksumtype)
- break;
- if (strlen(pd->content) != 2 * solv_chksum_len(pd->chksumtype))
- {
- pd->ret = pool_error(pool, -1, "line %d: invalid checksum length for %s", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), solv_chksum_type2str(pd->chksumtype));
- break;
- }
- repodata_set_checksum(pd->data, handle, SOLVABLE_CHECKSUM, pd->chksumtype, pd->content);
- /* we save the checksum to solvable id relationship for extended
- metadata */
- index = stringpool_str2id(&pd->cspool, pd->content, 1 /* create it */);
- if (index >= pd->ncscache)
- {
- pd->cscache = solv_zextend(pd->cscache, pd->ncscache, index + 1 - pd->ncscache, sizeof(Id), 255);
- pd->ncscache = index + 1;
- }
- /* add the checksum to the cache */
- pd->cscache[index] = s - pool->solvables;
- break;
- }
- case STATE_FILE:
-#if 0
- id = pool_str2id(pool, pd->content, 1);
- s->provides = repo_addid_dep(repo, s->provides, id, SOLVABLE_FILEMARKER);
-#endif
- if ((p = strrchr(pd->content, '/')) != 0)
- {
- *p++ = 0;
- if (pd->lastdir && !strcmp(pd->lastdirstr, pd->content))
- {
- id = pd->lastdir;
- }
- else
- {
- int l;
- id = repodata_str2dir(pd->data, pd->content, 1);
- l = strlen(pd->content) + 1;
- if (l > pd->lastdirstrl)
- {
- pd->lastdirstrl = l + 128;
- pd->lastdirstr = solv_realloc(pd->lastdirstr, pd->lastdirstrl);
- }
- strcpy(pd->lastdirstr, pd->content);
- pd->lastdir = id;
- }
- }
- else
- {
- p = pd->content;
- id = 0;
- }
- if (!id)
- id = repodata_str2dir(pd->data, "/", 1);
- repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, id, p);
- break;
- case STATE_SUMMARY:
- repodata_set_str(pd->data, handle, langtag(pd, SOLVABLE_SUMMARY, pd->tmplang), pd->content);
- break;
- case STATE_DESCRIPTION:
- set_description_author(pd->data, handle, pd->content, pd);
- break;
- case STATE_CATEGORY:
- repodata_set_str(pd->data, handle, langtag(pd, SOLVABLE_CATEGORY, pd->tmplang), pd->content);
- break;
- case STATE_DISTRIBUTION:
- repodata_set_poolstr(pd->data, handle, SOLVABLE_DISTRIBUTION, pd->content);
- break;
- case STATE_URL:
- if (pd->content[0])
- repodata_set_str(pd->data, handle, SOLVABLE_URL, pd->content);
- break;
- case STATE_PACKAGER:
- if (pd->content[0])
- repodata_set_poolstr(pd->data, handle, SOLVABLE_PACKAGER, pd->content);
- break;
- case STATE_SOURCERPM:
- if (pd->content[0])
- repodata_set_sourcepkg(pd->data, handle, pd->content);
- break;
- case STATE_RELNOTESURL:
- if (pd->content[0])
- {
- repodata_add_poolstr_array(pd->data, handle, PRODUCT_URL, pd->content);
- repodata_add_idarray(pd->data, handle, PRODUCT_URL_TYPE, pool_str2id(pool, "releasenotes", 1));
- }
- break;
- case STATE_UPDATEURL:
- if (pd->content[0])
- {
- repodata_add_poolstr_array(pd->data, handle, PRODUCT_URL, pd->content);
- repodata_add_idarray(pd->data, handle, PRODUCT_URL_TYPE, pool_str2id(pool, "update", 1));
- }
- break;
- case STATE_OPTIONALURL:
- if (pd->content[0])
- {
- repodata_add_poolstr_array(pd->data, handle, PRODUCT_URL, pd->content);
- repodata_add_idarray(pd->data, handle, PRODUCT_URL_TYPE, pool_str2id(pool, "optional", 1));
- }
- break;
- case STATE_FLAG:
- if (pd->content[0])
- repodata_add_poolstr_array(pd->data, handle, PRODUCT_FLAGS, pd->content);
- break;
- case STATE_EULA:
- if (pd->content[0])
- repodata_set_str(pd->data, handle, langtag(pd, SOLVABLE_EULA, pd->tmplang), pd->content);
- break;
- case STATE_KEYWORD:
- if (pd->content[0])
- repodata_add_poolstr_array(pd->data, handle, SOLVABLE_KEYWORDS, pd->content);
- break;
- case STATE_DISKUSAGE:
- if (pd->ndirs)
- commit_diskusage(pd, handle);
- break;
- case STATE_ORDER:
- if (pd->content[0])
- repodata_set_str(pd->data, handle, SOLVABLE_ORDER, pd->content);
- break;
- case STATE_CHANGELOG:
- repodata_set_str(pd->data, pd->changelog_handle, SOLVABLE_CHANGELOG_TEXT, pd->content);
- repodata_add_flexarray(pd->data, handle, SOLVABLE_CHANGELOG, pd->changelog_handle);
- pd->changelog_handle = 0;
- break;
- default:
- break;
- }
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
- /* fprintf(stderr, "back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth); */
-}
-
-
-/*
- * characterData
- * XML callback
- *
- */
-
-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 = solv_realloc(pd->content, l + 256);
- pd->acontent = l + 256;
- }
- c = pd->content + pd->lcontent;
- pd->lcontent += len;
- while (len-- > 0)
- *c++ = *s++;
- *c = 0;
-}
-
-
-/*-----------------------------------------------*/
-/* 'main' */
-
-#define BUFF_SIZE 8192
-
-/*
- * repo_add_rpmmd
- * parse rpm-md metadata (primary, others)
- *
- */
-
-int
-repo_add_rpmmd(Repo *repo, FILE *fp, const char *language, int flags)
-{
- Pool *pool = repo->pool;
- struct parsedata pd;
- char buf[BUFF_SIZE];
- int i, l;
- struct stateswitch *sw;
- Repodata *data;
- unsigned int now;
- XML_Parser parser;
-
- now = solv_timems(0);
- data = repo_add_repodata(repo, flags);
-
- 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.repo = repo;
- pd.data = data;
-
- pd.content = solv_malloc(256);
- pd.acontent = 256;
- pd.lcontent = 0;
- pd.kind = 0;
- pd.language = language && *language && strcmp(language, "en") != 0 ? language : 0;
-
- /* initialize the string pool where we will store
- the package checksums we know about, to get an Id
- we can use in a cache */
- stringpool_init_empty(&pd.cspool);
- if ((flags & REPO_EXTEND_SOLVABLES) != 0)
- {
- /* setup join data */
- Dataiterator di;
- dataiterator_init(&di, pool, repo, 0, SOLVABLE_CHECKSUM, 0, 0);
- while (dataiterator_step(&di))
- {
- const char *str;
- int index;
-
- if (!solv_chksum_len(di.key->type))
- continue;
- str = repodata_chk2str(di.data, di.key->type, (const unsigned char *)di.kv.str);
- index = stringpool_str2id(&pd.cspool, str, 1);
- if (index >= pd.ncscache)
- {
- pd.cscache = solv_zextend(pd.cscache, pd.ncscache, index + 1 - pd.ncscache, sizeof(Id), 255);
- pd.ncscache = index + 1;
- }
- pd.cscache[index] = di.solvid;
- }
- dataiterator_free(&di);
- }
-
- parser = XML_ParserCreate(NULL);
- XML_SetUserData(parser, &pd);
- pd.parser = &parser;
- 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)
- {
- pd.ret = pool_error(pool, -1, "repo_rpmmd: %s at line %u:%u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- break;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
- solv_free(pd.content);
- solv_free(pd.lastdirstr);
- join_freemem(&pd.jd);
- stringpool_free(&pd.cspool);
- solv_free(pd.cscache);
- repodata_free_dircache(data);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_rpmmd took %d ms\n", solv_timems(now));
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
- return pd.ret;
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_rpmmd(Repo *repo, FILE *fp, const char *language, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "hash.h"
-#include "chksum.h"
-#include "tools_util.h"
-#include "repo_susetags.h"
-#ifdef ENABLE_COMPLEX_DEPS
-#include "pool_parserpmrichdep.h"
-#endif
-
-struct datashare {
- Id name;
- Id evr;
- Id arch;
-};
-
-struct parsedata {
- int ret;
- Pool *pool;
- Repo *repo;
- Repodata *data;
- char *kind;
- int flags;
- int last_found_source;
- struct datashare *share_with;
- int nshare;
- Id (*dirs)[3]; /* dirid, size, nfiles */
- int ndirs;
- struct joindata jd;
- char *language; /* the default language */
- Id langcache[ID_NUM_INTERNAL]; /* cache for the default language */
- int lineno;
- char *filelist;
- int afilelist; /* allocated */
- int nfilelist; /* used */
-};
-
-static char *flagtab[] = {
- ">",
- "=",
- ">=",
- "<",
- "!=",
- "<="
-};
-
-
-static Id
-langtag(struct parsedata *pd, Id tag, const char *language)
-{
- if (language && *language)
- return pool_id2langid(pd->repo->pool, tag, language, 1);
- if (!pd->language)
- return tag;
- if (tag >= ID_NUM_INTERNAL)
- return pool_id2langid(pd->repo->pool, tag, pd->language, 1);
- if (!pd->langcache[tag])
- pd->langcache[tag] = pool_id2langid(pd->repo->pool, tag, pd->language, 1);
- return pd->langcache[tag];
-}
-
-/*
- * adddep
- * create and add dependency
- */
-
-static unsigned int
-adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker, char *kind)
-{
- int i, flags;
- Id id, evrid;
- char *sp[4];
-
- if (line[6] == '/')
- {
- /* A file dependency. Do not try to parse it */
- id = pool_str2id(pool, line + 6, 1);
- }
-#ifdef ENABLE_COMPLEX_DEPS
- else if (line[6] == '(')
- {
- id = pool_parserpmrichdep(pool, line + 6);
- if (!id)
- {
- pd->ret = pool_error(pool, -1, "susetags: line %d: bad dependency: '%s'\n", pd->lineno, line);
- return olddeps;
- }
- }
-#endif
- else
- {
- i = split(line + 6, sp, 4); /* name, <op>, evr, ? */
- if (i != 1 && i != 3) /* expect either 'name' or 'name' <op> 'evr' */
- {
- pd->ret = pool_error(pool, -1, "susetags: line %d: bad dependency: '%s'\n", pd->lineno, line);
- return olddeps;
- }
- if (kind)
- id = pool_str2id(pool, join2(&pd->jd, kind, ":", sp[0]), 1);
- else
- id = pool_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)
- {
- if (!strcmp(sp[1], "<>"))
- flags = 4;
- else
- {
- pd->ret = pool_error(pool, -1, "susetags: line %d: unknown relation: '%s'\n", pd->lineno, sp[1]);
- return olddeps;
- }
- }
- id = pool_rel2id(pool, id, evrid, flags + 1, 1);
- }
- }
- return repo_addid_dep(pd->repo, olddeps, id, marker);
-}
-
-
-/*
- * add_source
- *
- */
-
-static void
-add_source(struct parsedata *pd, char *line, Solvable *s, Id handle)
-{
- Pool *pool = s->repo->pool;
- char *sp[5];
- Id name;
- Id arch;
- const char *evr, *sevr;
-
- if (split(line, sp, 5) != 4)
- {
- pd->ret = pool_error(pool, -1, "susetags: line %d: bad source line '%s'\n", pd->lineno, line);
- return;
- }
-
- name = pool_str2id(pool, sp[0], 1);
- arch = pool_str2id(pool, sp[3], 1); /* do this before id2str */
- evr = join2(&pd->jd, sp[1], "-", sp[2]);
- sevr = pool_id2str(pool, s->evr);
- if (sevr)
- {
- /* strip epoch */
- const char *p;
- for (p = sevr; *p >= '0' && *p <= '9'; p++)
- ;
- if (p != sevr && *p == ':' && p[1])
- sevr = p;
- }
- if (name == s->name)
- repodata_set_void(pd->data, handle, SOLVABLE_SOURCENAME);
- else
- repodata_set_id(pd->data, handle, SOLVABLE_SOURCENAME, name);
- if (sevr && !strcmp(sevr, evr))
- repodata_set_void(pd->data, handle, SOLVABLE_SOURCEEVR);
- else
- repodata_set_id(pd->data, handle, SOLVABLE_SOURCEEVR, pool_str2id(pool, evr, 1));
- repodata_set_constantid(pd->data, handle, SOLVABLE_SOURCEARCH, arch);
-}
-
-/*
- * add_dirline
- * add a line with directory information
- *
- */
-
-static void
-add_dirline(struct parsedata *pd, char *line)
-{
- char *sp[6];
- long filesz;
- long filenum;
- Id dirid;
- if (split(line, sp, 6) != 5)
- return;
- pd->dirs = solv_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
- filesz = strtol(sp[1], 0, 0);
- filesz += strtol(sp[2], 0, 0);
- filenum = strtol(sp[3], 0, 0);
- filenum += strtol(sp[4], 0, 0);
- /* hack: we know that there's room for a / */
- if (*sp[0] != '/')
- *--sp[0] = '/';
- dirid = repodata_str2dir(pd->data, sp[0], 1);
-#if 0
-fprintf(stderr, "%s -> %d\n", sp[0], dirid);
-#endif
- pd->dirs[pd->ndirs][0] = dirid;
- pd->dirs[pd->ndirs][1] = filesz;
- pd->dirs[pd->ndirs][2] = filenum;
- pd->ndirs++;
-}
-
-static void
-set_checksum(struct parsedata *pd, Repodata *data, Id handle, Id keyname, char *line)
-{
- char *sp[3];
- Id type;
- if (split(line, sp, 3) != 2)
- {
- pd->ret = pool_error(pd->pool, -1, "susetags: line %d: bad checksum line '%s'\n", pd->lineno, line);
- return;
- }
- type = solv_chksum_str2type(sp[0]);
- if (!type)
- {
- pd->ret = pool_error(pd->pool, -1, "susetags: line %d: unknown checksum type: '%s'\n", pd->lineno, sp[0]);
- return;
- }
- if (strlen(sp[1]) != 2 * solv_chksum_len(type))
- {
- pd->ret = pool_error(pd->pool, -1, "susetags: line %d: bad checksum length for type %s: '%s'\n", pd->lineno, sp[0], sp[1]);
- return;
- }
- repodata_set_checksum(data, handle, keyname, type, sp[1]);
-}
-
-
-/*
- * id3_cmp
- * compare
- *
- */
-
-static int
-id3_cmp(const void *v1, const void *v2, void *dp)
-{
- Id *i1 = (Id*)v1;
- Id *i2 = (Id*)v2;
- return i1[0] - i2[0];
-}
-
-
-/*
- * commit_diskusage
- *
- */
-
-static void
-commit_diskusage(struct parsedata *pd, Id handle)
-{
- int i;
- Dirpool *dp = &pd->data->dirpool;
- /* Now sort in dirid order. This ensures that parents come before
- their children. */
- if (pd->ndirs > 1)
- solv_sort(pd->dirs, pd->ndirs, sizeof(pd->dirs[0]), id3_cmp, 0);
- /* Substract leaf numbers from all parents to make the numbers
- non-cumulative. This must be done post-order (i.e. all leafs
- adjusted before parents). We ensure this by starting at the end of
- the array moving to the start, hence seeing leafs before parents. */
- for (i = pd->ndirs; i--;)
- {
- Id p = dirpool_parent(dp, pd->dirs[i][0]);
- int j = i;
- for (; p; p = dirpool_parent(dp, p))
- {
- for (; j--;)
- if (pd->dirs[j][0] == p)
- break;
- if (j >= 0)
- {
- if (pd->dirs[j][1] < pd->dirs[i][1])
- pd->dirs[j][1] = 0;
- else
- pd->dirs[j][1] -= pd->dirs[i][1];
- if (pd->dirs[j][2] < pd->dirs[i][2])
- pd->dirs[j][2] = 0;
- else
- pd->dirs[j][2] -= pd->dirs[i][2];
- }
- else
- /* Haven't found this parent in the list, look further if
- we maybe find the parents parent. */
- j = i;
- }
- }
-#if 0
- char sbuf[1024];
- char *buf = sbuf;
- unsigned slen = sizeof(sbuf);
- for (i = 0; i < pd->ndirs; i++)
- {
- dir2str(attr, pd->dirs[i][0], &buf, &slen);
- fprintf(stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
- }
- if (buf != sbuf)
- free (buf);
-#endif
- for (i = 0; i < pd->ndirs; i++)
- if (pd->dirs[i][1] || pd->dirs[i][2])
- {
- repodata_add_dirnumnum(pd->data, handle, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
- }
- pd->ndirs = 0;
-}
-
-
-/* Unfortunately "a"[0] is no constant expression in the C languages,
- so we need to pass the four characters individually :-/ */
-#define CTAG(a,b,c,d) ((unsigned)(((unsigned char)a) << 24) \
- | ((unsigned char)b << 16) \
- | ((unsigned char)c << 8) \
- | ((unsigned char)d))
-
-/*
- * tag_from_string
- *
- */
-
-static inline unsigned
-tag_from_string(char *cs)
-{
- unsigned char *s = (unsigned char *)cs;
- return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]);
-}
-
-
-/*
- * repo_add_susetags
- * Parse susetags file passed in fp, fill solvables into repo
- *
- * susetags is key,value based
- * for short values
- * =key: value
- * is used
- * for long (multi-line) values,
- * +key:
- * value
- * value
- * -key:
- * is used
- *
- * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata
- * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/packages
- * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/pattern
- *
- * Assumptions:
- * All keys have 3 characters and end in ':'
- */
-
-static void
-finish_solvable(struct parsedata *pd, Solvable *s, Offset freshens)
-{
- Pool *pool = pd->repo->pool;
- Id handle = s - pool->solvables;
-
- if (pd->nfilelist)
- {
- int l;
- Id did;
- for (l = 0; l < pd->nfilelist; l += strlen(pd->filelist + l) + 1)
- {
- char *p = strrchr(pd->filelist + l, '/');
- if (!p)
- continue;
- *p++ = 0;
- did = repodata_str2dir(pd->data, pd->filelist + l, 1);
- p[-1] = '/';
- if (!did)
- did = repodata_str2dir(pd->data, "/", 1);
- repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, did, p);
- }
- pd->nfilelist = 0;
- }
- /* A self provide, except for source packages. This is harmless
- to do twice (in case we see the same package twice). */
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(pd->repo, s->provides,
- pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- /* XXX This uses repo_addid_dep internally, so should also be
- harmless to do twice. */
- s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, freshens);
- s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
- if (pd->ndirs)
- commit_diskusage(pd, handle);
-}
-
-static Hashtable
-joinhash_init(Repo *repo, Hashval *hmp)
-{
- Hashval hm = mkmask(repo->nsolvables);
- Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
- Hashval h, hh;
- Solvable *s;
- int i;
-
- FOR_REPO_SOLVABLES(repo, i, s)
- {
- hh = HASHCHAIN_START;
- h = s->name & hm;
- while (ht[h])
- h = HASHCHAIN_NEXT(h, hh, hm);
- ht[h] = i;
- }
- *hmp = hm;
- return ht;
-}
-
-static Solvable *
-joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, Id name, Id evr, Id arch, Id start)
-{
- Hashval h, hh;
-
- if (!name || !arch || !evr)
- return 0;
- hh = HASHCHAIN_START;
- h = name & hm;
- while (ht[h])
- {
- Solvable *s = repo->pool->solvables + ht[h];
- if (ht[h] >= start && s->name == name && s->evr == evr && s->arch == arch)
- return s;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- return 0;
-}
-
-static Id
-lookup_shared_id(Repodata *data, Id p, Id keyname, Id voidid, int uninternalized)
-{
- Id r;
- r = repodata_lookup_type(data, p, keyname);
- if (r)
- {
- if (r == REPOKEY_TYPE_VOID)
- return voidid;
- r = repodata_lookup_id(data, p, keyname);
- if (r)
- return r;
- }
- if (uninternalized)
- return repodata_lookup_id_uninternalized(data, p, keyname, voidid);
- return 0;
-}
-
-static inline Id
-toevr(Pool *pool, struct parsedata *pd, const char *version, const char *release)
-{
- return makeevr(pool, !release || (release[0] == '-' && !release[1]) ?
- version : join2(&pd->jd, version, "-", release));
-}
-
-
-/*
- * parse susetags
- *
- * fp: file to read from
- * defvendor: default vendor (0 if none)
- * language: current language (0 if none)
- * flags: flags
- */
-
-int
-repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int flags)
-{
- Pool *pool = repo->pool;
- char *line, *linep;
- int aline;
- Solvable *s;
- Offset freshens;
- int intag = 0;
- int cummulate = 0;
- int indesc = 0;
- int indelta = 0;
- int last_found_pack = 0;
- Id first_new_pkg = 0;
- char *sp[5];
- struct parsedata pd;
- Repodata *data = 0;
- Id handle = 0;
- Hashtable joinhash = 0;
- Hashval joinhashm = 0;
- int createdpkgs = 0;
-
- if ((flags & (SUSETAGS_EXTEND|REPO_EXTEND_SOLVABLES)) != 0 && repo->nrepodata)
- {
- joinhash = joinhash_init(repo, &joinhashm);
- indesc = 1;
- }
-
- data = repo_add_repodata(repo, flags);
-
- memset(&pd, 0, sizeof(pd));
- line = solv_malloc(1024);
- aline = 1024;
-
- pd.pool = pool;
- pd.repo = repo;
- pd.data = data;
- pd.flags = flags;
- pd.language = language && *language ? solv_strdup(language) : 0;
-
- linep = line;
- s = 0;
- freshens = 0;
-
- /* if this is a join setup the recorded share data */
- if (joinhash)
- {
- Repodata *sdata;
- int i;
-
- FOR_REPODATAS(repo, i, sdata)
- {
- int p;
- if (!repodata_has_keyname(sdata, SUSETAGS_SHARE_NAME))
- continue;
- for (p = sdata->start; p < sdata->end; p++)
- {
- Id name, evr, arch;
- name = lookup_shared_id(sdata, p, SUSETAGS_SHARE_NAME, pool->solvables[p].name, sdata == data);
- if (!name)
- continue;
- evr = lookup_shared_id(sdata, p, SUSETAGS_SHARE_EVR, pool->solvables[p].evr, sdata == data);
- if (!evr)
- continue;
- arch = lookup_shared_id(sdata, p, SUSETAGS_SHARE_ARCH, pool->solvables[p].arch, sdata == data);
- if (!arch)
- continue;
- if (p - repo->start >= pd.nshare)
- {
- pd.share_with = solv_realloc2(pd.share_with, p - repo->start + 256, sizeof(*pd.share_with));
- memset(pd.share_with + pd.nshare, 0, (p - repo->start + 256 - pd.nshare) * sizeof(*pd.share_with));
- pd.nshare = p - repo->start + 256;
- }
- pd.share_with[p - repo->start].name = name;
- pd.share_with[p - repo->start].evr = evr;
- pd.share_with[p - repo->start].arch = arch;
- }
- }
- }
-
- /*
- * read complete file
- *
- * collect values in 'struct parsedata pd'
- * then build .solv (and .attr) file
- */
-
- for (;;)
- {
- unsigned tag;
- char *olinep; /* old line pointer */
- char line_lang[6];
- int keylen = 3;
-
- if (pd.ret)
- break;
- if (linep - line + 16 > aline) /* (re-)alloc buffer */
- {
- aline = linep - line;
- line = solv_realloc(line, aline + 512);
- linep = line + aline;
- aline += 512;
- }
- if (!fgets(linep, aline - (linep - line), fp)) /* read line */
- break;
- olinep = linep;
- linep += strlen(linep);
- if (linep == line || linep[-1] != '\n')
- continue;
- pd.lineno++;
- *--linep = 0;
- if (linep == olinep)
- continue;
-
- if (intag)
- {
- /* check for multi-line value tags (+Key:/-Key:) */
-
- int is_end = (linep[-intag - keylen + 1] == '-')
- && (linep[-1] == ':')
- && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - keylen] == '\n');
- if (is_end
- && strncmp(linep - 1 - intag, line + 1, intag))
- {
- pool_debug(pool, SOLV_ERROR, "susetags: Nonmatching multi-line tags: %d: '%s' '%s' %d\n", pd.lineno, linep - 1 - intag, line + 1, intag);
- }
- if (cummulate && !is_end)
- {
- *linep++ = '\n';
- continue;
- }
- if (cummulate && is_end)
- {
- linep[-intag - keylen + 1] = 0;
- if (linep[-intag - keylen] == '\n')
- linep[-intag - keylen] = 0;
- linep = line;
- intag = 0;
- }
- if (!cummulate && is_end)
- {
- intag = 0;
- linep = line;
- continue;
- }
- if (!cummulate && !is_end)
- linep = line + intag + keylen;
- }
- else
- linep = line;
-
- if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
- {
- char *tagend = strchr(line, ':');
- if (!tagend)
- {
- pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
- break;
- }
- intag = tagend - (line + 1);
- cummulate = 0;
- switch (tag_from_string(line)) /* check if accumulation is needed */
- {
- case CTAG('+', 'D', 'e', 's'):
- case CTAG('+', 'E', 'u', 'l'):
- case CTAG('+', 'I', 'n', 's'):
- case CTAG('+', 'D', 'e', 'l'):
- case CTAG('+', 'A', 'u', 't'):
- if (line[4] == ':' || line[4] == '.')
- cummulate = 1;
- break;
- default:
- break;
- }
- line[0] = '='; /* handle lines between +Key:/-Key: as =Key: */
- line[intag + keylen - 1] = ' ';
- linep = line + intag + keylen;
- continue;
- }
- if (*line == '#' || !*line)
- continue;
- if (! (line[0] && line[1] && line[2] && line[3] && (line[4] == ':' || line[4] == '.')))
- continue;
- line_lang[0] = 0;
- if (line[4] == '.')
- {
- char *endlang;
- endlang = strchr(line + 5, ':');
- if (endlang)
- {
- int langsize = endlang - (line + 5);
- keylen = endlang - line - 1;
- if (langsize > 5)
- langsize = 5;
- strncpy(line_lang, line + 5, langsize);
- line_lang[langsize] = 0;
- }
- }
- tag = tag_from_string(line);
-
- if (indelta)
- {
- /* Example:
- =Dlt: subversion 1.6.16 1.3.1 i586
- =Dsq: subversion 1.6.15 4.2 i586 d57b3fc86e7a2f73796e8e35b96fa86212c910
- =Cks: SHA1 14a8410cf741856a5d70d89dab62984dba6a1ca7
- =Loc: 1 subversion-1.6.15_1.6.16-4.2_1.3.1.i586.delta.rpm
- =Siz: 81558
- */
- switch (tag)
- {
- case CTAG('=', 'D', 's', 'q'):
- {
- Id evr;
- if (split(line + 5, sp, 5) != 5)
- continue;
- repodata_set_id(data, handle, DELTA_SEQ_NAME, pool_str2id(pool, sp[0], 1));
- evr = toevr(pool, &pd, sp[1], sp[2]);
- repodata_set_id(data, handle, DELTA_SEQ_EVR, evr);
- /* repodata_set_id(data, handle, DELTA_SEQ_ARCH, pool_str2id(pool, sp[3], 1)); */
- repodata_set_str(data, handle, DELTA_SEQ_NUM, sp[4]);
- repodata_set_id(data, handle, DELTA_BASE_EVR, evr);
- continue;
- }
- case CTAG('=', 'C', 'k', 's'):
- set_checksum(&pd, data, handle, DELTA_CHECKSUM, line + 6);
- continue;
- case CTAG('=', 'L', 'o', 'c'):
- {
- int i = split(line + 6, sp, 3);
- if (i != 2 && i != 3)
- {
- pd.ret = pool_error(pool, -1, "susetags: line %d: bad location line '%s'\n", pd.lineno, line);
- continue;
- }
- repodata_set_deltalocation(data, handle, atoi(sp[0]), i == 3 ? sp[2] : 0, sp[1]);
- continue;
- }
- case CTAG('=', 'S', 'i', 'z'):
- if (split(line + 6, sp, 3) == 2)
- repodata_set_num(data, handle, DELTA_DOWNLOADSIZE, strtoull(sp[0], 0, 10));
- continue;
- case CTAG('=', 'P', 'k', 'g'):
- case CTAG('=', 'P', 'a', 't'):
- case CTAG('=', 'D', 'l', 't'):
- handle = 0;
- indelta = 0;
- break;
- default:
- pool_debug(pool, SOLV_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
- continue;
- }
- }
-
- /*
- * start of (next) package or pattern or delta
- *
- * =Pkg: <name> <version> <release> <architecture>
- * (=Pat: ...)
- */
- if (tag == CTAG('=', 'D', 'l', 't'))
- {
- if (s)
- finish_solvable(&pd, s, freshens);
- s = 0;
- pd.kind = 0;
- if (split(line + 5, sp, 5) != 4)
- {
- pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
- break;
- }
- handle = repodata_new_handle(data);
- repodata_set_id(data, handle, DELTA_PACKAGE_NAME, pool_str2id(pool, sp[0], 1));
- repodata_set_id(data, handle, DELTA_PACKAGE_EVR, toevr(pool, &pd, sp[1], sp[2]));
- repodata_set_id(data, handle, DELTA_PACKAGE_ARCH, pool_str2id(pool, sp[3], 1));
- repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DELTAINFO, handle);
- indelta = 1;
- continue;
- }
- if (tag == CTAG('=', 'P', 'k', 'g')
- || tag == CTAG('=', 'P', 'a', 't'))
- {
- /* If we have an old solvable, complete it by filling in some
- default stuff. */
- if (s)
- finish_solvable(&pd, s, freshens);
-
- /*
- * define kind
- */
-
- pd.kind = 0;
- if (line[3] == 't')
- pd.kind = "pattern";
-
- /*
- * parse nevra
- */
-
- if (split(line + 5, sp, 5) != 4)
- {
- pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
- break;
- }
- s = 0;
- freshens = 0;
-
- if (joinhash)
- {
- /* data join operation. find solvable matching name/arch/evr and
- * add data to it */
- Id name, evr, arch;
- /* we don't use the create flag here as a simple pre-check for existance */
- if (pd.kind)
- name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 0);
- else
- name = pool_str2id(pool, sp[0], 0);
- evr = toevr(pool, &pd, sp[1], sp[2]);
- arch = pool_str2id(pool, sp[3], 0);
- if (name && arch)
- {
- Id start = (flags & REPO_EXTEND_SOLVABLES) ? 0 : first_new_pkg;
- if (repo->start + last_found_pack + 1 >= start && repo->start + last_found_pack + 1 < repo->end)
- {
- s = pool->solvables + repo->start + last_found_pack + 1;
- if (s->repo != repo || s->name != name || s->evr != evr || s->arch != arch)
- s = 0;
- }
- if (!s)
- s = joinhash_lookup(repo, joinhash, joinhashm, name, evr, arch, start);
- }
- /* do not create new packages in EXTEND_SOLVABLES mode */
- if (!s && (flags & REPO_EXTEND_SOLVABLES) != 0)
- continue;
- /* fallthrough to package creation */
- }
- if (!s)
- {
- /* normal operation. create a new solvable. */
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- if (pd.kind)
- s->name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 1);
- else
- s->name = pool_str2id(pool, sp[0], 1);
- s->evr = toevr(pool, &pd, sp[1], sp[2]);
- s->arch = pool_str2id(pool, sp[3], 1);
- s->vendor = defvendor;
- if (!first_new_pkg)
- first_new_pkg = s - pool->solvables;
- createdpkgs = 1;
- }
- last_found_pack = (s - pool->solvables) - repo->start;
- if (data)
- handle = s - pool->solvables;
- }
-
- /* If we have no current solvable to add to, ignore all further lines
- for it. Probably invalid input data in the second set of
- solvables. */
- if (indesc >= 2 && !s)
- {
-#if 0
- pool_debug(pool, SOLV_ERROR, "susetags: huh %d: %s?\n", pd.lineno, line);
-#endif
- continue;
- }
- switch (tag)
- {
- case CTAG('=', 'P', 'r', 'v'): /* provides */
- if (line[6] == '/')
- {
- /* probably a filelist entry. stash it away for now */
- int l = strlen(line + 6) + 1;
- if (pd.nfilelist + l > pd.afilelist)
- {
- pd.afilelist = pd.nfilelist + l + 512;
- pd.filelist = solv_realloc(pd.filelist, pd.afilelist);
- }
- memcpy(pd.filelist + pd.nfilelist, line + 6, l);
- pd.nfilelist += l;
- break;
- }
- if (pd.nfilelist)
- {
- int l;
- for (l = 0; l < pd.nfilelist; l += strlen(pd.filelist + l) + 1)
- s->provides = repo_addid_dep(pd.repo, s->provides, pool_str2id(pool, pd.filelist + l, 1), 0);
- pd.nfilelist = 0;
- }
- s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
- continue;
- case CTAG('=', 'R', 'e', 'q'): /* requires */
- s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
- continue;
- case CTAG('=', 'P', 'r', 'q'): /* pre-requires / packages required */
- if (pd.kind)
- {
- s->requires = adddep(pool, &pd, s->requires, line, 0, 0); /* patterns: a required package */
- }
- else
- s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
- continue;
- case CTAG('=', 'O', 'b', 's'): /* obsoletes */
- s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
- continue;
- case CTAG('=', 'C', 'o', 'n'): /* conflicts */
- s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
- continue;
- case CTAG('=', 'R', 'e', 'c'): /* recommends */
- s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
- continue;
- case CTAG('=', 'S', 'u', 'p'): /* supplements */
- s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
- continue;
- case CTAG('=', 'E', 'n', 'h'): /* enhances */
- s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
- continue;
- case CTAG('=', 'S', 'u', 'g'): /* suggests */
- s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
- continue;
- case CTAG('=', 'F', 'r', 'e'): /* freshens */
- freshens = adddep(pool, &pd, freshens, line, 0, pd.kind);
- continue;
- case CTAG('=', 'P', 'r', 'c'): /* packages recommended */
- s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
- continue;
- case CTAG('=', 'P', 's', 'g'): /* packages suggested */
- s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
- continue;
- case CTAG('=', 'P', 'c', 'n'): /* pattern: package conflicts */
- s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
- continue;
- case CTAG('=', 'P', 'o', 'b'): /* pattern: package obsoletes */
- s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
- continue;
- case CTAG('=', 'P', 'f', 'r'): /* pattern: package freshens */
- freshens = adddep(pool, &pd, freshens, line, 0, 0);
- continue;
- case CTAG('=', 'P', 's', 'p'): /* pattern: package supplements */
- s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
- continue;
- case CTAG('=', 'P', 'e', 'n'): /* pattern: package enhances */
- s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
- continue;
- case CTAG('=', 'V', 'e', 'r'): /* - version - */
- last_found_pack = 0;
- handle = 0;
- indesc++;
- if (createdpkgs)
- {
- solv_free(joinhash);
- joinhash = joinhash_init(repo, &joinhashm);
- createdpkgs = 0;
- }
- continue;
- case CTAG('=', 'V', 'n', 'd'): /* vendor */
- s->vendor = pool_str2id(pool, line + 6, 1);
- continue;
-
- /* From here it's the attribute tags. */
- case CTAG('=', 'G', 'r', 'p'):
- repodata_set_poolstr(data, handle, SOLVABLE_GROUP, line + 6);
- continue;
- case CTAG('=', 'L', 'i', 'c'):
- repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, line + 6);
- continue;
- case CTAG('=', 'L', 'o', 'c'):
- {
- int i = split(line + 6, sp, 3);
- if (i != 2 && i != 3)
- {
- pd.ret = pool_error(pool, -1, "susetags: line %d: bad location line '%s'\n", pd.lineno, line);
- continue;
- }
- repodata_set_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : pool_id2str(pool, s->arch), sp[1]);
- }
- continue;
- case CTAG('=', 'S', 'r', 'c'):
- add_source(&pd, line + 6, s, handle);
- continue;
- case CTAG('=', 'S', 'i', 'z'):
- if (split(line + 6, sp, 3) == 2)
- {
- repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(sp[0], 0, 10));
- repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, strtoull(sp[1], 0, 10));
- }
- continue;
- case CTAG('=', 'T', 'i', 'm'):
- {
- unsigned int t = atoi(line + 6);
- if (t)
- repodata_set_num(data, handle, SOLVABLE_BUILDTIME, t);
- }
- continue;
- case CTAG('=', 'K', 'w', 'd'):
- repodata_add_poolstr_array(data, handle, SOLVABLE_KEYWORDS, line + 6);
- continue;
- case CTAG('=', 'A', 'u', 't'):
- repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
- continue;
- case CTAG('=', 'S', 'u', 'm'):
- repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, line_lang), line + 3 + keylen);
- continue;
- case CTAG('=', 'D', 'e', 's'):
- repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, line_lang), line + 3 + keylen);
- continue;
- case CTAG('=', 'E', 'u', 'l'):
- repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, line_lang), line + 6);
- continue;
- case CTAG('=', 'I', 'n', 's'):
- repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, line_lang), line + 6);
- continue;
- case CTAG('=', 'D', 'e', 'l'):
- repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, line_lang), line + 6);
- continue;
- case CTAG('=', 'V', 'i', 's'):
- {
- /* Accept numbers and textual bools. */
- int k;
- k = atoi(line + 6);
- if (k || !strcasecmp(line + 6, "true"))
- repodata_set_void(data, handle, SOLVABLE_ISVISIBLE);
- }
- continue;
- case CTAG('=', 'S', 'h', 'r'):
- {
- Id name, evr, arch;
- if (split(line + 6, sp, 5) != 4)
- {
- pd.ret = pool_error(pool, -1, "susetags: line %d: bad =Shr line '%s'\n", pd.lineno, line);
- continue;
- }
- name = pool_str2id(pool, sp[0], 1);
- evr = toevr(pool, &pd, sp[1], sp[2]);
- arch = pool_str2id(pool, sp[3], 1);
- if (last_found_pack >= pd.nshare)
- {
- pd.share_with = solv_realloc2(pd.share_with, last_found_pack + 256, sizeof(*pd.share_with));
- memset(pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof(*pd.share_with));
- pd.nshare = last_found_pack + 256;
- }
- pd.share_with[last_found_pack].name = name;
- pd.share_with[last_found_pack].evr = evr;
- pd.share_with[last_found_pack].arch = arch;
- if ((flags & SUSETAGS_RECORD_SHARES) != 0)
- {
- if (s->name == name)
- repodata_set_void(data, handle, SUSETAGS_SHARE_NAME);
- else
- repodata_set_id(data, handle, SUSETAGS_SHARE_NAME, name);
- if (s->evr == evr)
- repodata_set_void(data, handle, SUSETAGS_SHARE_EVR);
- else
- repodata_set_id(data, handle, SUSETAGS_SHARE_EVR, evr);
- if (s->arch == arch)
- repodata_set_void(data, handle, SUSETAGS_SHARE_ARCH);
- else
- repodata_set_id(data, handle, SUSETAGS_SHARE_ARCH, arch);
- }
- continue;
- }
- case CTAG('=', 'D', 'i', 'r'):
- add_dirline(&pd, line + 6);
- continue;
- case CTAG('=', 'C', 'a', 't'):
- repodata_set_poolstr(data, handle, langtag(&pd, SOLVABLE_CATEGORY, line_lang), line + 3 + keylen);
- break;
- case CTAG('=', 'O', 'r', 'd'):
- /* Order is a string not a number, so we can retroactively insert
- new patterns in the middle, i.e. 1 < 15 < 2. */
- repodata_set_str(data, handle, SOLVABLE_ORDER, line + 6);
- break;
- case CTAG('=', 'I', 'c', 'o'):
- repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
- break;
- case CTAG('=', 'E', 'x', 't'):
- repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2(&pd.jd, "pattern", ":", line + 6));
- break;
- case CTAG('=', 'I', 'n', 'c'):
- repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2(&pd.jd, "pattern", ":", line + 6));
- break;
- case CTAG('=', 'C', 'k', 's'):
- set_checksum(&pd, data, handle, SOLVABLE_CHECKSUM, line + 6);
- break;
- case CTAG('=', 'L', 'a', 'n'):
- pd.language = solv_free(pd.language);
- memset(pd.langcache, 0, sizeof(pd.langcache));
- if (line[6])
- pd.language = solv_strdup(line + 6);
- break;
-
- case CTAG('=', 'F', 'l', 's'):
- {
- char *p = strrchr(line + 6, '/');
- Id did;
- /* strip trailing slash */
- if (p && p != line + 6 && !p[1])
- {
- *p = 0;
- p = strrchr(line + 6, '/');
- }
- if (p)
- {
- *p++ = 0;
- did = repodata_str2dir(data, line + 6, 1);
- }
- else
- {
- p = line + 6;
- did = 0;
- }
- if (!did)
- did = repodata_str2dir(data, "/", 1);
- repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, p);
- break;
- }
- case CTAG('=', 'H', 'd', 'r'):
- /* rpm header range */
- if (split(line + 6, sp, 3) == 2)
- {
- /* we ignore the start value */
- unsigned int end = (unsigned int)atoi(sp[1]);
- if (end)
- repodata_set_num(data, handle, SOLVABLE_HEADEREND, end);
- }
- break;
-
- case CTAG('=', 'P', 'a', 't'):
- case CTAG('=', 'P', 'k', 'g'):
- break;
-
- default:
-#if 0
- pool_debug(pool, SOLV_WARN, "susetags: unknown line: %d: %s\n", pd.lineno, line);
-#endif
- break;
- }
-
- }
-
- if (s)
- finish_solvable(&pd, s, freshens);
- solv_free(pd.filelist);
-
- /* Shared attributes
- * (e.g. multiple binaries built from same source)
- */
- if (pd.nshare)
- {
- int i, last_found;
- Map keyidmap;
-
- map_init(&keyidmap, data->nkeys);
- for (i = 1; i < data->nkeys; i++)
- {
- Id keyname = data->keys[i].name;
- if (keyname == SOLVABLE_INSTALLSIZE || keyname == SOLVABLE_DISKUSAGE || keyname == SOLVABLE_FILELIST)
- continue;
- if (keyname == SOLVABLE_MEDIADIR || keyname == SOLVABLE_MEDIAFILE || keyname == SOLVABLE_MEDIANR)
- continue;
- if (keyname == SOLVABLE_DOWNLOADSIZE || keyname == SOLVABLE_CHECKSUM)
- continue;
- if (keyname == SOLVABLE_SOURCENAME || keyname == SOLVABLE_SOURCEARCH || keyname == SOLVABLE_SOURCEEVR)
- continue;
- if (keyname == SOLVABLE_PKGID || keyname == SOLVABLE_HDRID || keyname == SOLVABLE_LEADSIGID)
- continue;
- if (keyname == SUSETAGS_SHARE_NAME || keyname == SUSETAGS_SHARE_EVR || keyname == SUSETAGS_SHARE_ARCH)
- continue;
- MAPSET(&keyidmap, i);
- }
- last_found = 0;
- for (i = 0; i < pd.nshare; i++)
- {
- unsigned int n, nn;
- Solvable *found = 0;
- if (!pd.share_with[i].name)
- continue;
- for (n = repo->start, nn = repo->start + last_found; n < repo->end; n++, nn++)
- {
- if (nn >= repo->end)
- nn = repo->start;
- found = pool->solvables + nn;
- if (found->repo == repo
- && found->name == pd.share_with[i].name
- && found->evr == pd.share_with[i].evr
- && found->arch == pd.share_with[i].arch)
- {
- last_found = nn - repo->start;
- break;
- }
- }
- if (n != repo->end)
- repodata_merge_some_attrs(data, repo->start + i, repo->start + last_found, &keyidmap, 0);
- }
- free(pd.share_with);
- map_free(&keyidmap);
- }
-
- solv_free(joinhash);
- repodata_free_dircache(data);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
-
- solv_free(pd.language);
- solv_free(line);
- join_freemem(&pd.jd);
- return pd.ret;
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/* read susetags file <fp> into <repo>
- * if <attrname> given, write attributes as '<attrname>.attr'
- */
-
-#define SUSETAGS_EXTEND (1 << 9)
-#define SUSETAGS_RECORD_SHARES (1 << 10)
-
-extern int repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define _GNU_SOURCE
-#define _XOPEN_SOURCE /* glibc2 needs this */
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <expat.h>
-#include <time.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_updateinfoxml.h"
-#define DISABLE_SPLIT
-#include "tools_util.h"
-
-/*
- * <updates>
- * <update from="rel-eng@fedoraproject.org" status="stable" type="security" version="1.4">
- * <id>FEDORA-2007-4594</id>
- * <title>imlib-1.9.15-6.fc8</title>
- * <severity>Important</severity>
- * <release>Fedora 8</release>
- * <rights>Copyright 2007 Company Inc</rights>
- * <issued date="2007-12-28 16:42:30"/>
- * <updated date="2008-03-14 12:00:00"/>
- * <references>
- * <reference href="https://bugzilla.redhat.com/show_bug.cgi?id=426091" id="426091" title="CVE-2007-3568 imlib: infinite loop DoS using crafted BMP image" type="bugzilla"/>
- * </references>
- * <description>This update includes a fix for a denial-of-service issue (CVE-2007-3568) whereby an attacker who could get an imlib-using user to view a specially-crafted BMP image could cause the user's CPU to go into an infinite loop.</description>
- * <pkglist>
- * <collection short="F8">
- * <name>Fedora 8</name>
- * <package arch="ppc64" name="imlib-debuginfo" release="6.fc8" src="http://download.fedoraproject.org/pub/fedora/linux/updates/8/ppc64/imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm" version="1.9.15">
- * <filename>imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm</filename>
- * <reboot_suggested>True</reboot_suggested>
- * </package>
- * </collection>
- * </pkglist>
- * </update>
- * </updates>
-*/
-
-enum state {
- STATE_START,
- STATE_UPDATES,
- STATE_UPDATE,
- STATE_ID,
- STATE_TITLE,
- STATE_RELEASE,
- STATE_ISSUED,
- STATE_UPDATED,
- STATE_MESSAGE,
- STATE_REFERENCES,
- STATE_REFERENCE,
- STATE_DESCRIPTION,
- STATE_PKGLIST,
- STATE_COLLECTION,
- STATE_NAME,
- STATE_PACKAGE,
- STATE_FILENAME,
- STATE_REBOOT,
- STATE_RESTART,
- STATE_RELOGIN,
- STATE_RIGHTS,
- STATE_SEVERITY,
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-
-/* !! must be sorted by first column !! */
-static struct stateswitch stateswitches[] = {
- { STATE_START, "updates", STATE_UPDATES, 0 },
- { STATE_START, "update", STATE_UPDATE, 0 },
- { STATE_UPDATES, "update", STATE_UPDATE, 0 },
- { STATE_UPDATE, "id", STATE_ID, 1 },
- { STATE_UPDATE, "title", STATE_TITLE, 1 },
- { STATE_UPDATE, "severity", STATE_SEVERITY, 1 },
- { STATE_UPDATE, "rights", STATE_RIGHTS, 1 },
- { STATE_UPDATE, "release", STATE_RELEASE, 1 },
- { STATE_UPDATE, "issued", STATE_ISSUED, 0 },
- { STATE_UPDATE, "updated", STATE_UPDATED, 0 },
- { STATE_UPDATE, "description", STATE_DESCRIPTION, 1 },
- { STATE_UPDATE, "message", STATE_MESSAGE , 1 },
- { STATE_UPDATE, "references", STATE_REFERENCES, 0 },
- { STATE_UPDATE, "pkglist", STATE_PKGLIST, 0 },
- { STATE_REFERENCES, "reference", STATE_REFERENCE, 0 },
- { STATE_PKGLIST, "collection", STATE_COLLECTION, 0 },
- { STATE_COLLECTION, "name", STATE_NAME, 1 },
- { STATE_COLLECTION, "package", STATE_PACKAGE, 0 },
- { STATE_PACKAGE, "filename", STATE_FILENAME, 1 },
- { STATE_PACKAGE, "reboot_suggested",STATE_REBOOT, 1 },
- { STATE_PACKAGE, "restart_suggested",STATE_RESTART, 1 },
- { STATE_PACKAGE, "relogin_suggested",STATE_RELOGIN, 1 },
- { NUMSTATES }
-};
-
-struct parsedata {
- int ret;
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
- Pool *pool;
- Repo *repo;
- Repodata *data;
- Id handle;
- Solvable *solvable;
- time_t buildtime;
- Id collhandle;
- struct joindata jd;
-
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
-};
-
-/*
- * Convert date strings ("1287746075" or "2010-10-22 13:14:35")
- * to timestamp.
- */
-static time_t
-datestr2timestamp(const char *date)
-{
- const char *p;
- struct tm tm;
-
- if (!date || !*date)
- return 0;
- for (p = date; *p >= '0' && *p <= '9'; p++)
- ;
- if (!*p)
- return atoi(date);
- memset(&tm, 0, sizeof(tm));
- if (!strptime(date, "%F%T", &tm))
- return 0;
- return timegm(&tm);
-}
-
-/*
- * create evr (as Id) from 'epoch', 'version' and 'release' attributes
- */
-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, "version"))
- v = atts[1];
- else if (!strcmp(*atts, "release"))
- r = atts[1];
- }
- if (e && (!*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 pool_str2id(pool, pd->content, 1);
-}
-
-
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *solvable = pd->solvable;
- struct stateswitch *sw;
- /*const char *str; */
-
-#if 0
- fprintf(stderr, "start: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
-
- pd->depth++;
- if (!pd->swtab[pd->state])
- return;
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
- if (!strcmp(sw->ename, name))
- break;
-
- if (sw->from != pd->state)
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#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_START:
- break;
- case STATE_UPDATES:
- break;
- /*
- * <update from="rel-eng@fedoraproject.org"
- * status="stable"
- * type="bugfix" (enhancement, security)
- * version="1.4">
- */
- case STATE_UPDATE:
- {
- const char *from = 0, *type = 0, *version = 0;
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, "from"))
- from = atts[1];
- else if (!strcmp(*atts, "type"))
- type = atts[1];
- else if (!strcmp(*atts, "version"))
- version = atts[1];
- }
- solvable = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
- pd->handle = pd->solvable - pool->solvables;
-
- solvable->vendor = pool_str2id(pool, from, 1);
- solvable->evr = pool_str2id(pool, version, 1);
- solvable->arch = ARCH_NOARCH;
- if (type)
- repodata_set_str(pd->data, pd->handle, SOLVABLE_PATCHCATEGORY, type);
- pd->buildtime = (time_t)0;
- }
- break;
- /* <id>FEDORA-2007-4594</id> */
- case STATE_ID:
- break;
- /* <title>imlib-1.9.15-6.fc8</title> */
- case STATE_TITLE:
- break;
- /* <release>Fedora 8</release> */
- case STATE_RELEASE:
- break;
- /* <issued date="2008-03-21 21:36:55"/>
- */
- case STATE_ISSUED:
- case STATE_UPDATED:
- {
- const char *date = 0;
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, "date"))
- date = atts[1];
- }
- if (date)
- {
- time_t t = datestr2timestamp(date);
- if (t && t > pd->buildtime)
- pd->buildtime = t;
- }
- }
- break;
- case STATE_REFERENCES:
- break;
- /* <reference href="https://bugzilla.redhat.com/show_bug.cgi?id=330471"
- * id="330471"
- * title="LDAP schema file missing for dhcpd"
- * type="bugzilla"/>
- */
- case STATE_REFERENCE:
- {
- const char *href = 0, *id = 0, *title = 0, *type = 0;
- Id refhandle;
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, "href"))
- href = atts[1];
- else if (!strcmp(*atts, "id"))
- id = atts[1];
- else if (!strcmp(*atts, "title"))
- title = atts[1];
- else if (!strcmp(*atts, "type"))
- type = atts[1];
- }
- refhandle = repodata_new_handle(pd->data);
- if (href)
- repodata_set_str(pd->data, refhandle, UPDATE_REFERENCE_HREF, href);
- if (id)
- repodata_set_str(pd->data, refhandle, UPDATE_REFERENCE_ID, id);
- if (title)
- repodata_set_str(pd->data, refhandle, UPDATE_REFERENCE_TITLE, title);
- if (type)
- repodata_set_poolstr(pd->data, refhandle, UPDATE_REFERENCE_TYPE, type);
- repodata_add_flexarray(pd->data, pd->handle, UPDATE_REFERENCE, refhandle);
- }
- break;
- /* <description>This update ...</description> */
- case STATE_DESCRIPTION:
- break;
- /* <message type="confirm">This update ...</message> */
- case STATE_MESSAGE:
- break;
- case STATE_PKGLIST:
- break;
- /* <collection short="F8" */
- case STATE_COLLECTION:
- break;
- /* <name>Fedora 8</name> */
- case STATE_NAME:
- break;
- /* <package arch="ppc64" name="imlib-debuginfo" release="6.fc8"
- * src="http://download.fedoraproject.org/pub/fedora/linux/updates/8/ppc64/imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm"
- * version="1.9.15">
- *
- *
- * -> patch.conflicts: {name} < {version}.{release}
- */
- case STATE_PACKAGE:
- {
- const char *arch = 0, *name = 0;
- Id evr = makeevr_atts(pool, pd, atts); /* parse "epoch", "version", "release" */
- Id n, a = 0;
- Id rel_id;
-
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, "arch"))
- arch = atts[1];
- else if (!strcmp(*atts, "name"))
- name = atts[1];
- }
- /* generated Id for name */
- n = pool_str2id(pool, name, 1);
- rel_id = n;
- if (arch)
- {
- /* generate Id for arch and combine with name */
- a = pool_str2id(pool, arch, 1);
- rel_id = pool_rel2id(pool, n, a, REL_ARCH, 1);
- }
- rel_id = pool_rel2id(pool, rel_id, evr, REL_LT, 1);
- solvable->conflicts = repo_addid_dep(pd->repo, solvable->conflicts, rel_id, 0);
-
- /* who needs the collection anyway? */
- pd->collhandle = repodata_new_handle(pd->data);
- repodata_set_id(pd->data, pd->collhandle, UPDATE_COLLECTION_NAME, n);
- repodata_set_id(pd->data, pd->collhandle, UPDATE_COLLECTION_EVR, evr);
- if (a)
- repodata_set_id(pd->data, pd->collhandle, UPDATE_COLLECTION_ARCH, a);
- break;
- }
- /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */
- /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */
- case STATE_FILENAME:
- break;
- /* <reboot_suggested>True</reboot_suggested> */
- case STATE_REBOOT:
- break;
- /* <restart_suggested>True</restart_suggested> */
- case STATE_RESTART:
- break;
- /* <relogin_suggested>True</relogin_suggested> */
- case STATE_RELOGIN:
- break;
- default:
- break;
- }
- return;
-}
-
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- Repo *repo = pd->repo;
-
-#if 0
- fprintf(stderr, "end: %s\n", name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
-#if 0
- fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-#endif
- return;
- }
-
- pd->depth--;
- pd->statedepth--;
- switch (pd->state)
- {
- case STATE_START:
- break;
- case STATE_UPDATES:
- break;
- case STATE_UPDATE:
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- if (pd->buildtime)
- {
- repodata_set_num(pd->data, pd->handle, SOLVABLE_BUILDTIME, pd->buildtime);
- pd->buildtime = (time_t)0;
- }
- break;
- case STATE_ID:
- s->name = pool_str2id(pool, join2(&pd->jd, "patch", ":", pd->content), 1);
- break;
- /* <title>imlib-1.9.15-6.fc8</title> */
- case STATE_TITLE:
- while (pd->lcontent > 0 && pd->content[pd->lcontent - 1] == '\n')
- pd->content[--pd->lcontent] = 0;
- repodata_set_str(pd->data, pd->handle, SOLVABLE_SUMMARY, pd->content);
- break;
- case STATE_SEVERITY:
- repodata_set_poolstr(pd->data, pd->handle, UPDATE_SEVERITY, pd->content);
- break;
- case STATE_RIGHTS:
- repodata_set_poolstr(pd->data, pd->handle, UPDATE_RIGHTS, pd->content);
- break;
- /*
- * <release>Fedora 8</release>
- */
- case STATE_RELEASE:
- break;
- case STATE_ISSUED:
- break;
- case STATE_REFERENCES:
- break;
- case STATE_REFERENCE:
- break;
- /*
- * <description>This update ...</description>
- */
- case STATE_DESCRIPTION:
- repodata_set_str(pd->data, pd->handle, SOLVABLE_DESCRIPTION, pd->content);
- break;
- /*
- * <message>Warning! ...</message>
- */
- case STATE_MESSAGE:
- repodata_set_str(pd->data, pd->handle, UPDATE_MESSAGE, pd->content);
- break;
- case STATE_PKGLIST:
- break;
- case STATE_COLLECTION:
- break;
- case STATE_NAME:
- break;
- case STATE_PACKAGE:
- repodata_add_flexarray(pd->data, pd->handle, UPDATE_COLLECTION, pd->collhandle);
- pd->collhandle = 0;
- break;
- /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */
- /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */
- case STATE_FILENAME:
- repodata_set_str(pd->data, pd->collhandle, UPDATE_COLLECTION_FILENAME, pd->content);
- break;
- /* <reboot_suggested>True</reboot_suggested> */
- case STATE_REBOOT:
- if (pd->content[0] == 'T' || pd->content[0] == 't'|| pd->content[0] == '1')
- {
- /* FIXME: this is per-package, the global flag should be computed at runtime */
- repodata_set_void(pd->data, pd->handle, UPDATE_REBOOT);
- repodata_set_void(pd->data, pd->collhandle, UPDATE_REBOOT);
- }
- break;
- /* <restart_suggested>True</restart_suggested> */
- case STATE_RESTART:
- if (pd->content[0] == 'T' || pd->content[0] == 't'|| pd->content[0] == '1')
- {
- /* FIXME: this is per-package, the global flag should be computed at runtime */
- repodata_set_void(pd->data, pd->handle, UPDATE_RESTART);
- repodata_set_void(pd->data, pd->collhandle, UPDATE_RESTART);
- }
- break;
- /* <relogin_suggested>True</relogin_suggested> */
- case STATE_RELOGIN:
- if (pd->content[0] == 'T' || pd->content[0] == 't'|| pd->content[0] == '1')
- {
- /* FIXME: this is per-package, the global flag should be computed at runtime */
- repodata_set_void(pd->data, pd->handle, UPDATE_RELOGIN);
- repodata_set_void(pd->data, pd->collhandle, UPDATE_RELOGIN);
- }
- break;
- default:
- break;
- }
-
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
-}
-
-
-static void XMLCALL
-characterData(void *userData, const XML_Char *s, int len)
-{
- struct parsedata *pd = userData;
- int l;
- char *c;
-
- if (!pd->docontent)
- {
-#if 0
- fprintf(stderr, "Content: [%d]'%.*s'\n", pd->state, len, s);
-#endif
- 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
-
-int
-repo_add_updateinfoxml(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- struct parsedata pd;
- char buf[BUFF_SIZE];
- int i, l;
- struct stateswitch *sw;
- Repodata *data;
- XML_Parser parser;
-
- data = repo_add_repodata(repo, flags);
-
- 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.repo = repo;
- pd.data = data;
-
- pd.content = malloc(256);
- pd.acontent = 256;
- pd.lcontent = 0;
- 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)
- {
- pd.ret = pool_error(pool, -1, "repo_updateinfoxml: %s at line %u:%u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- break;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
- free(pd.content);
- join_freemem(&pd.jd);
-
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return pd.ret;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_updateinfoxml(Repo *repo, FILE *fp, int flags);
+++ /dev/null
-/*
- * repo_zyppdb.c
- *
- * Parses legacy /var/lib/zypp/db/products/... files.
- * They are old (pre Code11) product descriptions. See bnc#429177
- *
- *
- * Copyright (c) 2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <dirent.h>
-#include <expat.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#define DISABLE_SPLIT
-#include "tools_util.h"
-#include "repo_zyppdb.h"
-
-
-enum state {
- STATE_START,
- STATE_PRODUCT,
- STATE_NAME,
- STATE_VERSION,
- STATE_ARCH,
- STATE_SUMMARY,
- STATE_VENDOR,
- STATE_INSTALLTIME,
- NUMSTATES
-};
-
-struct stateswitch {
- enum state from;
- char *ename;
- enum state to;
- int docontent;
-};
-
-/* !! must be sorted by first column !! */
-static struct stateswitch stateswitches[] = {
- { STATE_START, "product", STATE_PRODUCT, 0 },
- { STATE_PRODUCT, "name", STATE_NAME, 1 },
- { STATE_PRODUCT, "version", STATE_VERSION, 0 },
- { STATE_PRODUCT, "arch", STATE_ARCH, 1 },
- { STATE_PRODUCT, "summary", STATE_SUMMARY, 1 },
- { STATE_PRODUCT, "install-time", STATE_INSTALLTIME, 1 },
- { STATE_PRODUCT, "vendor", STATE_VENDOR, 1 },
- { NUMSTATES }
-};
-
-struct parsedata {
- int depth;
- enum state state;
- int statedepth;
- char *content;
- int lcontent;
- int acontent;
- int docontent;
- Pool *pool;
- Repo *repo;
- Repodata *data;
-
- struct stateswitch *swtab[NUMSTATES];
- enum state sbtab[NUMSTATES];
- struct joindata jd;
-
- const char *tmplang;
-
- Solvable *solvable;
- Id handle;
-};
-
-
-/*
- * find_attr
- * find value for xml attribute
- * I: txt, name of attribute
- * I: atts, list of key/value attributes
- * O: pointer to value of matching key, or NULL
- *
- */
-
-static inline const char *
-find_attr(const char *txt, const char **atts)
-{
- for (; *atts; atts += 2)
- {
- if (!strcmp(*atts, txt))
- return atts[1];
- }
- return 0;
-}
-
-
-/*
- * XML callback: startElement
- */
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
-{
- struct parsedata *pd = userData;
- Pool *pool = pd->pool;
- Solvable *s = pd->solvable;
- struct stateswitch *sw;
-
-#if 0
- fprintf(stderr, "start: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth++;
- return;
- }
-
- pd->depth++;
- if (!pd->swtab[pd->state]) /* no statetable -> no substates */
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#endif
- return;
- }
- for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
- if (!strcmp(sw->ename, name))
- break;
-
- if (sw->from != pd->state)
- {
-#if 0
- fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
-#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_PRODUCT:
- {
- /* parse 'type' */
- const char *type = find_attr("type", atts);
- s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
- pd->handle = s - pool->solvables;
- if (type)
- repodata_set_str(pd->data, pd->handle, PRODUCT_TYPE, type);
- }
- break;
- case STATE_VERSION:
- {
- const char *ver = find_attr("ver", atts);
- const char *rel = find_attr("rel", atts);
- /* const char *epoch = find_attr("epoch", atts); ignored */
- s->evr = makeevr(pd->pool, join2(&pd->jd, ver, "-", rel));
- }
- break;
- /* <summary lang="xy">... */
- case STATE_SUMMARY:
- pd->tmplang = join_dup(&pd->jd, find_attr("lang", atts));
- break;
- default:
- break;
- }
-}
-
-
-static void XMLCALL
-endElement(void *userData, const char *name)
-{
- struct parsedata *pd = userData;
- Solvable *s = pd->solvable;
-
-#if 0
- fprintf(stderr, "end: [%d]%s\n", pd->state, name);
-#endif
- if (pd->depth != pd->statedepth)
- {
- pd->depth--;
-#if 0
- fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-#endif
- return;
- }
-
- pd->depth--;
- pd->statedepth--;
-
- switch (pd->state)
- {
- case STATE_PRODUCT:
- if (!s->arch)
- s->arch = ARCH_NOARCH;
- if (!s->evr)
- s->evr = ID_EMPTY;
- if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
- pd->solvable = 0;
- break;
- case STATE_NAME:
- s->name = pool_str2id(pd->pool, join2(&pd->jd, "product", ":", pd->content), 1);
- break;
- case STATE_ARCH:
- s->arch = pool_str2id(pd->pool, pd->content, 1);
- break;
- case STATE_SUMMARY:
- repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_SUMMARY, pd->tmplang, 1), pd->content);
- break;
- case STATE_VENDOR:
- s->vendor = pool_str2id(pd->pool, pd->content, 1);
- break;
- case STATE_INSTALLTIME:
- repodata_set_num(pd->data, pd->handle, SOLVABLE_INSTALLTIME, atol(pd->content));
- default:
- break;
- }
-
- pd->state = pd->sbtab[pd->state];
- pd->docontent = 0;
-
-#if 0
- fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
-#endif
-}
-
-
-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
-
-
-/*
- * add single product to repo
- *
- */
-
-static void
-add_zyppdb_product(struct parsedata *pd, FILE *fp)
-{
- char buf[BUFF_SIZE];
- int l;
-
- 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)
- {
- pool_debug(pd->pool, SOLV_ERROR, "repo_zyppdb: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
- if (pd->solvable)
- {
- repo_free_solvable(pd->repo, pd->solvable - pd->pool->solvables, 1);
- pd->solvable = 0;
- }
- return;
- }
- if (l == 0)
- break;
- }
- XML_ParserFree(parser);
-}
-
-
-/*
- * read all installed products
- *
- * parse each one as a product
- */
-
-int
-repo_add_zyppdb_products(Repo *repo, const char *dirpath, int flags)
-{
- int i;
- struct parsedata pd;
- struct stateswitch *sw;
- struct dirent *entry;
- char *fullpath;
- DIR *dir;
- FILE *fp;
- Repodata *data;
-
- data = repo_add_repodata(repo, flags);
- memset(&pd, 0, sizeof(pd));
- pd.repo = repo;
- pd.pool = repo->pool;
- pd.data = data;
-
- pd.content = malloc(256);
- pd.acontent = 256;
-
- 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;
- }
-
- if (flags & REPO_USE_ROOTDIR)
- dirpath = pool_prepend_rootdir(repo->pool, dirpath);
- dir = opendir(dirpath);
- if (dir)
- {
- while ((entry = readdir(dir)))
- {
- if (strlen(entry->d_name) < 3)
- continue; /* skip '.' and '..' */
- fullpath = join2(&pd.jd, dirpath, "/", entry->d_name);
- if ((fp = fopen(fullpath, "r")) == 0)
- {
- pool_error(repo->pool, 0, "%s: %s", fullpath, strerror(errno));
- continue;
- }
- add_zyppdb_product(&pd, fp);
- fclose(fp);
- }
- }
- closedir(dir);
-
- free(pd.content);
- join_freemem(&pd.jd);
- if (flags & REPO_USE_ROOTDIR)
- solv_free((char *)dirpath);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-/* EOF */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int repo_add_zyppdb_products(Repo *repo, const char *dirpath, int flags);
+++ /dev/null
-/*
- * Copyright (c) 2013, SUSE Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/* simple and slow rsa/dsa verification code. */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "util.h"
-#include "solv_pgpvrfy.h"
-
-typedef unsigned int mp_t;
-typedef unsigned long long mp2_t;
-#define MP_T_BYTES 4
-
-#define MP_T_BITS (MP_T_BYTES * 8)
-
-static inline void
-mpzero(int len, mp_t *target)
-{
- memset(target, 0, MP_T_BYTES * len);
-}
-
-static inline mp_t *
-mpnew(int len)
-{
- return solv_calloc(len, MP_T_BYTES);
-}
-
-static inline void
-mpcpy(int len, mp_t *target, mp_t *source)
-{
- memcpy(target, source, len * MP_T_BYTES);
-}
-
-#if 0
-static void mpdump(int l, mp_t *a, char *s)
-{
- int i;
- if (s)
- fprintf(stderr, "%s", s);
- for (i = l - 1; i >= 0; i--)
- fprintf(stderr, "%0*x", MP_T_BYTES * 2, a[i]);
- fprintf(stderr, "\n");
-}
-#endif
-
-/* target[len] = x, target = target % mod
- * assumes that target < (mod << MP_T_BITS)! */
-static void
-mpdomod(int len, mp_t *target, mp2_t x, mp_t *mod)
-{
- int i, j;
- for (i = len - 1; i >= 0; i--)
- {
- x = (x << MP_T_BITS) | target[i];
- target[i] = 0;
- if (mod[i])
- break;
- }
- if (i < 0)
- return;
- while (x >= 2 * (mp2_t)mod[i])
- {
- /* reduce */
- mp2_t z = x / ((mp2_t)mod[i] + 1);
- mp2_t n = 0;
- if ((z >> MP_T_BITS) != 0)
- z = (mp2_t)1 << MP_T_BITS; /* just in case... */
- for (j = 0; j < i; j++)
- {
- mp_t n2;
- n += mod[j] * z;
- n2 = (mp_t)n;
- n >>= MP_T_BITS;
- if (n2 > target[j])
- n++;
- target[j] -= n2;
- }
- n += mod[j] * z;
- x -= n;
- }
- target[i] = x;
- if (x >= mod[i])
- {
- mp_t n;
- if (x == mod[i])
- {
- for (j = i - 1; j >= 0; j--)
- if (target[j] < mod[j])
- return;
- else if (target[j] > mod[j])
- break;
- }
- /* target >= mod, subtract mod */
- n = 0;
- for (j = 0; j <= i; j++)
- {
- mp2_t n2 = mod[j] + n;
- n = n2 > target[j] ? 1 : 0;
- target[j] -= (mp_t)n2;
- }
- }
-}
-
-/* target += src * m */
-static void
-mpmult_add_int(int len, mp_t *target, mp_t *src, mp2_t m, mp_t *mod)
-{
- int i;
- mp2_t x = 0;
- for (i = 0; i < len; i++)
- {
- x += src[i] * m + target[i];
- target[i] = x;
- x >>= MP_T_BITS;
- }
- mpdomod(len, target, x, mod);
-}
-
-/* target = target << MP_T_BITS */
-static void
-mpshift(int len, mp_t *target, mp_t *mod)
-{
- mp_t x;
- if (len <= 0)
- return;
- x = target[len - 1];
- if (len > 1)
- memmove(target + 1, target, (len - 1) * MP_T_BYTES);
- target[0] = 0;
- mpdomod(len, target, x, mod);
-}
-
-/* target += m1 * m2 */
-static void
-mpmult_add(int len, mp_t *target, mp_t *m1, int m2len, mp_t *m2, mp_t *tmp, mp_t *mod)
-{
- int i, j;
- for (j = m2len - 1; j >= 0; j--)
- if (m2[j])
- break;
- if (j < 0)
- return;
- mpcpy(len, tmp, m1);
- for (i = 0; i < j; i++)
- {
- if (m2[i])
- mpmult_add_int(len, target, tmp, m2[i], mod);
- mpshift(len, tmp, mod);
- }
- if (m2[i])
- mpmult_add_int(len, target, tmp, m2[i], mod);
-}
-
-/* target = target * m */
-static void
-mpmult_inplace(int len, mp_t *target, mp_t *m, mp_t *tmp1, mp_t *tmp2, mp_t *mod)
-{
- mpzero(len, tmp1);
- mpmult_add(len, tmp1, target, len, m, tmp2, mod);
- mpcpy(len, target, tmp1);
-}
-
-/* target = target ^ 16 * b ^ e */
-static void
-mppow_int(int len, mp_t *target, mp_t *t, mp_t *mod, int e)
-{
- mp_t *t2 = t + len * 16;
- mpmult_inplace(len, target, target, t, t2, mod);
- mpmult_inplace(len, target, target, t, t2, mod);
- mpmult_inplace(len, target, target, t, t2, mod);
- mpmult_inplace(len, target, target, t, t2, mod);
- if (e)
- mpmult_inplace(len, target, t + len * e, t, t2, mod);
-}
-
-/* target = b ^ e (b has to be < mod) */
-static void
-mppow(int len, mp_t *target, mp_t *b, int elen, mp_t *e, mp_t *mod)
-{
- int i, j;
- mp_t *t;
- mpzero(len, target);
- target[0] = 1;
- for (i = elen - 1; i >= 0; i--)
- if (e[i])
- break;
- if (i < 0)
- return;
- t = mpnew(len * 17);
- mpcpy(len, t + len, b);
- for (j = 2; j < 16; j++)
- mpmult_add(len, t + len * j, b, len, t + len * j - len, t + len * 16, mod);
- for (; i >= 0; i--)
- {
-#if MP_T_BYTES == 4
- mppow_int(len, target, t, mod, (e[i] >> 28) & 0x0f);
- mppow_int(len, target, t, mod, (e[i] >> 24) & 0x0f);
- mppow_int(len, target, t, mod, (e[i] >> 20) & 0x0f);
- mppow_int(len, target, t, mod, (e[i] >> 16) & 0x0f);
- mppow_int(len, target, t, mod, (e[i] >> 12) & 0x0f);
- mppow_int(len, target, t, mod, (e[i] >> 8) & 0x0f);
- mppow_int(len, target, t, mod, (e[i] >> 4) & 0x0f);
- mppow_int(len, target, t, mod, e[i] & 0x0f);
-#elif MP_T_BYTES == 1
- mppow_int(len, target, t, mod, (e[i] >> 4) & 0x0f);
- mppow_int(len, target, t, mod, e[i] & 0x0f);
-#endif
- }
- free(t);
-}
-
-/* target = m1 * m2 (m1 has to be < mod) */
-static void
-mpmult(int len, mp_t *target, mp_t *m1, int m2len, mp_t *m2, mp_t *mod)
-{
- mp_t *tmp = mpnew(len);
- mpzero(len, target);
- mpmult_add(len, target, m1, m2len, m2, tmp, mod);
- free(tmp);
-}
-
-static int
-mpisless(int len, mp_t *a, mp_t *b)
-{
- int i;
- for (i = len - 1; i >= 0; i--)
- if (a[i] < b[i])
- return 1;
- else if (a[i] > b[i])
- return 0;
- return 0;
-}
-
-static int
-mpiszero(int len, mp_t *a)
-{
- int i;
- for (i = 0; i < len; i++)
- if (a[i])
- return 0;
- return 1;
-}
-
-static void
-mpdec(int len, mp_t *a)
-{
- int i;
- for (i = 0; i < len; i++)
- if (a[i]--)
- return;
- else
- a[i] = -(mp_t)1;
-}
-
-static int
-mpdsa(int pl, mp_t *p, int ql, mp_t *q, mp_t *g, mp_t *y, mp_t *r, mp_t *s, int hl, mp_t *h)
-{
- mp_t *w;
- mp_t *tmp;
- mp_t *u1, *u2;
- mp_t *gu1, *yu2;
-#if 0
- mpdump(pl, p, "p = ");
- mpdump(ql, q, "q = ");
- mpdump(pl, g, "g = ");
- mpdump(pl, y, "y = ");
- mpdump(ql, r, "r = ");
- mpdump(ql, s, "s = ");
- mpdump(hl, h, "h = ");
-#endif
- if (pl < ql || !mpisless(pl, g, p) || !mpisless(pl, y, p))
- return 0; /* hmm, bad pubkey? */
- if (!mpisless(ql, r, q) || mpiszero(ql, r))
- return 0;
- if (!mpisless(ql, s, q) || mpiszero(ql, s))
- return 0;
- tmp = mpnew(pl); /* note pl */
- mpcpy(ql, tmp, q); /* tmp = q */
- mpdec(ql, tmp); /* tmp-- */
- mpdec(ql, tmp); /* tmp-- */
- w = mpnew(ql);
- mppow(ql, w, s, ql, tmp, q); /* w = s ^ tmp (s ^ -1) */
- u1 = mpnew(pl); /* note pl */
- /* order is important here: h can be >= q */
- mpmult(ql, u1, w, hl, h, q); /* u1 = w * h */
- u2 = mpnew(ql); /* u2 = 0 */
- mpmult(ql, u2, w, ql, r, q); /* u2 = w * r */
- free(w);
- gu1 = mpnew(pl);
- yu2 = mpnew(pl);
- mppow(pl, gu1, g, ql, u1, p); /* gu1 = g ^ u1 */
- mppow(pl, yu2, y, ql, u2, p); /* yu2 = y ^ u2 */
- mpmult(pl, u1, gu1, pl, yu2, p); /* u1 = gu1 * yu2 */
- free(gu1);
- free(yu2);
- mpzero(ql, u2);
- u2[0] = 1; /* u2 = 1 */
- mpmult(ql, tmp, u2, pl, u1, q); /* tmp = u2 * u1 */
- free(u1);
- free(u2);
-#if 0
- mpdump(ql, tmp, "res = ");
-#endif
- if (memcmp(tmp, r, ql * MP_T_BYTES) != 0)
- {
- free(tmp);
- return 0;
- }
- free(tmp);
- return 1;
-}
-
-static int
-mprsa(int nl, mp_t *n, int el, mp_t *e, mp_t *m, mp_t *c)
-{
- mp_t *tmp;
-#if 0
- mpdump(nl, n, "n = ");
- mpdump(el, e, "e = ");
- mpdump(nl, m, "m = ");
- mpdump(nl, c, "c = ");
-#endif
- if (!mpisless(nl, m, n))
- return 0;
- if (!mpisless(nl, c, n))
- return 0;
- tmp = mpnew(nl);
- mppow(nl, tmp, m, el, e, n); /* tmp = m ^ e */
-#if 0
- mpdump(nl, tmp, "res = ");
-#endif
- if (memcmp(tmp, c, nl * MP_T_BYTES) != 0)
- {
- free(tmp);
- return 0;
- }
- free(tmp);
- return 1;
-}
-
-/* create mp with size tbits from data with size dbits */
-static mp_t *
-mpbuild(const unsigned char *d, int dbits, int tbits, int *mplp)
-{
- int l = (tbits + MP_T_BITS - 1) / MP_T_BITS;
- int dl, i;
-
- mp_t *out = mpnew(l ? l : 1);
- if (mplp)
- *mplp = l;
- dl = (dbits + 7) / 8;
- d += dl;
- if (dbits > tbits)
- dl = (tbits + 7) / 8;
- for (i = 0; dl > 0; dl--, i++)
- {
- int x = *--d;
- out[i / MP_T_BYTES] |= x << (8 * (i % MP_T_BYTES));
- }
- return out;
-}
-
-static const unsigned char *
-findmpi(const unsigned char **mpip, int *mpilp, int maxbits, int *outlen)
-{
- int mpil = *mpilp;
- const unsigned char *mpi = *mpip;
- int bits, l;
-
- *outlen = 0;
- if (mpil < 2)
- return 0;
- bits = mpi[0] << 8 | mpi[1];
- l = 2 + (bits + 7) / 8;
- if (bits > maxbits || mpil < l || (bits && !mpi[2]))
- {
- *mpilp = 0;
- return 0;
- }
- *outlen = bits;
- *mpilp = mpil - l;
- *mpip = mpi + l;
- return mpi + 2;
-}
-
-int
-solv_pgpvrfy(const unsigned char *pub, int publ, const unsigned char *sig, int sigl)
-{
- int hashl;
- unsigned char *oid = 0;
- const unsigned char *mpi;
- int mpil;
- int res = 0;
-
- if (!pub || !sig || publ < 1 || sigl < 2)
- return 0;
- if (pub[0] != sig[0])
- return 0; /* key algo mismatch */
- switch(sig[1])
- {
- case 1:
- hashl = 16; /* MD5 */
- oid = (unsigned char *)"\022\060\040\060\014\006\010\052\206\110\206\367\015\002\005\005\000\004\020";
- break;
- case 2:
- hashl = 20; /* SHA-1 */
- oid = (unsigned char *)"\017\060\041\060\011\006\005\053\016\003\002\032\005\000\004\024";
- break;
- case 8:
- hashl = 32; /* SHA-256 */
- oid = (unsigned char *)"\023\060\061\060\015\006\011\140\206\110\001\145\003\004\002\001\005\000\004\040";
- break;
- case 9:
- hashl = 48; /* SHA-384 */
- oid = (unsigned char *)"\023\060\101\060\015\006\011\140\206\110\001\145\003\004\002\002\005\000\004\060";
- break;
- case 10:
- hashl = 64; /* SHA-512 */
- oid = (unsigned char *)"\023\060\121\060\015\006\011\140\206\110\001\145\003\004\002\003\005\000\004\100";
- break;
- case 11:
- hashl = 28; /* SHA-224 */
- oid = (unsigned char *)"\023\060\061\060\015\006\011\140\206\110\001\145\003\004\002\004\005\000\004\034";
- break;
- default:
- return 0; /* unsupported hash algo */
- }
- if (sigl < 2 + hashl)
- return 0;
- switch (pub[0])
- {
- case 1: /* RSA */
- {
- const unsigned char *n, *e, *m;
- unsigned char *c;
- int nlen, elen, mlen, clen;
- mp_t *nx, *ex, *mx, *cx;
- int nxl, exl;
-
- mpi = pub + 1;
- mpil = publ - 1;
- n = findmpi(&mpi, &mpil, 8192, &nlen);
- e = findmpi(&mpi, &mpil, 1024, &elen);
- mpi = sig + 2 + hashl;
- mpil = sigl - (2 + hashl);
- m = findmpi(&mpi, &mpil, nlen, &mlen);
- if (!n || !e || !m || !nlen || !elen)
- return 0;
- /* build padding block */
- clen = (nlen - 1) / 8;
- if (hashl + *oid + 2 > clen)
- return 0;
- c = solv_malloc(clen);
- memset(c, 0xff, clen);
- c[0] = 1;
- memcpy(c + clen - hashl, sig + 2, hashl);
- memcpy(c + clen - hashl - *oid, oid + 1, *oid);
- c[clen - hashl - *oid - 1] = 0;
- clen = clen * 8 - 7; /* always <= nlen */
- nx = mpbuild(n, nlen, nlen, &nxl);
- ex = mpbuild(e, elen, elen, &exl);
- mx = mpbuild(m, mlen, nlen, 0);
- cx = mpbuild(c, clen, nlen, 0);
- free(c);
- res = mprsa(nxl, nx, exl, ex, mx, cx);
- free(nx);
- free(ex);
- free(mx);
- free(cx);
- break;
- }
- case 17: /* DSA */
- {
- const unsigned char *p, *q, *g, *y, *r, *s;
- int plen, qlen, glen, ylen, rlen, slen, hlen;
- mp_t *px, *qx, *gx, *yx, *rx, *sx, *hx;
- int pxl, qxl, hxl;
-
- mpi = pub + 1;
- mpil = publ - 1;
- p = findmpi(&mpi, &mpil, 8192, &plen);
- q = findmpi(&mpi, &mpil, 1024, &qlen);
- g = findmpi(&mpi, &mpil, plen, &glen);
- y = findmpi(&mpi, &mpil, plen, &ylen);
- mpi = sig + 2 + hashl;
- mpil = sigl - (2 + hashl);
- r = findmpi(&mpi, &mpil, qlen, &rlen);
- s = findmpi(&mpi, &mpil, qlen, &slen);
- if (!p || !q || !g || !y || !r || !s || !plen || !qlen)
- return 0;
- hlen = (qlen + 7) & ~7;
- if (hlen > hashl * 8)
- return 0;
- px = mpbuild(p, plen, plen, &pxl);
- qx = mpbuild(q, qlen, qlen, &qxl);
- gx = mpbuild(g, glen, plen, 0);
- yx = mpbuild(y, ylen, plen, 0);
- rx = mpbuild(r, rlen, qlen, 0);
- sx = mpbuild(s, slen, qlen, 0);
- hx = mpbuild(sig + 2, hlen, hlen, &hxl);
- res = mpdsa(pxl, px, qxl, qx, gx, yx, rx, sx, hxl, hx);
- free(px);
- free(qx);
- free(gx);
- free(yx);
- free(rx);
- free(sx);
- free(hx);
- break;
- }
- default:
- return 0; /* unsupported pubkey algo */
- }
- return res;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2013, SUSE Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-extern int solv_pgpvrfy(const unsigned char *pub, int publ, const unsigned char *sig, int sigl);
-
+++ /dev/null
-/*
- * Copyright (c) 2011, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <zlib.h>
-#include <fcntl.h>
-
-#include "solv_xfopen.h"
-#include "util.h"
-
-
-/* Evil hack for Haiku: fopencookie() is implemented internally, but not
- exported by a header. */
-#ifdef __HAIKU__
-
-typedef struct {
- ssize_t (*read)(void*, char*, size_t);
- ssize_t (*write)(void*, const char*, size_t);
- int (*seek)(off_t*, int);
- int (*close)(void*);
-} cookie_io_functions_t;
-
-
-FILE *fopencookie(void*, const char*, cookie_io_functions_t);
-
-#endif /* __HAIKU__ */
-
-
-static FILE *cookieopen(void *cookie, const char *mode,
- ssize_t (*cread)(void *, char *, size_t),
- ssize_t (*cwrite)(void *, const char *, size_t),
- int (*cclose)(void *))
-{
-#ifdef HAVE_FUNOPEN
- if (!cookie)
- return 0;
- return funopen(cookie,
- (int (*)(void *, char *, int))(*mode == 'r' ? cread : NULL), /* readfn */
- (int (*)(void *, const char *, int))(*mode == 'w' ? cwrite : NULL), /* writefn */
- (fpos_t (*)(void *, fpos_t, int))NULL, /* seekfn */
- cclose
- );
-#elif defined(HAVE_FOPENCOOKIE)
- cookie_io_functions_t cio;
-
- if (!cookie)
- return 0;
- memset(&cio, 0, sizeof(cio));
- if (*mode == 'r')
- cio.read = cread;
- else if (*mode == 'w')
- cio.write = cwrite;
- cio.close = cclose;
- return fopencookie(cookie, *mode == 'w' ? "w" : "r", cio);
-#else
-# error Need to implement custom I/O
-#endif
-}
-
-
-/* gzip compression */
-
-static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
-{
- return gzread((gzFile)cookie, buf, nbytes);
-}
-
-static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
-{
- return gzwrite((gzFile)cookie, buf, nbytes);
-}
-
-static int cookie_gzclose(void *cookie)
-{
- return gzclose((gzFile)cookie);
-}
-
-static inline FILE *mygzfopen(const char *fn, const char *mode)
-{
- gzFile gzf = gzopen(fn, mode);
- return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
-}
-
-static inline FILE *mygzfdopen(int fd, const char *mode)
-{
- gzFile gzf = gzdopen(fd, mode);
- return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
-}
-
-#ifdef ENABLE_BZIP2_COMPRESSION
-
-#include <bzlib.h>
-
-/* bzip2 compression */
-
-static ssize_t cookie_bzread(void *cookie, char *buf, size_t nbytes)
-{
- return BZ2_bzread((BZFILE *)cookie, buf, nbytes);
-}
-
-static ssize_t cookie_bzwrite(void *cookie, const char *buf, size_t nbytes)
-{
- return BZ2_bzwrite((BZFILE *)cookie, (char *)buf, nbytes);
-}
-
-static int cookie_bzclose(void *cookie)
-{
- BZ2_bzclose((BZFILE *)cookie);
- return 0;
-}
-
-static inline FILE *mybzfopen(const char *fn, const char *mode)
-{
- BZFILE *bzf = BZ2_bzopen(fn, mode);
- return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
-}
-
-static inline FILE *mybzfdopen(int fd, const char *mode)
-{
- BZFILE *bzf = BZ2_bzdopen(fd, mode);
- return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
-}
-
-#endif
-
-
-#ifdef ENABLE_LZMA_COMPRESSION
-
-#include <lzma.h>
-
-/* lzma code written by me in 2008 for rpm's rpmio.c */
-
-typedef struct lzfile {
- unsigned char buf[1 << 15];
- lzma_stream strm;
- FILE *file;
- int encoding;
- int eof;
-} LZFILE;
-
-static inline lzma_ret setup_alone_encoder(lzma_stream *strm, int level)
-{
- lzma_options_lzma options;
- lzma_lzma_preset(&options, level);
- return lzma_alone_encoder(strm, &options);
-}
-
-static lzma_stream stream_init = LZMA_STREAM_INIT;
-
-static LZFILE *lzopen(const char *path, const char *mode, int fd, int isxz)
-{
- int level = 7;
- int encoding = 0;
- FILE *fp;
- LZFILE *lzfile;
- lzma_ret ret;
-
- if (!path && fd < 0)
- return 0;
- for (; *mode; mode++)
- {
- if (*mode == 'w')
- encoding = 1;
- else if (*mode == 'r')
- encoding = 0;
- else if (*mode >= '1' && *mode <= '9')
- level = *mode - '0';
- }
- if (fd != -1)
- fp = fdopen(fd, encoding ? "w" : "r");
- else
- fp = fopen(path, encoding ? "w" : "r");
- if (!fp)
- return 0;
- lzfile = calloc(1, sizeof(*lzfile));
- if (!lzfile)
- {
- fclose(fp);
- return 0;
- }
- lzfile->file = fp;
- lzfile->encoding = encoding;
- lzfile->eof = 0;
- lzfile->strm = stream_init;
- if (encoding)
- {
- if (isxz)
- ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
- else
- ret = setup_alone_encoder(&lzfile->strm, level);
- }
- else
- ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
- if (ret != LZMA_OK)
- {
- fclose(fp);
- free(lzfile);
- return 0;
- }
- return lzfile;
-}
-
-static int lzclose(void *cookie)
-{
- LZFILE *lzfile = cookie;
- lzma_ret ret;
- size_t n;
- int rc;
-
- if (!lzfile)
- return -1;
- if (lzfile->encoding)
- {
- for (;;)
- {
- lzfile->strm.avail_out = sizeof(lzfile->buf);
- lzfile->strm.next_out = lzfile->buf;
- ret = lzma_code(&lzfile->strm, LZMA_FINISH);
- if (ret != LZMA_OK && ret != LZMA_STREAM_END)
- return -1;
- n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
- if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
- return -1;
- if (ret == LZMA_STREAM_END)
- break;
- }
- }
- lzma_end(&lzfile->strm);
- rc = fclose(lzfile->file);
- free(lzfile);
- return rc;
-}
-
-static ssize_t lzread(void *cookie, char *buf, size_t len)
-{
- LZFILE *lzfile = cookie;
- lzma_ret ret;
- int eof = 0;
-
- if (!lzfile || lzfile->encoding)
- return -1;
- if (lzfile->eof)
- return 0;
- lzfile->strm.next_out = (unsigned char *)buf;
- lzfile->strm.avail_out = len;
- for (;;)
- {
- if (!lzfile->strm.avail_in)
- {
- lzfile->strm.next_in = lzfile->buf;
- lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
- if (!lzfile->strm.avail_in)
- eof = 1;
- }
- ret = lzma_code(&lzfile->strm, LZMA_RUN);
- if (ret == LZMA_STREAM_END)
- {
- lzfile->eof = 1;
- return len - lzfile->strm.avail_out;
- }
- if (ret != LZMA_OK)
- return -1;
- if (!lzfile->strm.avail_out)
- return len;
- if (eof)
- return -1;
- }
-}
-
-static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
-{
- LZFILE *lzfile = cookie;
- lzma_ret ret;
- size_t n;
- if (!lzfile || !lzfile->encoding)
- return -1;
- if (!len)
- return 0;
- lzfile->strm.next_in = (unsigned char *)buf;
- lzfile->strm.avail_in = len;
- for (;;)
- {
- lzfile->strm.next_out = lzfile->buf;
- lzfile->strm.avail_out = sizeof(lzfile->buf);
- ret = lzma_code(&lzfile->strm, LZMA_RUN);
- if (ret != LZMA_OK)
- return -1;
- n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
- if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
- return -1;
- if (!lzfile->strm.avail_in)
- return len;
- }
-}
-
-static inline FILE *myxzfopen(const char *fn, const char *mode)
-{
- LZFILE *lzf = lzopen(fn, mode, -1, 1);
- return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
-}
-
-static inline FILE *myxzfdopen(int fd, const char *mode)
-{
- LZFILE *lzf = lzopen(0, mode, fd, 1);
- return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
-}
-
-static inline FILE *mylzfopen(const char *fn, const char *mode)
-{
- LZFILE *lzf = lzopen(fn, mode, -1, 0);
- return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
-}
-
-static inline FILE *mylzfdopen(int fd, const char *mode)
-{
- LZFILE *lzf = lzopen(0, mode, fd, 0);
- return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
-}
-
-#endif /* ENABLE_LZMA_COMPRESSION */
-
-
-FILE *
-solv_xfopen(const char *fn, const char *mode)
-{
- char *suf;
-
- if (!fn)
- return 0;
- if (!mode)
- mode = "r";
- suf = strrchr(fn, '.');
- if (suf && !strcmp(suf, ".gz"))
- return mygzfopen(fn, mode);
-#ifdef ENABLE_LZMA_COMPRESSION
- if (suf && !strcmp(suf, ".xz"))
- return myxzfopen(fn, mode);
- if (suf && !strcmp(suf, ".lzma"))
- return mylzfopen(fn, mode);
-#else
- if (suf && !strcmp(suf, ".xz"))
- return 0;
- if (suf && !strcmp(suf, ".lzma"))
- return 0;
-#endif
-#ifdef ENABLE_BZIP2_COMPRESSION
- if (suf && !strcmp(suf, ".bz2"))
- return mybzfopen(fn, mode);
-#else
- if (suf && !strcmp(suf, ".bz2"))
- return 0;
-#endif
- return fopen(fn, mode);
-}
-
-FILE *
-solv_xfopen_fd(const char *fn, int fd, const char *mode)
-{
- const char *simplemode = mode;
- char *suf;
-
- suf = fn ? strrchr(fn, '.') : 0;
- if (!mode)
- {
- int fl = fcntl(fd, F_GETFL, 0);
- if (fl == -1)
- return 0;
- fl &= O_RDONLY|O_WRONLY|O_RDWR;
- if (fl == O_WRONLY)
- mode = simplemode = "w";
- else if (fl == O_RDWR)
- {
- mode = "r+";
- simplemode = "r";
- }
- else
- mode = simplemode = "r";
- }
- if (suf && !strcmp(suf, ".gz"))
- return mygzfdopen(fd, simplemode);
-#ifdef ENABLE_LZMA_COMPRESSION
- if (suf && !strcmp(suf, ".xz"))
- return myxzfdopen(fd, simplemode);
- if (suf && !strcmp(suf, ".lzma"))
- return mylzfdopen(fd, simplemode);
-#else
- if (suf && !strcmp(suf, ".xz"))
- return 0;
- if (suf && !strcmp(suf, ".lzma"))
- return 0;
-#endif
-#ifdef ENABLE_BZIP2_COMPRESSION
- if (suf && !strcmp(suf, ".bz2"))
- return mybzfdopen(fd, simplemode);
-#else
- if (suf && !strcmp(suf, ".bz2"))
- return 0;
-#endif
- return fdopen(fd, mode);
-}
-
-int
-solv_xfopen_iscompressed(const char *fn)
-{
- const char *suf = fn ? strrchr(fn, '.') : 0;
- if (!suf)
- return 0;
- if (!strcmp(suf, ".gz"))
- return 1;
- if (!strcmp(suf, ".xz") || !strcmp(suf, ".lzma"))
-#ifdef ENABLE_LZMA_COMPRESSION
- return 1;
-#else
- return -1;
-#endif
- if (!strcmp(suf, ".bz2"))
-#ifdef ENABLE_BZIP2_COMPRESSION
- return 1;
-#else
- return -1;
-#endif
- return 0;
-}
-
-struct bufcookie {
- char **bufp;
- size_t *buflp;
- char *freemem;
- size_t bufl_int;
-};
-
-static ssize_t cookie_bufread(void *cookie, char *buf, size_t nbytes)
-{
- struct bufcookie *bc = cookie;
- size_t n = *bc->buflp > nbytes ? nbytes : *bc->buflp;
- if (n)
- {
- memcpy(buf, *bc->bufp, n);
- *bc->bufp += n;
- *bc->buflp -= n;
- }
- return n;
-}
-
-static ssize_t cookie_bufwrite(void *cookie, const char *buf, size_t nbytes)
-{
- struct bufcookie *bc = cookie;
- int n = nbytes > 0x40000000 ? 0x40000000 : nbytes;
- if (n)
- {
- *bc->bufp = solv_extend(*bc->bufp, *bc->buflp, n + 1, 1, 4095);
- memcpy(*bc->bufp, buf, n);
- (*bc->bufp)[n] = 0; /* zero-terminate */
- *bc->buflp += n;
- }
- return n;
-}
-
-static int cookie_bufclose(void *cookie)
-{
- struct bufcookie *bc = cookie;
- if (bc->freemem)
- solv_free(bc->freemem);
- solv_free(bc);
- return 0;
-}
-
-FILE *
-solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
-{
- struct bufcookie *bc;
- FILE *fp;
- if (*mode != 'r' && *mode != 'w')
- return 0;
- bc = solv_calloc(1, sizeof(*bc));
- bc->freemem = 0;
- bc->bufp = bufp;
- if (!buflp)
- {
- bc->bufl_int = *mode == 'w' ? 0 : strlen(*bufp);
- buflp = &bc->bufl_int;
- }
- bc->buflp = buflp;
- if (*mode == 'w')
- {
- *bc->bufp = solv_extend(0, 0, 1, 1, 4095); /* always zero-terminate */
- (*bc->bufp)[0] = 0;
- *bc->buflp = 0;
- }
- fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
- if (!strcmp(mode, "rf")) /* auto-free */
- bc->freemem = *bufp;
- if (!fp)
- {
- if (*mode == 'w')
- *bc->bufp = solv_free(*bc->bufp);
- cookie_bufclose(bc);
- }
- return fp;
-}
+++ /dev/null
-/*
- * Copyright (c) 2009-2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef SOLV_XFOPEN_H
-#define SOLV_XFOPEN_H
-
-extern FILE *solv_xfopen(const char *fn, const char *mode);
-extern FILE *solv_xfopen_fd(const char *fn, int fd, const char *mode);
-extern FILE *solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode);
-extern int solv_xfopen_iscompressed(const char *fn);
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "poolarch.h"
-#include "poolvendor.h"
-#include "repo.h"
-#include "repo_solv.h"
-#include "solver.h"
-#include "solverdebug.h"
-#include "chksum.h"
-#include "testcase.h"
-#include "selection.h"
-#include "solv_xfopen.h"
-
-#define DISABLE_JOIN2
-#include "tools_util.h"
-
-static struct job2str {
- Id job;
- const char *str;
-} job2str[] = {
- { SOLVER_NOOP, "noop" },
- { SOLVER_INSTALL, "install" },
- { SOLVER_ERASE, "erase" },
- { SOLVER_UPDATE, "update" },
- { SOLVER_WEAKENDEPS, "weakendeps" },
- { SOLVER_MULTIVERSION, "multiversion" },
- { SOLVER_MULTIVERSION, "noobsoletes" }, /* old name */
- { SOLVER_LOCK, "lock" },
- { SOLVER_DISTUPGRADE, "distupgrade" },
- { SOLVER_VERIFY, "verify" },
- { SOLVER_DROP_ORPHANED, "droporphaned" },
- { SOLVER_USERINSTALLED, "userinstalled" },
- { SOLVER_ALLOWUNINSTALL, "allowuninstall" },
- { 0, 0 }
-};
-
-static struct jobflags2str {
- Id flag;
- const char *str;
-} jobflags2str[] = {
- { SOLVER_WEAK, "weak" },
- { SOLVER_ESSENTIAL, "essential" },
- { SOLVER_CLEANDEPS, "cleandeps" },
- { SOLVER_ORUPDATE, "orupdate" },
- { SOLVER_FORCEBEST, "forcebest" },
- { SOLVER_TARGETED, "targeted" },
- { SOLVER_NOTBYUSER, "notbyuser" },
- { SOLVER_SETEV, "setev" },
- { SOLVER_SETEVR, "setevr" },
- { SOLVER_SETARCH, "setarch" },
- { SOLVER_SETVENDOR, "setvendor" },
- { SOLVER_SETREPO, "setrepo" },
- { SOLVER_NOAUTOSET, "noautoset" },
- { 0, 0 }
-};
-
-static struct resultflags2str {
- Id flag;
- const char *str;
-} resultflags2str[] = {
- { TESTCASE_RESULT_TRANSACTION, "transaction" },
- { TESTCASE_RESULT_PROBLEMS, "problems" },
- { TESTCASE_RESULT_ORPHANED, "orphaned" },
- { TESTCASE_RESULT_RECOMMENDED, "recommended" },
- { TESTCASE_RESULT_UNNEEDED, "unneeded" },
- { TESTCASE_RESULT_ALTERNATIVES, "alternatives" },
- { TESTCASE_RESULT_RULES, "rules" },
- { TESTCASE_RESULT_GENID, "genid" },
- { 0, 0 }
-};
-
-static struct solverflags2str {
- Id flag;
- const char *str;
- int def;
-} solverflags2str[] = {
- { SOLVER_FLAG_ALLOW_DOWNGRADE, "allowdowngrade", 0 },
- { SOLVER_FLAG_ALLOW_NAMECHANGE, "allownamechange", 1 },
- { SOLVER_FLAG_ALLOW_ARCHCHANGE, "allowarchchange", 0 },
- { SOLVER_FLAG_ALLOW_VENDORCHANGE, "allowvendorchange", 0 },
- { SOLVER_FLAG_ALLOW_UNINSTALL, "allowuninstall", 0 },
- { SOLVER_FLAG_NO_UPDATEPROVIDE, "noupdateprovide", 0 },
- { SOLVER_FLAG_SPLITPROVIDES, "splitprovides", 0 },
- { SOLVER_FLAG_IGNORE_RECOMMENDED, "ignorerecommended", 0 },
- { SOLVER_FLAG_ADD_ALREADY_RECOMMENDED, "addalreadyrecommended", 0 },
- { SOLVER_FLAG_NO_INFARCHCHECK, "noinfarchcheck", 0 },
- { SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES, "keepexplicitobsoletes", 0 },
- { SOLVER_FLAG_BEST_OBEY_POLICY, "bestobeypolicy", 0 },
- { SOLVER_FLAG_NO_AUTOTARGET, "noautotarget", 0 },
- { SOLVER_FLAG_DUP_ALLOW_DOWNGRADE, "dupallowdowngrade", 1 },
- { SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE, "dupallowarchchange", 1 },
- { SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE, "dupallowvendorchange", 1 },
- { SOLVER_FLAG_DUP_ALLOW_NAMECHANGE, "dupallownamechange", 1 },
- { SOLVER_FLAG_KEEP_ORPHANS, "keeporphans", 0 },
- { SOLVER_FLAG_BREAK_ORPHANS, "breakorphans", 0 },
- { SOLVER_FLAG_FOCUS_INSTALLED, "focusinstalled", 0 },
- { SOLVER_FLAG_YUM_OBSOLETES, "yumobsoletes", 0 },
- { SOLVER_FLAG_NEED_UPDATEPROVIDE, "needupdateprovide", 0 },
- { 0, 0, 0 }
-};
-
-static struct poolflags2str {
- Id flag;
- const char *str;
- int def;
-} poolflags2str[] = {
- { POOL_FLAG_PROMOTEEPOCH, "promoteepoch", 0 },
- { POOL_FLAG_FORBIDSELFCONFLICTS, "forbidselfconflicts", 0 },
- { POOL_FLAG_OBSOLETEUSESPROVIDES, "obsoleteusesprovides", 0 },
- { POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES, "implicitobsoleteusesprovides", 0 },
- { POOL_FLAG_OBSOLETEUSESCOLORS, "obsoleteusescolors", 0 },
- { POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS, "implicitobsoleteusescolors", 0 },
- { POOL_FLAG_NOINSTALLEDOBSOLETES, "noinstalledobsoletes", 0 },
- { POOL_FLAG_HAVEDISTEPOCH, "havedistepoch", 0 },
- { POOL_FLAG_NOOBSOLETESMULTIVERSION, "noobsoletesmultiversion", 0 },
- { POOL_FLAG_ADDFILEPROVIDESFILTERED, "addfileprovidesfiltered", 0 },
- { POOL_FLAG_NOWHATPROVIDESAUX, "nowhatprovidesaux", 0 },
- { 0, 0, 0 }
-};
-
-static struct disttype2str {
- Id type;
- const char *str;
-} disttype2str[] = {
- { DISTTYPE_RPM, "rpm" },
- { DISTTYPE_DEB, "deb" },
- { DISTTYPE_ARCH, "arch" },
- { DISTTYPE_HAIKU, "haiku" },
- { 0, 0 }
-};
-
-static struct selflags2str {
- Id flag;
- const char *str;
-} selflags2str[] = {
- { SELECTION_NAME, "name" },
- { SELECTION_PROVIDES, "provides" },
- { SELECTION_FILELIST, "filelist" },
- { SELECTION_CANON, "canon" },
- { SELECTION_DOTARCH, "dotarch" },
- { SELECTION_REL, "rel" },
- { SELECTION_INSTALLED_ONLY, "installedonly" },
- { SELECTION_GLOB, "glob" },
- { SELECTION_FLAT, "flat" },
- { SELECTION_NOCASE, "nocase" },
- { SELECTION_SOURCE_ONLY, "sourceonly" },
- { SELECTION_WITH_SOURCE, "withsource" },
- { 0, 0 }
-};
-
-static const char *features[] = {
-#ifdef ENABLE_LINKED_PKGS
- "linked_packages",
-#endif
-#ifdef ENABLE_COMPLEX_DEPS
- "complex_deps",
-#endif
- 0
-};
-
-typedef struct strqueue {
- char **str;
- int nstr;
-} Strqueue;
-
-#define STRQUEUE_BLOCK 63
-
-static void
-strqueue_init(Strqueue *q)
-{
- q->str = 0;
- q->nstr = 0;
-}
-
-static void
-strqueue_free(Strqueue *q)
-{
- int i;
- for (i = 0; i < q->nstr; i++)
- solv_free(q->str[i]);
- q->str = solv_free(q->str);
- q->nstr = 0;
-}
-
-static void
-strqueue_push(Strqueue *q, const char *s)
-{
- q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
- q->str[q->nstr++] = solv_strdup(s);
-}
-
-static void
-strqueue_pushjoin(Strqueue *q, const char *s1, const char *s2, const char *s3)
-{
- q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
- q->str[q->nstr++] = solv_dupjoin(s1, s2, s3);
-}
-
-static int
-strqueue_sort_cmp(const void *ap, const void *bp, void *dp)
-{
- const char *a = *(const char **)ap;
- const char *b = *(const char **)bp;
- return strcmp(a ? a : "", b ? b : "");
-}
-
-static void
-strqueue_sort(Strqueue *q)
-{
- if (q->nstr > 1)
- solv_sort(q->str, q->nstr, sizeof(*q->str), strqueue_sort_cmp, 0);
-}
-
-static void
-strqueue_sort_u(Strqueue *q)
-{
- int i, j;
- strqueue_sort(q);
- for (i = j = 0; i < q->nstr; i++)
- if (!j || strqueue_sort_cmp(q->str + i, q->str + j - 1, 0) != 0)
- q->str[j++] = q->str[i];
- q->nstr = j;
-}
-
-static char *
-strqueue_join(Strqueue *q)
-{
- int i, l = 0;
- char *r, *rp;
- for (i = 0; i < q->nstr; i++)
- if (q->str[i])
- l += strlen(q->str[i]) + 1;
- l++; /* trailing \0 */
- r = solv_malloc(l);
- rp = r;
- for (i = 0; i < q->nstr; i++)
- if (q->str[i])
- {
- strcpy(rp, q->str[i]);
- rp += strlen(rp);
- *rp++ = '\n';
- }
- *rp = 0;
- return r;
-}
-
-static void
-strqueue_split(Strqueue *q, const char *s)
-{
- const char *p;
- if (!s)
- return;
- while ((p = strchr(s, '\n')) != 0)
- {
- q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
- q->str[q->nstr] = solv_malloc(p - s + 1);
- if (p > s)
- memcpy(q->str[q->nstr], s, p - s);
- q->str[q->nstr][p - s] = 0;
- q->nstr++;
- s = p + 1;
- }
- if (*s)
- strqueue_push(q, s);
-}
-
-static void
-strqueue_diff(Strqueue *sq1, Strqueue *sq2, Strqueue *osq)
-{
- int i = 0, j = 0;
- while (i < sq1->nstr && j < sq2->nstr)
- {
- int r = strqueue_sort_cmp(sq1->str + i, sq2->str + j, 0);
- if (!r)
- i++, j++;
- else if (r < 0)
- strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
- else
- strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
- }
- while (i < sq1->nstr)
- strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
- while (j < sq2->nstr)
- strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
-}
-
-
-static const char *
-testcase_id2str(Pool *pool, Id id, int isname)
-{
- const char *s = pool_id2str(pool, id);
- const char *ss;
- char *s2, *s2p;
- int bad = 0, paren = 0, parenbad = 0;
-
- if (id == 0)
- return "<NULL>";
- if (id == 1)
- return "\\00";
- if (strchr("[(<=>!", *s))
- bad++;
- if (!strncmp(s, "namespace:", 10))
- bad++;
- for (ss = s + bad; *ss; ss++)
- {
- if (*ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
- bad++;
- if (*ss == '(')
- paren = paren == 0 ? 1 : -1;
- else if (*ss == ')')
- {
- paren = paren == 1 ? 0 : -1;
- if (!paren)
- parenbad += 2;
- }
- }
- if (isname && ss - s > 4 && !strcmp(ss - 4, ":any"))
- bad++;
- if (!paren && !(bad - parenbad))
- return s;
-
- /* we need escaping! */
- s2 = s2p = pool_alloctmpspace(pool, strlen(s) + bad * 2 + 1);
- if (!strncmp(s, "namespace:", 10))
- {
- strcpy(s2p, "namespace\\3a");
- s2p += 12;
- s += 10;
- }
- ss = s;
- for (; *ss; ss++)
- {
- *s2p++ = *ss;
- if ((ss == s && strchr("[(<=>!", *s)) || *ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
- {
- s2p[-1] = '\\';
- solv_bin2hex((unsigned char *)ss, 1, s2p);
- s2p += 2;
- }
- }
- *s2p = 0;
- if (isname && s2p - s2 > 4 && !strcmp(s2p - 4, ":any"))
- strcpy(s2p - 4, "\\3aany");
- return s2;
-}
-
-struct oplist {
- Id flags;
- const char *opname;
-} oplist[] = {
- { REL_EQ, "=" },
- { REL_GT | REL_LT | REL_EQ, "<=>" },
- { REL_LT | REL_EQ, "<=" },
- { REL_GT | REL_EQ, ">=" },
- { REL_GT, ">" },
- { REL_GT | REL_LT, "<>" },
- { REL_AND, "&" },
- { REL_OR , "|" },
- { REL_WITH , "+" },
- { REL_NAMESPACE , "<NAMESPACE>" },
- { REL_ARCH, "." },
- { REL_MULTIARCH, "<MULTIARCH>" },
- { REL_FILECONFLICT, "<FILECONFLICT>" },
- { REL_COND, "<IF>" },
- { REL_COMPAT, "compat >=" },
- { REL_KIND, "<KIND>" },
- { REL_ELSE, "<ELSE>" },
- { REL_LT, "<" },
- { 0, 0 }
-};
-
-static const char *
-testcase_dep2str_complex(Pool *pool, Id id, int addparens)
-{
- Reldep *rd;
- char *s;
- const char *s2;
- int needparens;
- struct oplist *op;
-
- if (!ISRELDEP(id))
- return testcase_id2str(pool, id, 1);
- rd = GETRELDEP(pool, id);
-
- /* check for special shortcuts */
- if (rd->flags == REL_NAMESPACE && !ISRELDEP(rd->name) && !strncmp(pool_id2str(pool, rd->name), "namespace:", 10))
- {
- const char *ns = pool_id2str(pool, rd->name);
- int nslen = strlen(ns);
- /* special namespace formatting */
- const char *evrs = testcase_dep2str_complex(pool, rd->evr, 0);
- s = pool_tmpappend(pool, evrs, ns, "()");
- memmove(s + nslen + 1, s, strlen(s) - nslen - 2);
- memcpy(s, ns, nslen);
- s[nslen] = '(';
- return s;
- }
- if (rd->flags == REL_MULTIARCH && !ISRELDEP(rd->name) && rd->evr == ARCH_ANY)
- {
- /* special :any suffix */
- const char *ns = testcase_id2str(pool, rd->name, 1);
- return pool_tmpappend(pool, ns, ":any", 0);
- }
-
- needparens = 0;
- if (ISRELDEP(rd->name))
- {
- Reldep *rd2 = GETRELDEP(pool, rd->name);
- needparens = 1;
- if (rd->flags > 7 && rd->flags != REL_COMPAT && rd2->flags && rd2->flags <= 7)
- needparens = 0;
- }
- s = (char *)testcase_dep2str_complex(pool, rd->name, needparens);
-
- if (addparens)
- {
- s = pool_tmpappend(pool, s, "(", 0);
- memmove(s + 1, s, strlen(s + 1));
- s[0] = '(';
- }
- for (op = oplist; op->flags; op++)
- if (rd->flags == op->flags)
- break;
- if (op->flags)
- {
- s = pool_tmpappend(pool, s, " ", op->opname);
- s = pool_tmpappend(pool, s, " ", 0);
- }
- else
- {
- char buf[64];
- sprintf(buf, " <%u> ", rd->flags);
- s = pool_tmpappend(pool, s, buf, 0);
- }
-
- needparens = 0;
- if (ISRELDEP(rd->evr))
- {
- Reldep *rd2 = GETRELDEP(pool, rd->evr);
- needparens = 1;
- if (rd->flags > 7 && rd2->flags && rd2->flags <= 7)
- needparens = 0;
- if (rd->flags == REL_AND && rd2->flags == REL_AND)
- needparens = 0; /* chain */
- if (rd->flags == REL_OR && rd2->flags == REL_OR)
- needparens = 0; /* chain */
- if (rd->flags > 0 && rd->flags < 8 && rd2->flags == REL_COMPAT)
- needparens = 0; /* chain */
- }
- if (!ISRELDEP(rd->evr))
- s2 = testcase_id2str(pool, rd->evr, 0);
- else
- s2 = testcase_dep2str_complex(pool, rd->evr, needparens);
- if (addparens)
- s = pool_tmpappend(pool, s, s2, ")");
- else
- s = pool_tmpappend(pool, s, s2, 0);
- pool_freetmpspace(pool, s2);
- return s;
-}
-
-const char *
-testcase_dep2str(Pool *pool, Id id)
-{
- return testcase_dep2str_complex(pool, id, 0);
-}
-
-
-/* Convert a simple string. Also handle the :any suffix */
-static Id
-testcase_str2dep_simple(Pool *pool, const char **sp, int isname)
-{
- int haveesc = 0;
- int paren = 0;
- int isany = 0;
- Id id;
- const char *s;
- for (s = *sp; *s; s++)
- {
- if (*s == '\\')
- haveesc++;
- if (*s == ' ' || *(unsigned char *)s < 32)
- break;
- if (*s == '(')
- paren++;
- if (*s == ')' && paren-- <= 0)
- break;
- }
- if (isname && s - *sp > 4 && !strncmp(s - 4, ":any", 4))
- {
- isany = 1;
- s -= 4;
- }
- if (!haveesc)
- {
- if (s - *sp == 6 && !strncmp(*sp, "<NULL>", 6))
- id = 0;
- else
- id = pool_strn2id(pool, *sp, s - *sp, 1);
- }
- else if (s - *sp == 3 && !strncmp(*sp, "\\00", 3))
- id = 1;
- else
- {
- char buf[128], *bp, *bp2;
- const char *sp2;
- bp = s - *sp >= 128 ? solv_malloc(s - *sp + 1) : buf;
- for (bp2 = bp, sp2 = *sp; sp2 < s;)
- {
- *bp2++ = *sp2++;
- if (bp2[-1] == '\\')
- solv_hex2bin(&sp2, (unsigned char *)bp2 - 1, 1);
- }
- *bp2 = 0;
- id = pool_str2id(pool, bp, 1);
- if (bp != buf)
- solv_free(bp);
- }
- if (isany)
- {
- id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
- s += 4;
- }
- *sp = s;
- return id;
-}
-
-
-static Id
-testcase_str2dep_complex(Pool *pool, const char **sp, int relop)
-{
- const char *s = *sp;
- Id flags, id, id2, namespaceid = 0;
- struct oplist *op;
-
- while (*s == ' ' || *s == '\t')
- s++;
- if (!strncmp(s, "namespace:", 10))
- {
- /* special namespace hack */
- const char *s2;
- for (s2 = s + 10; *s2 && *s2 != '('; s2++)
- ;
- if (*s2 == '(')
- {
- namespaceid = pool_strn2id(pool, s, s2 - s, 1);
- s = s2;
- }
- }
- if (*s == '(')
- {
- s++;
- id = testcase_str2dep_complex(pool, &s, 0);
- if (!s || *s != ')')
- {
- *sp = 0;
- return 0;
- }
- s++;
- }
- else
- id = testcase_str2dep_simple(pool, &s, relop ? 0 : 1);
- if (namespaceid)
- id = pool_rel2id(pool, namespaceid, id, REL_NAMESPACE, 1);
-
- for (;;)
- {
- while (*s == ' ' || *s == '\t')
- s++;
- if (!*s || *s == ')' || (relop && strncmp(s, "compat >= ", 10) != 0))
- {
- *sp = s;
- return id;
- }
-
- /* we have an op! Find the end */
- flags = -1;
- if (s[0] == '<' && (s[1] >= '0' && s[1] <= '9'))
- {
- const char *s2;
- for (s2 = s + 1; *s2 >= '0' && *s2 <= '9'; s2++)
- ;
- if (*s2 == '>')
- {
- flags = strtoul(s + 1, 0, 10);
- s = s2 + 1;
- }
- }
- if (flags == -1)
- {
- for (op = oplist; op->flags; op++)
- if (!strncmp(s, op->opname, strlen(op->opname)))
- break;
- if (!op->flags)
- {
- *sp = 0;
- return 0;
- }
- flags = op->flags;
- s += strlen(op->opname);
- }
- id2 = testcase_str2dep_complex(pool, &s, flags > 0 && flags < 8);
- if (!s)
- {
- *sp = 0;
- return 0;
- }
- id = pool_rel2id(pool, id, id2, flags, 1);
- }
-}
-
-Id
-testcase_str2dep(Pool *pool, const char *s)
-{
- Id id = testcase_str2dep_complex(pool, &s, 0);
- return s && !*s ? id : 0;
-}
-
-/**********************************************************/
-
-const char *
-testcase_repoid2str(Pool *pool, Id repoid)
-{
- Repo *repo = pool_id2repo(pool, repoid);
- if (repo->name)
- {
- char *r = pool_tmpjoin(pool, repo->name, 0, 0);
- char *rp;
- for (rp = r; *rp; rp++)
- if (*rp == ' ' || *rp == '\t')
- *rp = '_';
- return r;
- }
- else
- {
- char buf[20];
- sprintf(buf, "#%d", repoid);
- return pool_tmpjoin(pool, buf, 0, 0);
- }
-}
-
-const char *
-testcase_solvid2str(Pool *pool, Id p)
-{
- Solvable *s = pool->solvables + p;
- const char *n, *e, *a;
- char *str, buf[20];
-
- if (p == SYSTEMSOLVABLE)
- return "@SYSTEM";
- n = pool_id2str(pool, s->name);
- e = pool_id2str(pool, s->evr);
- a = pool_id2str(pool, s->arch);
- str = pool_alloctmpspace(pool, strlen(n) + strlen(e) + strlen(a) + 3);
- sprintf(str, "%s-%s.%s", n, e, a);
- if (!s->repo)
- return pool_tmpappend(pool, str, "@", 0);
- if (s->repo->name)
- {
- int l = strlen(str);
- char *str2 = pool_tmpappend(pool, str, "@", s->repo->name);
- for (; str2[l]; l++)
- if (str2[l] == ' ' || str2[l] == '\t')
- str2[l] = '_';
- return str2;
- }
- sprintf(buf, "@#%d", s->repo->repoid);
- return pool_tmpappend(pool, str, buf, 0);
-}
-
-Repo *
-testcase_str2repo(Pool *pool, const char *str)
-{
- int repoid;
- Repo *repo = 0;
- if (str[0] == '#' && (str[1] >= '0' && str[1] <= '9'))
- {
- int j;
- repoid = 0;
- for (j = 1; str[j] >= '0' && str[j] <= '9'; j++)
- repoid = repoid * 10 + (str[j] - '0');
- if (!str[j] && repoid > 0 && repoid < pool->nrepos)
- repo = pool_id2repo(pool, repoid);
- }
- if (!repo)
- {
- FOR_REPOS(repoid, repo)
- {
- int i, l;
- if (!repo->name)
- continue;
- l = strlen(repo->name);
- for (i = 0; i < l; i++)
- {
- int c = repo->name[i];
- if (c == ' ' || c == '\t')
- c = '_';
- if (c != str[i])
- break;
- }
- if (i == l && !str[l])
- break;
- }
- if (repoid >= pool->nrepos)
- repo = 0;
- }
- return repo;
-}
-
-Id
-testcase_str2solvid(Pool *pool, const char *str)
-{
- int i, l = strlen(str);
- int repostart;
- Repo *repo;
- Id arch;
-
- if (!l)
- return 0;
- if (*str == '@' && !strcmp(str, "@SYSTEM"))
- return SYSTEMSOLVABLE;
- repo = 0;
- for (i = l - 1; i >= 0; i--)
- if (str[i] == '@' && (repo = testcase_str2repo(pool, str + i + 1)) != 0)
- break;
- if (i < 0)
- i = l;
- repostart = i;
- /* now find the arch (if present) */
- arch = 0;
- for (i = repostart - 1; i > 0; i--)
- if (str[i] == '.')
- {
- arch = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
- if (arch)
- repostart = i;
- break;
- }
- /* now find the name */
- for (i = repostart - 1; i > 0; i--)
- {
- if (str[i] == '-')
- {
- Id nid, evrid, p, pp;
- nid = pool_strn2id(pool, str, i, 0);
- if (!nid)
- continue;
- evrid = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
- if (!evrid)
- continue;
- FOR_PROVIDES(p, pp, nid)
- {
- Solvable *s = pool->solvables + p;
- if (s->name != nid || s->evr != evrid)
- continue;
- if (repo && s->repo != repo)
- continue;
- if (arch && s->arch != arch)
- continue;
- return p;
- }
- }
- }
- return 0;
-}
-
-const char *
-testcase_job2str(Pool *pool, Id how, Id what)
-{
- char *ret;
- const char *jobstr;
- const char *selstr;
- const char *pkgstr;
- int i, o;
- Id select = how & SOLVER_SELECTMASK;
-
- for (i = 0; job2str[i].str; i++)
- if ((how & SOLVER_JOBMASK) == job2str[i].job)
- break;
- jobstr = job2str[i].str ? job2str[i].str : "unknown";
- if (select == SOLVER_SOLVABLE)
- {
- selstr = " pkg ";
- pkgstr = testcase_solvid2str(pool, what);
- }
- else if (select == SOLVER_SOLVABLE_NAME)
- {
- selstr = " name ";
- pkgstr = testcase_dep2str(pool, what);
- }
- else if (select == SOLVER_SOLVABLE_PROVIDES)
- {
- selstr = " provides ";
- pkgstr = testcase_dep2str(pool, what);
- }
- else if (select == SOLVER_SOLVABLE_ONE_OF)
- {
- Id p;
- selstr = " oneof ";
- pkgstr = 0;
- while ((p = pool->whatprovidesdata[what++]) != 0)
- {
- const char *s = testcase_solvid2str(pool, p);
- if (pkgstr)
- {
- pkgstr = pool_tmpappend(pool, pkgstr, " ", s);
- pool_freetmpspace(pool, s);
- }
- else
- pkgstr = s;
- }
- if (!pkgstr)
- pkgstr = "nothing";
- }
- else if (select == SOLVER_SOLVABLE_REPO)
- {
- Repo *repo = pool_id2repo(pool, what);
- selstr = " repo ";
- if (!repo->name)
- {
- char buf[20];
- sprintf(buf, "#%d", repo->repoid);
- pkgstr = pool_tmpjoin(pool, buf, 0, 0);
- }
- else
- pkgstr = pool_tmpjoin(pool, repo->name, 0, 0);
- }
- else if (select == SOLVER_SOLVABLE_ALL)
- {
- selstr = " all ";
- pkgstr = "packages";
- }
- else
- {
- selstr = " unknown ";
- pkgstr = "unknown";
- }
- ret = pool_tmpjoin(pool, jobstr, selstr, pkgstr);
- o = strlen(ret);
- ret = pool_tmpappend(pool, ret, " ", 0);
- for (i = 0; jobflags2str[i].str; i++)
- if ((how & jobflags2str[i].flag) != 0)
- ret = pool_tmpappend(pool, ret, ",", jobflags2str[i].str);
- if (!ret[o + 1])
- ret[o] = 0;
- else
- {
- ret[o + 1] = '[';
- ret = pool_tmpappend(pool, ret, "]", 0);
- }
- return ret;
-}
-
-static int
-str2selflags(Pool *pool, char *s) /* modifies the string! */
-{
- int i, selflags = 0;
- while (s)
- {
- char *se = strchr(s, ',');
- if (se)
- *se++ = 0;
- for (i = 0; selflags2str[i].str; i++)
- if (!strcmp(s, selflags2str[i].str))
- {
- selflags |= selflags2str[i].flag;
- break;
- }
- if (!selflags2str[i].str)
- pool_debug(pool, SOLV_ERROR, "str2job: unknown selection flag '%s'\n", s);
- s = se;
- }
- return selflags;
-}
-
-static int
-str2jobflags(Pool *pool, char *s) /* modifies the string */
-{
- int i, jobflags = 0;
- while (s)
- {
- char *se = strchr(s, ',');
- if (se)
- *se++ = 0;
- for (i = 0; jobflags2str[i].str; i++)
- if (!strcmp(s, jobflags2str[i].str))
- {
- jobflags |= jobflags2str[i].flag;
- break;
- }
- if (!jobflags2str[i].str)
- pool_debug(pool, SOLV_ERROR, "str2job: unknown job flag '%s'\n", s);
- s = se;
- }
- return jobflags;
-}
-
-Id
-testcase_str2job(Pool *pool, const char *str, Id *whatp)
-{
- int i;
- Id job;
- Id what;
- char *s;
- char **pieces = 0;
- int npieces = 0;
-
- *whatp = 0;
- /* so we can patch it */
- s = pool_tmpjoin(pool, str, 0, 0);
- /* split it in pieces */
- for (;;)
- {
- while (*s == ' ' || *s == '\t')
- s++;
- if (!*s)
- break;
- pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
- pieces[npieces++] = s;
- while (*s && *s != ' ' && *s != '\t')
- s++;
- if (*s)
- *s++ = 0;
- }
- if (npieces < 3)
- {
- pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
- solv_free(pieces);
- return -1;
- }
-
- for (i = 0; job2str[i].str; i++)
- if (!strcmp(pieces[0], job2str[i].str))
- break;
- if (!job2str[i].str)
- {
- pool_debug(pool, SOLV_ERROR, "str2job: unknown job '%s'\n", str);
- solv_free(pieces);
- return -1;
- }
- job = job2str[i].job;
- what = 0;
- if (npieces > 3)
- {
- char *flags = pieces[npieces - 1];
- if (*flags == '[' && flags[strlen(flags) - 1] == ']')
- {
- npieces--;
- flags++;
- flags[strlen(flags) - 1] = 0;
- job |= str2jobflags(pool, flags);
- }
- }
- if (!strcmp(pieces[1], "pkg"))
- {
- if (npieces != 3)
- {
- pool_debug(pool, SOLV_ERROR, "str2job: bad pkg selector in '%s'\n", str);
- solv_free(pieces);
- return -1;
- }
- job |= SOLVER_SOLVABLE;
- what = testcase_str2solvid(pool, pieces[2]);
- if (!what)
- {
- pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[2]);
- solv_free(pieces);
- return -1;
- }
- }
- else if (!strcmp(pieces[1], "name") || !strcmp(pieces[1], "provides"))
- {
- /* join em again for dep2str... */
- char *sp;
- for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
- if (*sp == 0)
- *sp = ' ';
- what = 0;
- if (pieces[1][0] == 'p' && strncmp(pieces[2], "namespace:", 10) == 0)
- {
- char *spe = strchr(pieces[2], '(');
- int l = strlen(pieces[2]);
- if (spe && pieces[2][l - 1] == ')')
- {
- /* special namespace provides */
- if (strcmp(spe, "(<NULL>)") != 0)
- {
- pieces[2][l - 1] = 0;
- what = testcase_str2dep(pool, spe + 1);
- pieces[2][l - 1] = ')';
- }
- what = pool_rel2id(pool, pool_strn2id(pool, pieces[2], spe - pieces[2], 1), what, REL_NAMESPACE, 1);
- }
- }
- if (!what)
- what = testcase_str2dep(pool, pieces[2]);
- if (pieces[1][0] == 'n')
- job |= SOLVER_SOLVABLE_NAME;
- else
- job |= SOLVER_SOLVABLE_PROVIDES;
- }
- else if (!strcmp(pieces[1], "oneof"))
- {
- Queue q;
- job |= SOLVER_SOLVABLE_ONE_OF;
- queue_init(&q);
- if (npieces > 3 && strcmp(pieces[2], "nothing") != 0)
- {
- for (i = 2; i < npieces; i++)
- {
- Id p = testcase_str2solvid(pool, pieces[i]);
- if (!p)
- {
- pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[i]);
- queue_free(&q);
- solv_free(pieces);
- return -1;
- }
- queue_push(&q, p);
- }
- }
- what = pool_queuetowhatprovides(pool, &q);
- queue_free(&q);
- }
- else if (!strcmp(pieces[1], "repo"))
- {
- Repo *repo;
- if (npieces != 3)
- {
- pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
- solv_free(pieces);
- return -1;
- }
- repo = testcase_str2repo(pool, pieces[2]);
- if (!repo)
- {
- pool_debug(pool, SOLV_ERROR, "str2job: unknown repo '%s'\n", pieces[2]);
- solv_free(pieces);
- return -1;
- }
- job |= SOLVER_SOLVABLE_REPO;
- what = repo->repoid;
- }
- else if (!strcmp(pieces[1], "all"))
- {
- if (npieces != 3 && strcmp(pieces[2], "packages") != 0)
- {
- pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
- solv_free(pieces);
- return -1;
- }
- job |= SOLVER_SOLVABLE_ALL;
- what = 0;
- }
- else
- {
- pool_debug(pool, SOLV_ERROR, "str2job: unknown selection in '%s'\n", str);
- solv_free(pieces);
- return -1;
- }
- *whatp = what;
- solv_free(pieces);
- return job;
-}
-
-int
-addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue)
-{
- Id job;
- int i, r;
- int selflags;
- Queue sel;
-
- for (i = 0; job2str[i].str; i++)
- if (!strcmp(pieces[0], job2str[i].str))
- break;
- if (!job2str[i].str)
- {
- pool_debug(pool, SOLV_ERROR, "selstr2job: unknown job '%s'\n", pieces[0]);
- return -1;
- }
- job = job2str[i].job;
- if (npieces > 3)
- {
- char *flags = pieces[npieces - 1];
- if (*flags == '[' && flags[strlen(flags) - 1] == ']')
- {
- npieces--;
- flags++;
- flags[strlen(flags) - 1] = 0;
- job |= str2jobflags(pool, flags);
- }
- }
- if (npieces < 4)
- {
- pool_debug(pool, SOLV_ERROR, "selstr2job: no selection flags\n");
- return -1;
- }
- selflags = str2selflags(pool, pieces[3]);
- queue_init(&sel);
- r = selection_make(pool, &sel, pieces[2], selflags);
- for (i = 0; i < sel.count; i += 2)
- queue_push2(jobqueue, job | sel.elements[i], sel.elements[i + 1]);
- queue_free(&sel);
- return r;
-}
-
-static void
-writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off)
-{
- Pool *pool = repo->pool;
- Id id, *dp;
- int tagwritten = 0;
- const char *idstr;
-
- if (!off)
- return;
- dp = repo->idarraydata + off;
- while ((id = *dp++) != 0)
- {
- if (key == SOLVABLE_REQUIRES && id == SOLVABLE_PREREQMARKER)
- {
- if (tagwritten)
- fprintf(fp, "-%s\n", tag);
- tagwritten = 0;
- tag = "Prq:";
- continue;
- }
- if (key == SOLVABLE_PROVIDES && id == SOLVABLE_FILEMARKER)
- continue;
- idstr = testcase_dep2str(pool, id);
- if (!tagwritten)
- {
- fprintf(fp, "+%s\n", tag);
- tagwritten = 1;
- }
- fprintf(fp, "%s\n", idstr);
- }
- if (tagwritten)
- fprintf(fp, "-%s\n", tag);
-}
-
-static void
-writefilelist(Repo *repo, FILE *fp, const char *tag, Solvable *s)
-{
- Pool *pool = repo->pool;
- int tagwritten = 0;
- Dataiterator di;
-
- dataiterator_init(&di, pool, repo, s - pool->solvables, SOLVABLE_FILELIST, 0, 0);
- while (dataiterator_step(&di))
- {
- const char *s = repodata_dir2str(di.data, di.kv.id, di.kv.str);
- if (!tagwritten)
- {
- fprintf(fp, "+%s\n", tag);
- tagwritten = 1;
- }
- fprintf(fp, "%s\n", s);
- }
- if (tagwritten)
- fprintf(fp, "-%s\n", tag);
- dataiterator_free(&di);
-}
-
-int
-testcase_write_testtags(Repo *repo, FILE *fp)
-{
- Pool *pool = repo->pool;
- Solvable *s;
- Id p;
- const char *name;
- const char *evr;
- const char *arch;
- const char *release;
- const char *tmp;
- unsigned int ti;
-
- fprintf(fp, "=Ver: 3.0\n");
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- name = pool_id2str(pool, s->name);
- evr = pool_id2str(pool, s->evr);
- arch = pool_id2str(pool, s->arch);
- release = strrchr(evr, '-');
- if (!release)
- release = evr + strlen(evr);
- fprintf(fp, "=Pkg: %s %.*s %s %s\n", name, (int)(release - evr), evr, *release && release[1] ? release + 1 : "-", arch);
- tmp = solvable_lookup_str(s, SOLVABLE_SUMMARY);
- if (tmp)
- fprintf(fp, "=Sum: %s\n", tmp);
- writedeps(repo, fp, "Req:", SOLVABLE_REQUIRES, s, s->requires);
- writedeps(repo, fp, "Prv:", SOLVABLE_PROVIDES, s, s->provides);
- writedeps(repo, fp, "Obs:", SOLVABLE_OBSOLETES, s, s->obsoletes);
- writedeps(repo, fp, "Con:", SOLVABLE_CONFLICTS, s, s->conflicts);
- writedeps(repo, fp, "Rec:", SOLVABLE_RECOMMENDS, s, s->recommends);
- writedeps(repo, fp, "Sup:", SOLVABLE_SUPPLEMENTS, s, s->supplements);
- writedeps(repo, fp, "Sug:", SOLVABLE_SUGGESTS, s, s->suggests);
- writedeps(repo, fp, "Enh:", SOLVABLE_ENHANCES, s, s->enhances);
- if (s->vendor)
- fprintf(fp, "=Vnd: %s\n", pool_id2str(pool, s->vendor));
- ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
- if (ti)
- fprintf(fp, "=Tim: %u\n", ti);
- writefilelist(repo, fp, "Fls:", s);
- }
- return 0;
-}
-
-static inline Offset
-adddep(Repo *repo, Offset olddeps, char *str, Id marker)
-{
- Id id = *str == '/' ? pool_str2id(repo->pool, str, 1) : testcase_str2dep(repo->pool, str);
- return repo_addid_dep(repo, olddeps, id, marker);
-}
-
-static void
-finish_v2_solvable(Pool *pool, Repodata *data, Solvable *s, char *filelist, int nfilelist)
-{
- if (nfilelist)
- {
- int l;
- Id did;
- for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
- {
- char *p = strrchr(filelist + l, '/');
- if (!p)
- continue;
- *p++ = 0;
- did = repodata_str2dir(data, filelist + l, 1);
- p[-1] = '/';
- if (!did)
- did = repodata_str2dir(data, "/", 1);
- repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
- }
- }
- s->supplements = repo_fix_supplements(s->repo, s->provides, s->supplements, 0);
- s->conflicts = repo_fix_conflicts(s->repo, s->conflicts);
-}
-
-/* stripped down version of susetags parser used for testcases */
-int
-testcase_add_testtags(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- char *line, *linep;
- int aline;
- int tag;
- Repodata *data;
- Solvable *s;
- char *sp[5];
- unsigned int t;
- int intag;
- char *filelist = 0;
- int afilelist = 0;
- int nfilelist = 0;
- int tagsversion = 0;
- int addselfprovides = 1; /* for compat reasons */
-
- data = repo_add_repodata(repo, flags);
- s = 0;
- intag = 0;
-
- aline = 1024;
- line = solv_malloc(aline);
- linep = line;
- for (;;)
- {
- if (linep - line + 16 > aline)
- {
- aline = linep - line;
- line = solv_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[-1] = 0;
- linep = line + intag;
- if (intag)
- {
- if (line[intag] == '-' && !strncmp(line + 1, line + intag + 1, intag - 2))
- {
- intag = 0;
- linep = line;
- continue;
- }
- }
- else if (line[0] == '+' && line[1] && line[1] != ':')
- {
- char *tagend = strchr(line, ':');
- if (!tagend)
- continue;
- line[0] = '=';
- tagend[1] = ' ';
- intag = tagend + 2 - line;
- linep = line + intag;
- continue;
- }
- if (*line != '=' || !line[1] || !line[2] || !line[3] || line[4] != ':')
- continue;
- tag = line[1] << 16 | line[2] << 8 | line[3];
- switch(tag)
- {
- case 'V' << 16 | 'e' << 8 | 'r':
- tagsversion = atoi(line + 6);
- addselfprovides = tagsversion < 3 || strstr(line + 6, "addselfprovides") != 0;
- break;
- case 'P' << 16 | 'k' << 8 | 'g':
- if (s)
- {
- if (tagsversion == 2)
- finish_v2_solvable(pool, data, s, filelist, nfilelist);
- if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- }
- nfilelist = 0;
- if (split(line + 5, sp, 5) != 4)
- break;
- s = pool_id2solvable(pool, repo_add_solvable(repo));
- s->name = pool_str2id(pool, sp[0], 1);
- /* join back version and release */
- if (sp[2] && !(sp[2][0] == '-' && !sp[2][1]))
- sp[2][-1] = '-';
- s->evr = makeevr(pool, sp[1]);
- s->arch = pool_str2id(pool, sp[3], 1);
- break;
- case 'S' << 16 | 'u' << 8 | 'm':
- repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 6);
- break;
- case 'V' << 16 | 'n' << 8 | 'd':
- s->vendor = pool_str2id(pool, line + 6, 1);
- break;
- case 'T' << 16 | 'i' << 8 | 'm':
- t = atoi(line + 6);
- if (t)
- repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
- break;
- case 'R' << 16 | 'e' << 8 | 'q':
- s->requires = adddep(repo, s->requires, line + 6, -SOLVABLE_PREREQMARKER);
- break;
- case 'P' << 16 | 'r' << 8 | 'q':
- s->requires = adddep(repo, s->requires, line + 6, SOLVABLE_PREREQMARKER);
- break;
- case 'P' << 16 | 'r' << 8 | 'v':
- /* version 2 had the file list at the end of the provides */
- if (tagsversion == 2)
- {
- if (line[6] == '/')
- {
- int l = strlen(line + 6) + 1;
- if (nfilelist + l > afilelist)
- {
- afilelist = nfilelist + l + 512;
- filelist = solv_realloc(filelist, afilelist);
- }
- memcpy(filelist + nfilelist, line + 6, l);
- nfilelist += l;
- break;
- }
- if (nfilelist)
- {
- int l;
- for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
- s->provides = repo_addid_dep(repo, s->provides, pool_str2id(pool, filelist + l, 1), 0);
- nfilelist = 0;
- }
- }
- s->provides = adddep(repo, s->provides, line + 6, 0);
- break;
- case 'F' << 16 | 'l' << 8 | 's':
- {
- char *p = strrchr(line + 6, '/');
- Id did;
- if (!p)
- break;
- *p++ = 0;
- did = repodata_str2dir(data, line + 6, 1);
- if (!did)
- did = repodata_str2dir(data, "/", 1);
- repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
- break;
- }
- case 'O' << 16 | 'b' << 8 | 's':
- s->obsoletes = adddep(repo, s->obsoletes, line + 6, 0);
- break;
- case 'C' << 16 | 'o' << 8 | 'n':
- s->conflicts = adddep(repo, s->conflicts, line + 6, 0);
- break;
- case 'R' << 16 | 'e' << 8 | 'c':
- s->recommends = adddep(repo, s->recommends, line + 6, 0);
- break;
- case 'S' << 16 | 'u' << 8 | 'p':
- s->supplements = adddep(repo, s->supplements, line + 6, 0);
- break;
- case 'S' << 16 | 'u' << 8 | 'g':
- s->suggests = adddep(repo, s->suggests, line + 6, 0);
- break;
- case 'E' << 16 | 'n' << 8 | 'h':
- s->enhances = adddep(repo, s->enhances, line + 6, 0);
- break;
- default:
- break;
- }
- }
- if (s)
- {
- if (tagsversion == 2)
- finish_v2_solvable(pool, data, s, filelist, nfilelist);
- if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
- s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- }
- solv_free(line);
- solv_free(filelist);
- repodata_free_dircache(data);
- if (!(flags & REPO_NO_INTERNALIZE))
- repodata_internalize(data);
- return 0;
-}
-
-const char *
-testcase_getpoolflags(Pool *pool)
-{
- const char *str = 0;
- int i, v;
- for (i = 0; poolflags2str[i].str; i++)
- {
- v = pool_get_flag(pool, poolflags2str[i].flag);
- if (v == poolflags2str[i].def)
- continue;
- str = pool_tmpappend(pool, str, v ? " " : " !", poolflags2str[i].str);
- }
- return str ? str + 1 : "";
-}
-
-int
-testcase_setpoolflags(Pool *pool, const char *str)
-{
- const char *p = str, *s;
- int i, v;
- for (;;)
- {
- while (*p == ' ' || *p == '\t' || *p == ',')
- p++;
- v = 1;
- if (*p == '!')
- {
- p++;
- v = 0;
- }
- if (!*p)
- break;
- s = p;
- while (*p && *p != ' ' && *p != '\t' && *p != ',')
- p++;
- for (i = 0; poolflags2str[i].str; i++)
- if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0)
- break;
- if (!poolflags2str[i].str)
- {
- pool_debug(pool, SOLV_ERROR, "setpoolflags: unknown flag '%.*s'\n", (int)(p - s), s);
- return 0;
- }
- pool_set_flag(pool, poolflags2str[i].flag, v);
- }
- return 1;
-}
-
-void
-testcase_resetpoolflags(Pool *pool)
-{
- int i;
- for (i = 0; poolflags2str[i].str; i++)
- pool_set_flag(pool, poolflags2str[i].flag, poolflags2str[i].def);
-}
-
-const char *
-testcase_getsolverflags(Solver *solv)
-{
- Pool *pool = solv->pool;
- const char *str = 0;
- int i, v;
- for (i = 0; solverflags2str[i].str; i++)
- {
- v = solver_get_flag(solv, solverflags2str[i].flag);
- if (v == solverflags2str[i].def)
- continue;
- str = pool_tmpappend(pool, str, v ? " " : " !", solverflags2str[i].str);
- }
- return str ? str + 1 : "";
-}
-
-int
-testcase_setsolverflags(Solver *solv, const char *str)
-{
- const char *p = str, *s;
- int i, v;
- for (;;)
- {
- while (*p == ' ' || *p == '\t' || *p == ',')
- p++;
- v = 1;
- if (*p == '!')
- {
- p++;
- v = 0;
- }
- if (!*p)
- break;
- s = p;
- while (*p && *p != ' ' && *p != '\t' && *p != ',')
- p++;
- for (i = 0; solverflags2str[i].str; i++)
- if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0)
- break;
- if (!solverflags2str[i].str)
- {
- pool_debug(solv->pool, SOLV_ERROR, "setsolverflags: unknown flag '%.*s'\n", (int)(p - s), s);
- return 0;
- }
- solver_set_flag(solv, solverflags2str[i].flag, v);
- }
- return 1;
-}
-
-void
-testcase_resetsolverflags(Solver *solv)
-{
- int i;
- for (i = 0; solverflags2str[i].str; i++)
- solver_set_flag(solv, solverflags2str[i].flag, solverflags2str[i].def);
-}
-
-static const char *
-testcase_ruleid(Solver *solv, Id rid)
-{
- Strqueue sq;
- Queue q;
- int i;
- Chksum *chk;
- const unsigned char *md5;
- int md5l;
- const char *s;
-
- queue_init(&q);
- strqueue_init(&sq);
- solver_ruleliterals(solv, rid, &q);
- for (i = 0; i < q.count; i++)
- {
- Id p = q.elements[i];
- s = testcase_solvid2str(solv->pool, p > 0 ? p : -p);
- if (p < 0)
- s = pool_tmpjoin(solv->pool, "!", s, 0);
- strqueue_push(&sq, s);
- }
- queue_free(&q);
- strqueue_sort_u(&sq);
- chk = solv_chksum_create(REPOKEY_TYPE_MD5);
- for (i = 0; i < sq.nstr; i++)
- solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
- md5 = solv_chksum_get(chk, &md5l);
- s = pool_bin2hex(solv->pool, md5, md5l);
- chk = solv_chksum_free(chk, 0);
- strqueue_free(&sq);
- return s;
-}
-
-static const char *
-testcase_problemid(Solver *solv, Id problem)
-{
- Strqueue sq;
- Queue q;
- Chksum *chk;
- const unsigned char *md5;
- int i, md5l;
- const char *s;
-
- /* we build a hash of all rules that define the problem */
- queue_init(&q);
- strqueue_init(&sq);
- solver_findallproblemrules(solv, problem, &q);
- for (i = 0; i < q.count; i++)
- strqueue_push(&sq, testcase_ruleid(solv, q.elements[i]));
- queue_free(&q);
- strqueue_sort_u(&sq);
- chk = solv_chksum_create(REPOKEY_TYPE_MD5);
- for (i = 0; i < sq.nstr; i++)
- solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
- md5 = solv_chksum_get(chk, &md5l);
- s = pool_bin2hex(solv->pool, md5, 4);
- chk = solv_chksum_free(chk, 0);
- strqueue_free(&sq);
- return s;
-}
-
-static const char *
-testcase_solutionid(Solver *solv, Id problem, Id solution)
-{
- Id intid;
- Chksum *chk;
- const unsigned char *md5;
- int md5l;
- const char *s;
-
- intid = solver_solutionelement_internalid(solv, problem, solution);
- /* internal stuff! handle with care! */
- if (intid < 0)
- {
- /* it's a job */
- s = testcase_job2str(solv->pool, solv->job.elements[-intid - 1], solv->job.elements[-intid]);
- }
- else
- {
- /* it's a rule */
- s = testcase_ruleid(solv, intid);
- }
- chk = solv_chksum_create(REPOKEY_TYPE_MD5);
- solv_chksum_add(chk, s, strlen(s) + 1);
- md5 = solv_chksum_get(chk, &md5l);
- s = pool_bin2hex(solv->pool, md5, 4);
- chk = solv_chksum_free(chk, 0);
- return s;
-}
-
-static const char *
-testcase_alternativeid(Solver *solv, int type, Id id, Id from)
-{
- const char *s;
- Pool *pool = solv->pool;
- Chksum *chk;
- const unsigned char *md5;
- int md5l;
- chk = solv_chksum_create(REPOKEY_TYPE_MD5);
- if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
- {
- s = testcase_solvid2str(pool, from);
- solv_chksum_add(chk, s, strlen(s) + 1);
- s = testcase_dep2str(pool, id);
- solv_chksum_add(chk, s, strlen(s) + 1);
- }
- else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
- {
- s = testcase_ruleid(solv, id);
- solv_chksum_add(chk, s, strlen(s) + 1);
- }
- md5 = solv_chksum_get(chk, &md5l);
- s = pool_bin2hex(pool, md5, 4);
- chk = solv_chksum_free(chk, 0);
- return s;
-}
-
-static struct class2str {
- Id class;
- const char *str;
-} class2str[] = {
- { SOLVER_TRANSACTION_ERASE, "erase" },
- { SOLVER_TRANSACTION_INSTALL, "install" },
- { SOLVER_TRANSACTION_REINSTALLED, "reinstall" },
- { SOLVER_TRANSACTION_DOWNGRADED, "downgrade" },
- { SOLVER_TRANSACTION_CHANGED, "change" },
- { SOLVER_TRANSACTION_UPGRADED, "upgrade" },
- { SOLVER_TRANSACTION_OBSOLETED, "obsolete" },
- { SOLVER_TRANSACTION_MULTIINSTALL, "multiinstall" },
- { SOLVER_TRANSACTION_MULTIREINSTALL, "multireinstall" },
- { 0, 0 }
-};
-
-static int
-dump_genid(Pool *pool, Strqueue *sq, Id id, int cnt)
-{
- struct oplist *op;
- char cntbuf[20];
- const char *s;
-
- if (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- for (op = oplist; op->flags; op++)
- if (rd->flags == op->flags)
- break;
- cnt = dump_genid(pool, sq, rd->name, cnt);
- cnt = dump_genid(pool, sq, rd->evr, cnt);
- sprintf(cntbuf, "genid %2d: genid ", cnt++);
- s = pool_tmpjoin(pool, cntbuf, "op ", op->flags ? op->opname : "unknown");
- }
- else
- {
- sprintf(cntbuf, "genid %2d: genid ", cnt++);
- s = pool_tmpjoin(pool, cntbuf, id ? "lit " : "null", id ? pool_id2str(pool, id) : 0);
- }
- strqueue_push(sq, s);
- return cnt;
-}
-
-char *
-testcase_solverresult(Solver *solv, int resultflags)
-{
- Pool *pool = solv->pool;
- int i, j;
- Id p, op;
- const char *s;
- char *result;
- Strqueue sq;
-
- strqueue_init(&sq);
- if ((resultflags & TESTCASE_RESULT_TRANSACTION) != 0)
- {
- Transaction *trans = solver_create_transaction(solv);
- Queue q;
-
- queue_init(&q);
- for (i = 0; class2str[i].str; i++)
- {
- queue_empty(&q);
- transaction_classify_pkgs(trans, SOLVER_TRANSACTION_KEEP_PSEUDO, class2str[i].class, 0, 0, &q);
- for (j = 0; j < q.count; j++)
- {
- p = q.elements[j];
- op = 0;
- if (pool->installed && pool->solvables[p].repo == pool->installed)
- op = transaction_obs_pkg(trans, p);
- s = pool_tmpjoin(pool, class2str[i].str, " ", testcase_solvid2str(pool, p));
- if (op)
- s = pool_tmpjoin(pool, s, " ", testcase_solvid2str(pool, op));
- strqueue_push(&sq, s);
- }
- }
- queue_free(&q);
- transaction_free(trans);
- }
- if ((resultflags & TESTCASE_RESULT_PROBLEMS) != 0)
- {
- char *probprefix, *solprefix;
- int problem, solution, element;
- int pcnt, scnt;
-
- pcnt = solver_problem_count(solv);
- for (problem = 1; problem <= pcnt; problem++)
- {
- Id rid, from, to, dep;
- SolverRuleinfo rinfo;
- rid = solver_findproblemrule(solv, problem);
- s = testcase_problemid(solv, problem);
- probprefix = solv_dupjoin("problem ", s, 0);
- rinfo = solver_ruleinfo(solv, rid, &from, &to, &dep);
- s = pool_tmpjoin(pool, probprefix, " info ", solver_problemruleinfo2str(solv, rinfo, from, to, dep));
- strqueue_push(&sq, s);
- scnt = solver_solution_count(solv, problem);
- for (solution = 1; solution <= scnt; solution++)
- {
- s = testcase_solutionid(solv, problem, solution);
- solprefix = solv_dupjoin(probprefix, " solution ", s);
- element = 0;
- while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &op)) != 0)
- {
- if (p == SOLVER_SOLUTION_JOB)
- s = pool_tmpjoin(pool, solprefix, " deljob ", testcase_job2str(pool, solv->job.elements[op - 1], solv->job.elements[op]));
- else if (p > 0 && op == 0)
- s = pool_tmpjoin(pool, solprefix, " erase ", testcase_solvid2str(pool, p));
- else if (p > 0 && op > 0)
- {
- s = pool_tmpjoin(pool, solprefix, " replace ", testcase_solvid2str(pool, p));
- s = pool_tmpappend(pool, s, " ", testcase_solvid2str(pool, op));
- }
- else if (p < 0 && op > 0)
- s = pool_tmpjoin(pool, solprefix, " allow ", testcase_solvid2str(pool, op));
- else
- s = pool_tmpjoin(pool, solprefix, " unknown", 0);
- strqueue_push(&sq, s);
- }
- solv_free(solprefix);
- }
- solv_free(probprefix);
- }
- }
-
- if ((resultflags & TESTCASE_RESULT_ORPHANED) != 0)
- {
- Queue q;
-
- queue_init(&q);
- solver_get_orphaned(solv, &q);
- for (i = 0; i < q.count; i++)
- {
- s = pool_tmpjoin(pool, "orphaned ", testcase_solvid2str(pool, q.elements[i]), 0);
- strqueue_push(&sq, s);
- }
- queue_free(&q);
- }
-
- if ((resultflags & TESTCASE_RESULT_RECOMMENDED) != 0)
- {
- Queue qr, qs;
-
- queue_init(&qr);
- queue_init(&qs);
- solver_get_recommendations(solv, &qr, &qs, 0);
- for (i = 0; i < qr.count; i++)
- {
- s = pool_tmpjoin(pool, "recommended ", testcase_solvid2str(pool, qr.elements[i]), 0);
- strqueue_push(&sq, s);
- }
- for (i = 0; i < qs.count; i++)
- {
- s = pool_tmpjoin(pool, "suggested ", testcase_solvid2str(pool, qs.elements[i]), 0);
- strqueue_push(&sq, s);
- }
- queue_free(&qr);
- queue_free(&qs);
- }
-
- if ((resultflags & TESTCASE_RESULT_UNNEEDED) != 0)
- {
- Queue q, qf;
-
- queue_init(&q);
- queue_init(&qf);
- solver_get_unneeded(solv, &q, 0);
- solver_get_unneeded(solv, &qf, 1);
- for (i = j = 0; i < q.count; i++)
- {
- /* we rely on qf containing a subset of q in the same order */
- if (j < qf.count && q.elements[i] == qf.elements[j])
- {
- s = pool_tmpjoin(pool, "unneeded_filtered ", testcase_solvid2str(pool, q.elements[i]), 0);
- j++;
- }
- else
- s = pool_tmpjoin(pool, "unneeded ", testcase_solvid2str(pool, q.elements[i]), 0);
- strqueue_push(&sq, s);
- }
- queue_free(&q);
- queue_free(&qf);
- }
- if ((resultflags & TESTCASE_RESULT_ALTERNATIVES) != 0)
- {
- char *altprefix;
- Queue q, rq;
- int cnt;
- Id alternative;
- queue_init(&q);
- queue_init(&rq);
- cnt = solver_alternatives_count(solv);
- for (alternative = 1; alternative <= cnt; alternative++)
- {
- Id id, from, chosen;
- char num[20];
- int type = solver_get_alternative(solv, alternative, &id, &from, &chosen, &q, 0);
- altprefix = solv_dupjoin("alternative ", testcase_alternativeid(solv, type, id, from), " ");
- strcpy(num, " 0 ");
- if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
- {
- char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, from));
- s = pool_tmpappend(pool, s, " recommends ", testcase_dep2str(pool, id));
- strqueue_push(&sq, s);
- }
- else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
- {
- /* map choice rules back to pkg rules */
- if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
- id = solver_rule2pkgrule(solv, id);
- solver_allruleinfos(solv, id, &rq);
- for (i = 0; i < rq.count; i += 4)
- {
- int rtype = rq.elements[i];
- if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
- {
- const char *js = testcase_job2str(pool, rq.elements[i + 2], rq.elements[i + 3]);
- char *s = pool_tmpjoin(pool, altprefix, num, " job ");
- s = pool_tmpappend(pool, s, js, 0);
- strqueue_push(&sq, s);
- }
- else if (rtype == SOLVER_RULE_PKG_REQUIRES)
- {
- char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, rq.elements[i + 1]));
- s = pool_tmpappend(pool, s, " requires ", testcase_dep2str(pool, rq.elements[i + 3]));
- strqueue_push(&sq, s);
- }
- }
- }
- for (i = 0; i < q.count; i++)
- {
- Id p = q.elements[i];
- if (i >= 9)
- num[0] = '0' + (i + 1) / 10;
- num[1] = '0' + (i + 1) % 10;
- if (-p == chosen)
- s = pool_tmpjoin(pool, altprefix, num, "+ ");
- else if (p < 0)
- s = pool_tmpjoin(pool, altprefix, num, "- ");
- else if (p >= 0)
- s = pool_tmpjoin(pool, altprefix, num, " ");
- s = pool_tmpappend(pool, s, testcase_solvid2str(pool, p < 0 ? -p : p), 0);
- strqueue_push(&sq, s);
- }
- solv_free(altprefix);
- }
- queue_free(&q);
- queue_free(&rq);
- }
- if ((resultflags & TESTCASE_RESULT_RULES) != 0)
- {
- /* dump all rules */
- Id rid;
- SolverRuleinfo rclass;
- Queue q;
- int i;
-
- queue_init(&q);
- for (rid = 1; (rclass = solver_ruleclass(solv, rid)) != SOLVER_RULE_UNKNOWN; rid++)
- {
- char *prefix;
- switch (rclass)
- {
- case SOLVER_RULE_PKG:
- prefix = "pkg ";
- break;
- case SOLVER_RULE_UPDATE:
- prefix = "update ";
- break;
- case SOLVER_RULE_FEATURE:
- prefix = "feature ";
- break;
- case SOLVER_RULE_JOB:
- prefix = "job ";
- break;
- case SOLVER_RULE_DISTUPGRADE:
- prefix = "distupgrade ";
- break;
- case SOLVER_RULE_INFARCH:
- prefix = "infarch ";
- break;
- case SOLVER_RULE_CHOICE:
- prefix = "choice ";
- break;
- case SOLVER_RULE_LEARNT:
- prefix = "learnt ";
- break;
- case SOLVER_RULE_BEST:
- prefix = "best ";
- break;
- case SOLVER_RULE_YUMOBS:
- prefix = "yumobs ";
- break;
- default:
- prefix = "unknown ";
- break;
- }
- prefix = solv_dupjoin("rule ", prefix, testcase_ruleid(solv, rid));
- solver_ruleliterals(solv, rid, &q);
- if (rclass == SOLVER_RULE_FEATURE && q.count == 1 && q.elements[0] == -SYSTEMSOLVABLE)
- continue;
- for (i = 0; i < q.count; i++)
- {
- Id p = q.elements[i];
- const char *s;
- if (p < 0)
- s = pool_tmpjoin(pool, prefix, " -", testcase_solvid2str(pool, -p));
- else
- s = pool_tmpjoin(pool, prefix, " ", testcase_solvid2str(pool, p));
- strqueue_push(&sq, s);
- }
- solv_free(prefix);
- }
- queue_free(&q);
- }
- if ((resultflags & TESTCASE_RESULT_GENID) != 0)
- {
- for (i = 0 ; i < solv->job.count; i += 2)
- {
- Id id, id2;
- if (solv->job.elements[i] != (SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES))
- continue;
- id = solv->job.elements[i + 1];
- s = testcase_dep2str(pool, id);
- strqueue_push(&sq, pool_tmpjoin(pool, "genid dep ", s, 0));
- if ((id2 = testcase_str2dep(pool, s)) != id)
- {
- s = pool_tmpjoin(pool, "genid roundtrip error: ", testcase_dep2str(pool, id2), 0);
- strqueue_push(&sq, s);
- }
- dump_genid(pool, &sq, id, 1);
- }
- }
- strqueue_sort(&sq);
- result = strqueue_join(&sq);
- strqueue_free(&sq);
- return result;
-}
-
-
-int
-testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
-{
- Pool *pool = solv->pool;
- Repo *repo;
- int i;
- Id arch, repoid;
- Id lowscore;
- FILE *fp;
- Strqueue sq;
- char *cmd, *out;
- const char *s;
-
- if (!testcasename)
- testcasename = "testcase.t";
- if (!resultname)
- resultname = "solver.result";
-
- if (mkdir(dir, 0777) && errno != EEXIST)
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not create directory '%s'\n", dir);
- return 0;
- }
- strqueue_init(&sq);
- FOR_REPOS(repoid, repo)
- {
- const char *name = testcase_repoid2str(pool, repoid);
- char priobuf[50];
- if (repo->subpriority)
- sprintf(priobuf, "%d.%d", repo->priority, repo->subpriority);
- else
- sprintf(priobuf, "%d", repo->priority);
- out = pool_tmpjoin(pool, name, ".repo", ".gz");
- cmd = pool_tmpjoin(pool, "repo ", name, " ");
- cmd = pool_tmpappend(pool, cmd, priobuf, " ");
- cmd = pool_tmpappend(pool, cmd, "testtags ", out);
- strqueue_push(&sq, cmd);
- out = pool_tmpjoin(pool, dir, "/", out);
- if (!(fp = solv_xfopen(out, "w")))
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
- strqueue_free(&sq);
- return 0;
- }
- testcase_write_testtags(repo, fp);
- if (fclose(fp))
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
- strqueue_free(&sq);
- return 0;
- }
- }
- /* hmm, this is not optimal... we currently search for the lowest score */
- lowscore = 0;
- arch = pool->solvables[SYSTEMSOLVABLE].arch;
- for (i = 0; i < pool->lastarch; i++)
- {
- if (pool->id2arch[i] == 1 && !lowscore)
- arch = i;
- if (pool->id2arch[i] > 0x10000 && (!lowscore || pool->id2arch[i] < lowscore))
- {
- arch = i;
- lowscore = pool->id2arch[i];
- }
- }
- cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "unset", 0);
- for (i = 0; disttype2str[i].str != 0; i++)
- if (pool->disttype == disttype2str[i].type)
- break;
- pool_tmpappend(pool, cmd, " ", disttype2str[i].str ? disttype2str[i].str : "unknown");
- if (pool->installed)
- cmd = pool_tmpappend(pool, cmd, " ", testcase_repoid2str(pool, pool->installed->repoid));
- strqueue_push(&sq, cmd);
- s = testcase_getpoolflags(solv->pool);
- if (*s)
- {
- cmd = pool_tmpjoin(pool, "poolflags ", s, 0);
- strqueue_push(&sq, cmd);
- }
-
- if (pool->vendorclasses)
- {
- cmd = 0;
- for (i = 0; pool->vendorclasses[i]; i++)
- {
- cmd = pool_tmpappend(pool, cmd ? cmd : "vendorclass", " ", pool->vendorclasses[i]);
- if (!pool->vendorclasses[i + 1])
- {
- strqueue_push(&sq, cmd);
- cmd = 0;
- i++;
- }
- }
- }
-
- /* dump disabled packages (must come before the namespace/job lines) */
- if (pool->considered)
- {
- Id p;
- FOR_POOL_SOLVABLES(p)
- if (!MAPTST(pool->considered, p))
- {
- cmd = pool_tmpjoin(pool, "disable pkg ", testcase_solvid2str(pool, p), 0);
- strqueue_push(&sq, cmd);
- }
- }
-
- s = testcase_getsolverflags(solv);
- if (*s)
- {
- cmd = pool_tmpjoin(pool, "solverflags ", s, 0);
- strqueue_push(&sq, cmd);
- }
-
- /* now dump all the ns callback values we know */
- if (pool->nscallback)
- {
- Id rid;
- int d;
- for (rid = 1; rid < pool->nrels; rid++)
- {
- Reldep *rd = pool->rels + rid;
- if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS)
- continue;
- /* evaluate all namespace ids, skip empty results */
- d = pool_whatprovides(pool, MAKERELDEP(rid));
- if (!d || !pool->whatprovidesdata[d])
- continue;
- cmd = pool_tmpjoin(pool, "namespace ", pool_id2str(pool, rd->name), "(");
- cmd = pool_tmpappend(pool, cmd, pool_id2str(pool, rd->evr), ")");
- for (; pool->whatprovidesdata[d]; d++)
- cmd = pool_tmpappend(pool, cmd, " ", testcase_solvid2str(pool, pool->whatprovidesdata[d]));
- strqueue_push(&sq, cmd);
- }
- }
-
- for (i = 0; i < solv->job.count; i += 2)
- {
- cmd = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
- cmd = pool_tmpjoin(pool, "job ", cmd, 0);
- strqueue_push(&sq, cmd);
- }
-
- if (resultflags)
- {
- char *result;
- cmd = 0;
- for (i = 0; resultflags2str[i].str; i++)
- if ((resultflags & resultflags2str[i].flag) != 0)
- cmd = pool_tmpappend(pool, cmd, cmd ? "," : 0, resultflags2str[i].str);
- cmd = pool_tmpjoin(pool, "result ", cmd ? cmd : "?", 0);
- cmd = pool_tmpappend(pool, cmd, " ", resultname);
- strqueue_push(&sq, cmd);
- result = testcase_solverresult(solv, resultflags);
- if (!strcmp(resultname, "<inline>"))
- {
- int i;
- Strqueue rsq;
- strqueue_init(&rsq);
- strqueue_split(&rsq, result);
- for (i = 0; i < rsq.nstr; i++)
- {
- cmd = pool_tmpjoin(pool, "#>", rsq.str[i], 0);
- strqueue_push(&sq, cmd);
- }
- strqueue_free(&rsq);
- }
- else
- {
- out = pool_tmpjoin(pool, dir, "/", resultname);
- if (!(fp = fopen(out, "w")))
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
- solv_free(result);
- strqueue_free(&sq);
- return 0;
- }
- if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
- solv_free(result);
- strqueue_free(&sq);
- fclose(fp);
- return 0;
- }
- if (fclose(fp))
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
- strqueue_free(&sq);
- return 0;
- }
- }
- solv_free(result);
- }
-
- cmd = strqueue_join(&sq);
- out = pool_tmpjoin(pool, dir, "/", testcasename);
- if (!(fp = fopen(out, "w")))
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
- strqueue_free(&sq);
- return 0;
- }
- if (*cmd && fwrite(cmd, strlen(cmd), 1, fp) != 1)
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
- strqueue_free(&sq);
- fclose(fp);
- return 0;
- }
- if (fclose(fp))
- {
- pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
- strqueue_free(&sq);
- return 0;
- }
- solv_free(cmd);
- strqueue_free(&sq);
- return 1;
-}
-
-static char *
-read_inline_file(FILE *fp, char **bufp, char **bufpp, int *buflp)
-{
- char *result = solv_malloc(1024);
- char *rp = result;
- int resultl = 1024;
-
- for (;;)
- {
- size_t rl;
- if (rp - result + 256 >= resultl)
- {
- resultl = rp - result;
- result = solv_realloc(result, resultl + 1024);
- rp = result + resultl;
- resultl += 1024;
- }
- if (!fgets(rp, resultl - (rp - result), fp))
- *rp = 0;
- rl = strlen(rp);
- if (rl && (rp == result || rp[-1] == '\n'))
- {
- if (rl > 1 && rp[0] == '#' && rp[1] == '>')
- {
- memmove(rp, rp + 2, rl - 2);
- rl -= 2;
- }
- else
- {
- while (rl + 16 > *buflp)
- {
- *bufp = solv_realloc(*bufp, *buflp + 512);
- *buflp += 512;
- }
- memmove(*bufp, rp, rl);
- if ((*bufp)[rl - 1] == '\n')
- {
- ungetc('\n', fp);
- rl--;
- }
- (*bufp)[rl] = 0;
- (*bufpp) = *bufp + rl;
- rl = 0;
- }
- }
- if (rl <= 0)
- {
- *rp = 0;
- break;
- }
- rp += rl;
- }
- return result;
-}
-
-static char *
-read_file(FILE *fp)
-{
- char *result = solv_malloc(1024);
- char *rp = result;
- int resultl = 1024;
-
- for (;;)
- {
- size_t rl;
- if (rp - result + 256 >= resultl)
- {
- resultl = rp - result;
- result = solv_realloc(result, resultl + 1024);
- rp = result + resultl;
- resultl += 1024;
- }
- rl = fread(rp, 1, resultl - (rp - result), fp);
- if (rl <= 0)
- {
- *rp = 0;
- break;
- }
- rp += rl;
- }
- return result;
-}
-
-static int
-str2resultflags(Pool *pool, char *s) /* modifies the string! */
-{
- int i, resultflags = 0;
- while (s)
- {
- char *se = strchr(s, ',');
- if (se)
- *se++ = 0;
- for (i = 0; resultflags2str[i].str; i++)
- if (!strcmp(s, resultflags2str[i].str))
- {
- resultflags |= resultflags2str[i].flag;
- break;
- }
- if (!resultflags2str[i].str)
- pool_debug(pool, SOLV_ERROR, "result: unknown flag '%s'\n", s);
- s = se;
- }
- return resultflags;
-}
-
-Solver *
-testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp)
-{
- Solver *solv;
- char *buf, *bufp;
- int bufl;
- char *testcasedir, *s;
- int l;
- char **pieces = 0;
- int npieces = 0;
- int prepared = 0;
- int closefp = !fp;
- int poolflagsreset = 0;
- int missing_features = 0;
- Id *genid = 0;
- int ngenid = 0;
-
- if (!fp && !(fp = fopen(testcase, "r")))
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", testcase);
- return 0;
- }
- testcasedir = solv_strdup(testcase);
- if ((s = strrchr(testcasedir, '/')) != 0)
- s[1] = 0;
- else
- *testcasedir = 0;
- bufl = 1024;
- buf = solv_malloc(bufl);
- bufp = buf;
- solv = 0;
- for (;;)
- {
- if (bufp - buf + 16 > bufl)
- {
- bufl = bufp - buf;
- buf = solv_realloc(buf, bufl + 512);
- bufp = buf + bufl;
- bufl += 512;
- }
- if (!fgets(bufp, bufl - (bufp - buf), fp))
- break;
- bufp = buf;
- l = strlen(buf);
- if (!l || buf[l - 1] != '\n')
- {
- bufp += l;
- continue;
- }
- buf[--l] = 0;
- s = buf;
- while (*s && (*s == ' ' || *s == '\t'))
- s++;
- if (!*s || *s == '#')
- continue;
- npieces = 0;
- /* split it in pieces */
- for (;;)
- {
- while (*s == ' ' || *s == '\t')
- s++;
- if (!*s)
- break;
- pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
- pieces[npieces++] = s;
- while (*s && *s != ' ' && *s != '\t')
- s++;
- if (*s)
- *s++ = 0;
- }
- pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
- pieces[npieces] = 0;
- if (!strcmp(pieces[0], "repo") && npieces >= 4)
- {
- Repo *repo = repo_create(pool, pieces[1]);
- FILE *rfp;
- int prio, subprio;
- const char *rdata;
-
- prepared = 0;
- if (!poolflagsreset)
- {
- poolflagsreset = 1;
- testcase_resetpoolflags(pool); /* hmm */
- }
- if (sscanf(pieces[2], "%d.%d", &prio, &subprio) != 2)
- {
- subprio = 0;
- prio = atoi(pieces[2]);
- }
- repo->priority = prio;
- repo->subpriority = subprio;
- if (strcmp(pieces[3], "empty") != 0)
- {
- const char *repotype = pool_tmpjoin(pool, pieces[3], 0, 0); /* gets overwritten in <inline> case */
- if (!strcmp(pieces[4], "<inline>"))
- {
- char *idata = read_inline_file(fp, &buf, &bufp, &bufl);
- rdata = "<inline>";
- rfp = solv_xfopen_buf(rdata, &idata, 0, "rf");
- }
- else
- {
- rdata = pool_tmpjoin(pool, testcasedir, pieces[4], 0);
- rfp = solv_xfopen(rdata, "r");
- }
- if (!rfp)
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
- }
- else if (!strcmp(repotype, "testtags"))
- {
- testcase_add_testtags(repo, rfp, 0);
- fclose(rfp);
- }
- else if (!strcmp(repotype, "solv"))
- {
- repo_add_solv(repo, rfp, 0);
- fclose(rfp);
- }
-#if 0
- else if (!strcmp(repotype, "helix"))
- {
- extern int repo_add_helix(Repo *repo, FILE *fp, int flags);
- repo_add_helix(repo, rfp, 0);
- fclose(rfp);
- }
-#endif
- else
- {
- fclose(rfp);
- pool_debug(pool, SOLV_ERROR, "testcase_read: unknown repo type for repo '%s'\n", repo->name);
- }
- }
- }
- else if (!strcmp(pieces[0], "system") && npieces >= 3)
- {
- int i;
-
- /* must set the disttype before the arch */
- prepared = 0;
- if (strcmp(pieces[2], "*") != 0)
- {
- char *dp = pieces[2];
- while (dp && *dp)
- {
- char *dpe = strchr(dp, ',');
- if (dpe)
- *dpe = 0;
- for (i = 0; disttype2str[i].str != 0; i++)
- if (!strcmp(disttype2str[i].str, dp))
- break;
- if (dpe)
- *dpe++ = ',';
- if (disttype2str[i].str)
- {
-#ifdef MULTI_SEMANTICS
- if (pool->disttype != disttype2str[i].type)
- pool_setdisttype(pool, disttype2str[i].type);
-#endif
- if (pool->disttype == disttype2str[i].type)
- break;
- }
- dp = dpe;
- }
- if (!(dp && *dp))
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: system: could not change disttype to '%s'\n", pieces[2]);
- missing_features = 1;
- }
- }
- if (strcmp(pieces[1], "unset") == 0)
- pool_setarch(pool, 0);
- else if (pieces[1][0] == ':')
- pool_setarchpolicy(pool, pieces[1] + 1);
- else
- pool_setarch(pool, pieces[1]);
- if (npieces > 3)
- {
- Repo *repo = testcase_str2repo(pool, pieces[3]);
- if (!repo)
- pool_debug(pool, SOLV_ERROR, "testcase_read: system: unknown repo '%s'\n", pieces[3]);
- else
- pool_set_installed(pool, repo);
- }
- }
- else if (!strcmp(pieces[0], "job") && npieces > 1)
- {
- char *sp;
- Id how, what;
- if (prepared <= 0)
- {
- pool_addfileprovides(pool);
- pool_createwhatprovides(pool);
- prepared = 1;
- }
- if (npieces >= 3 && !strcmp(pieces[2], "selection"))
- {
- addselectionjob(pool, pieces + 1, npieces - 1, job);
- continue;
- }
- /* rejoin */
- for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
- if (*sp == 0)
- *sp = ' ';
- how = testcase_str2job(pool, pieces[1], &what);
- if (how >= 0 && job)
- queue_push2(job, how, what);
- }
- else if (!strcmp(pieces[0], "vendorclass") && npieces > 1)
- {
- pool_addvendorclass(pool, (const char **)(pieces + 1));
- }
- else if (!strcmp(pieces[0], "namespace") && npieces > 1)
- {
- int i = strlen(pieces[1]);
- s = strchr(pieces[1], '(');
- if (!s && pieces[1][i - 1] != ')')
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: bad namespace '%s'\n", pieces[1]);
- }
- else
- {
- Id name, evr, id;
- Queue q;
- queue_init(&q);
- *s = 0;
- pieces[1][i - 1] = 0;
- name = pool_str2id(pool, pieces[1], 1);
- evr = pool_str2id(pool, s + 1, 1);
- *s = '(';
- pieces[1][i - 1] = ')';
- id = pool_rel2id(pool, name, evr, REL_NAMESPACE, 1);
- for (i = 2; i < npieces; i++)
- queue_push(&q, testcase_str2solvid(pool, pieces[i]));
- /* now do the callback */
- if (prepared <= 0)
- {
- pool_addfileprovides(pool);
- pool_createwhatprovides(pool);
- prepared = 1;
- }
- pool->whatprovides_rel[GETRELID(id)] = pool_queuetowhatprovides(pool, &q);
- queue_free(&q);
- }
- }
- else if (!strcmp(pieces[0], "poolflags"))
- {
- int i;
- if (!poolflagsreset)
- {
- poolflagsreset = 1;
- testcase_resetpoolflags(pool); /* hmm */
- }
- for (i = 1; i < npieces; i++)
- testcase_setpoolflags(pool, pieces[i]);
- }
- else if (!strcmp(pieces[0], "solverflags") && npieces > 1)
- {
- int i;
- if (!solv)
- {
- solv = solver_create(pool);
- testcase_resetsolverflags(solv);
- }
- for (i = 1; i < npieces; i++)
- testcase_setsolverflags(solv, pieces[i]);
- }
- else if (!strcmp(pieces[0], "result") && npieces > 1)
- {
- char *result = 0;
- int resultflags = str2resultflags(pool, pieces[1]);
- const char *rdata;
- if (npieces > 2)
- {
- rdata = pool_tmpjoin(pool, testcasedir, pieces[2], 0);
- if (!strcmp(pieces[2], "<inline>"))
- result = read_inline_file(fp, &buf, &bufp, &bufl);
- else
- {
- FILE *rfp = fopen(rdata, "r");
- if (!rfp)
- pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
- else
- {
- result = read_file(rfp);
- fclose(rfp);
- }
- }
- }
- if (resultp)
- *resultp = result;
- else
- solv_free(result);
- if (resultflagsp)
- *resultflagsp = resultflags;
- }
- else if (!strcmp(pieces[0], "nextjob") && npieces == 1)
- {
- break;
- }
- else if (!strcmp(pieces[0], "disable") && npieces == 3)
- {
- Id p;
- if (strcmp(pieces[1], "pkg"))
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: bad disable type '%s'\n", pieces[1]);
- continue;
- }
- if (!prepared)
- pool_createwhatprovides(pool);
- prepared = -1;
- if (!pool->considered)
- {
- pool->considered = solv_calloc(1, sizeof(Map));
- map_init(pool->considered, pool->nsolvables);
- map_setall(pool->considered);
- }
- p = testcase_str2solvid(pool, pieces[2]);
- if (p)
- MAPCLR(pool->considered, p);
- else
- pool_debug(pool, SOLV_ERROR, "disable: unknown package '%s'\n", pieces[2]);
- }
- else if (!strcmp(pieces[0], "feature"))
- {
- int i, j;
- for (i = 1; i < npieces; i++)
- {
- for (j = 0; features[j]; j++)
- if (!strcmp(pieces[i], features[j]))
- break;
- if (!features[j])
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: missing feature '%s'\n", pieces[i]);
- missing_features++;
- }
- }
- if (missing_features)
- break;
- }
- else if (!strcmp(pieces[0], "genid") && npieces > 1)
- {
- Id id;
- /* rejoin */
- if (npieces > 2)
- {
- char *sp;
- for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
- if (*sp == 0)
- *sp = ' ';
- }
- genid = solv_extend(genid, ngenid, 1, sizeof(*genid), 7);
- if (!strcmp(pieces[1], "op") && npieces > 2)
- {
- struct oplist *op;
- for (op = oplist; op->flags; op++)
- if (!strncmp(pieces[2], op->opname, strlen(op->opname)))
- break;
- if (!op->flags)
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown op '%s'\n", pieces[2]);
- break;
- }
- if (ngenid < 2)
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: genid: out of stack\n");
- break;
- }
- ngenid -= 2;
- id = pool_rel2id(pool, genid[ngenid] , genid[ngenid + 1], op->flags, 1);
- }
- else if (!strcmp(pieces[1], "lit"))
- id = pool_str2id(pool, npieces > 2 ? pieces[2] : "", 1);
- else if (!strcmp(pieces[1], "null"))
- id = 0;
- else if (!strcmp(pieces[1], "dep"))
- id = testcase_str2dep(pool, pieces[2]);
- else
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown command '%s'\n", pieces[1]);
- break;
- }
- genid[ngenid++] = id;
- }
- else
- {
- pool_debug(pool, SOLV_ERROR, "testcase_read: cannot parse command '%s'\n", pieces[0]);
- }
- }
- while (job && ngenid > 0)
- queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, genid[--ngenid]);
- genid = solv_free(genid);
- buf = solv_free(buf);
- pieces = solv_free(pieces);
- solv_free(testcasedir);
- if (!prepared)
- {
- pool_addfileprovides(pool);
- pool_createwhatprovides(pool);
- }
- if (!solv)
- {
- solv = solver_create(pool);
- testcase_resetsolverflags(solv);
- }
- if (closefp)
- fclose(fp);
- if (missing_features)
- {
- solver_free(solv);
- solv = 0;
- if (resultflagsp)
- *resultflagsp = 77; /* hack for testsolv */
- }
- return solv;
-}
-
-char *
-testcase_resultdiff(const char *result1, const char *result2)
-{
- Strqueue sq1, sq2, osq;
- char *r;
- strqueue_init(&sq1);
- strqueue_init(&sq2);
- strqueue_init(&osq);
- strqueue_split(&sq1, result1);
- strqueue_split(&sq2, result2);
- strqueue_sort(&sq1);
- strqueue_sort(&sq2);
- strqueue_diff(&sq1, &sq2, &osq);
- r = osq.nstr ? strqueue_join(&osq) : 0;
- strqueue_free(&sq1);
- strqueue_free(&sq2);
- strqueue_free(&osq);
- return r;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include "pool.h"
-#include "repo.h"
-#include "solver.h"
-
-#define TESTCASE_RESULT_TRANSACTION (1 << 0)
-#define TESTCASE_RESULT_PROBLEMS (1 << 1)
-#define TESTCASE_RESULT_ORPHANED (1 << 2)
-#define TESTCASE_RESULT_RECOMMENDED (1 << 3)
-#define TESTCASE_RESULT_UNNEEDED (1 << 4)
-#define TESTCASE_RESULT_ALTERNATIVES (1 << 5)
-#define TESTCASE_RESULT_RULES (1 << 6)
-#define TESTCASE_RESULT_GENID (1 << 7)
-
-extern Id testcase_str2dep(Pool *pool, const char *s);
-extern const char *testcase_dep2str(Pool *pool, Id id);
-extern const char *testcase_repoid2str(Pool *pool, Id repoid);
-extern const char *testcase_solvid2str(Pool *pool, Id p);
-extern Repo *testcase_str2repo(Pool *pool, const char *str);
-extern Id testcase_str2solvid(Pool *pool, const char *str);
-extern const char *testcase_job2str(Pool *pool, Id how, Id what);
-extern Id testcase_str2job(Pool *pool, const char *str, Id *whatp);
-extern int testcase_write_testtags(Repo *repo, FILE *fp);
-extern int testcase_add_testtags(Repo *repo, FILE *fp, int flags);
-extern const char *testcase_getsolverflags(Solver *solv);
-extern int testcase_setsolverflags(Solver *solv, const char *str);
-extern void testcase_resetsolverflags(Solver *solv);
-extern char *testcase_solverresult(Solver *solv, int flags);
-extern int testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname);
-extern Solver *testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp);
-extern char *testcase_resultdiff(const char *result1, const char *result2);
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * util.h
- *
- */
-
-#ifndef LIBSOLV_TOOLS_UTIL_H
-#define LIBSOLV_TOOLS_UTIL_H
-
-static inline Id
-makeevr(Pool *pool, const char *s)
-{
- if (!strncmp(s, "0:", 2) && s[2])
- s += 2;
- return pool_str2id(pool, s, 1);
-}
-
-/**
- * split a string
- */
-#ifndef DISABLE_SPLIT
-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;
-}
-#endif
-
-#ifndef DISABLE_JOIN2
-
-struct joindata {
- char *tmp;
- int tmpl;
-};
-
-/* this join does not depend on parsedata */
-static char *
-join2(struct joindata *jd, const char *s1, const char *s2, const char *s3)
-{
- int l = 1;
- char *p;
-
- if (s1)
- l += strlen(s1);
- if (s2)
- l += strlen(s2);
- if (s3)
- l += strlen(s3);
- if (l > jd->tmpl)
- {
- jd->tmpl = l + 256;
- jd->tmp = solv_realloc(jd->tmp, jd->tmpl);
- }
- p = jd->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);
- }
- *p = 0;
- return jd->tmp;
-}
-
-static inline char *
-join_dup(struct joindata *jd, const char *s)
-{
- return s ? join2(jd, s, 0, 0) : 0;
-}
-
-static inline void
-join_freemem(struct joindata *jd)
-{
- if (jd->tmp)
- free(jd->tmp);
- jd->tmp = 0;
- jd->tmpl = 0;
-}
-
-#endif
-
-#endif /* LIBSOLV_TOOLS_UTIL_H */
--- /dev/null
+;; -*- emacs-lisp -*-
+;;
+;; This file is processed by the dirvars emacs package. Each variable
+;; setting below is performed when this dirvars file is loaded.
+;;
+c-file-style: "gnu";
+fill-column: 78
+indent-tabs-mode: nil
+tab-width: 8
--- /dev/null
+*~
+build
+doc/*.xml
+tests/solver/data.libzypp/*/*.result
+src/solvversion.h
--- /dev/null
+language: C
+before_script: sudo apt-get install cmake
+script: mkdir build && cd build && cmake -DDEBIAN=1 -DMULTI_SEMANTICS=1 .. && make && make test
+
+
--- /dev/null
+
+Having the same key in multiple repodatas of a repo does not work
+- searching will return both entries
+- repo_write will write a broken solv file
+Fixing the search so that it only returns the last entry will also
+fix repo_write.
+
--- /dev/null
+PROJECT (libsolv)
+
+CMAKE_MINIMUM_REQUIRED (VERSION 2.4)
+
+OPTION (ENABLE_STATIC "Build a static version of the libraries?" OFF)
+OPTION (DISABLE_SHARED "Do not build a shared version of the libraries?" OFF)
+
+OPTION (ENABLE_PERL "Build the perl bindings?" OFF)
+OPTION (ENABLE_PYTHON "Build the python bindings?" OFF)
+OPTION (ENABLE_RUBY "Build the ruby bindings?" OFF)
+OPTION (ENABLE_TCL "Build the Tcl bindings?" OFF)
+
+OPTION (USE_VENDORDIRS "Install the bindings in vendor directories?" OFF)
+
+OPTION (ENABLE_RPMDB "Build with rpm database support?" OFF)
+OPTION (ENABLE_PUBKEY "Build with pubkey support?" OFF)
+OPTION (ENABLE_RPMDB_BYRPMHEADER "Build with rpmdb Header support?" OFF)
+OPTION (ENABLE_RPMMD "Build with rpmmd repository support?" OFF)
+OPTION (ENABLE_SUSEREPO "Build with suse repository support?" OFF)
+OPTION (ENABLE_COMPS "Build with fedora comps support?" OFF)
+OPTION (ENABLE_HELIXREPO "Build with helix repository support?" OFF)
+OPTION (ENABLE_DEBIAN "Build with debian database/repository support?" OFF)
+OPTION (ENABLE_MDKREPO "Build with mandriva/mageia repository support?" OFF)
+OPTION (ENABLE_ARCHREPO "Build with archlinux repository support?" OFF)
+OPTION (ENABLE_CUDFREPO "Build with cudf repository support?" OFF)
+OPTION (ENABLE_HAIKU "Build with Haiku package support?" OFF)
+OPTION (ENABLE_APPDATA "Build with AppStream appdata support?" OFF)
+
+OPTION (MULTI_SEMANTICS "Build with support for multiple distribution types?" OFF)
+
+OPTION (ENABLE_LZMA_COMPRESSION "Build with lzma/xz compression support?" OFF)
+OPTION (ENABLE_BZIP2_COMPRESSION "Build with bzip2 compression support?" OFF)
+
+#IF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERISION} GREATER 2.4)
+#ENDIF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERISION} GREATER 2.4)
+
+IF (COMMAND cmake_policy)
+ # escape preprocessor, see -DVERSION below
+ CMAKE_POLICY (SET CMP0005 OLD)
+ENDIF (COMMAND cmake_policy)
+
+# Library
+IF (DEFINED LIB)
+ SET (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB}")
+ELSE (DEFINED LIB)
+ IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
+ SET (LIB_SUFFIX "64")
+ ENDIF (CMAKE_SIZEOF_VOID_P MATCHES "8")
+ SET (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}")
+ENDIF (DEFINED LIB)
+MESSAGE (STATUS "Libraries will be installed in ${LIB_INSTALL_DIR}")
+# Library
+IF (DEFINED INCLUDE)
+ SET (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${INCLUDE}")
+else (DEFINED INCLUDE)
+ SET (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include")
+ENDIF (DEFINED INCLUDE)
+MESSAGE (STATUS "Header files will be installed in ${INCLUDE_INSTALL_DIR}")
+SET (BIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/bin")
+SET (MAN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/man")
+IF (IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/man" AND NOT IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/man")
+ SET (MAN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/man")
+ENDIF (IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/man" AND NOT IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/man")
+MESSAGE(STATUS "Man pages will be installed in ${MAN_INSTALL_DIR}")
+
+####################################################################
+# CONFIGURATION #
+####################################################################
+
+# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
+SET (CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules)
+INSTALL( FILES ${CMAKE_MODULE_PATH}/FindLibSolv.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/share/cmake/Modules )
+
+INCLUDE (${CMAKE_SOURCE_DIR}/VERSION.cmake)
+
+SET (have_system x)
+
+IF (FEDORA)
+MESSAGE(STATUS "Building for Fedora")
+ADD_DEFINITIONS (-DFEDORA)
+SET (ENABLE_RPMDB ON)
+SET (ENABLE_RPMMD ON)
+SET (have_system ${have_system}x)
+ENDIF (FEDORA)
+
+IF (DEBIAN)
+MESSAGE (STATUS "Building for Debian")
+ADD_DEFINITIONS (-DDEBIAN)
+SET (ENABLE_DEBIAN ON)
+SET (have_system ${have_system}x)
+ENDIF (DEBIAN)
+
+IF (SUSE)
+MESSAGE (STATUS "Building for SUSE")
+ADD_DEFINITIONS (-DSUSE)
+SET (ENABLE_RPMDB ON)
+SET (ENABLE_PUBKEY ON)
+SET (ENABLE_RPMDB_BYRPMHEADER ON)
+SET (ENABLE_RPMMD ON)
+SET (ENABLE_SUSEREPO ON)
+SET (ENABLE_HELIXREPO ON)
+SET (ENABLE_LINKED_PKGS ON)
+SET (have_system ${have_system}x)
+ENDIF (SUSE)
+
+IF (ARCHLINUX)
+MESSAGE (STATUS "Building for Archlinux")
+ADD_DEFINITIONS (-DARCHLINUX)
+SET (ENABLE_ARCHREPO ON)
+SET (have_system ${have_system}x)
+ENDIF (ARCHLINUX)
+
+IF (MANDRIVA)
+MESSAGE (STATUS "Building for Mandriva")
+ADD_DEFINITIONS (-DMANDRIVA)
+SET (ENABLE_MDKREPO ON)
+SET (ENABLE_RPMDB ON)
+SET (have_system ${have_system}x)
+ENDIF (MANDRIVA)
+
+IF (MAGEIA)
+MESSAGE (STATUS "Building for Mageia")
+ADD_DEFINITIONS (-DMAGEIA)
+SET (ENABLE_MDKREPO ON)
+SET (ENABLE_RPMDB ON)
+SET (ENABLE_RPMMD ON)
+SET (ENABLE_LZMA_COMPRESSION ON)
+SET (have_system ${have_system}x)
+ENDIF (MAGEIA)
+
+IF (HAIKU)
+MESSAGE(STATUS "Building for Haiku")
+FIND_LIBRARY(HAIKU_BE_LIBRARY NAMES be)
+FIND_LIBRARY(HAIKU_NETWORK_LIBRARY NAMES network)
+FIND_LIBRARY(HAIKU_PACKAGE_LIBRARY NAMES package)
+SET (HAIKU_SYSTEM_LIBRARIES
+ ${HAIKU_BE_LIBRARY} ${HAIKU_NETWORK_LIBRARY} ${HAIKU_PACKAGE_LIBRARY})
+ADD_DEFINITIONS (-DHAIKU)
+SET (ENABLE_HAIKU ON)
+SET (have_system ${have_system}x)
+ENDIF (HAIKU)
+
+IF (${have_system} STREQUAL x)
+ MESSAGE (STATUS "Building for no system")
+ENDIF (${have_system} STREQUAL x)
+IF (${have_system} STRGREATER xx)
+ MESSAGE (FATAL_ERROR "Can only compile for one system type.")
+ENDIF (${have_system} STRGREATER xx)
+
+IF (ENABLE_ARCHREPO)
+SET (ENABLE_LZMA_COMPRESSION ON)
+ENDIF (ENABLE_ARCHREPO)
+
+FIND_PACKAGE (EXPAT REQUIRED)
+FIND_PACKAGE (ZLIB REQUIRED)
+IF (ENABLE_LZMA_COMPRESSION)
+FIND_PACKAGE (LZMA REQUIRED)
+ENDIF (ENABLE_LZMA_COMPRESSION)
+IF (ENABLE_BZIP2_COMPRESSION)
+FIND_PACKAGE (BZip2 REQUIRED)
+ENDIF (ENABLE_BZIP2_COMPRESSION)
+
+IF (RPM5)
+MESSAGE (STATUS "Enabling RPM 5 support")
+ADD_DEFINITIONS (-DRPM5)
+SET (ENABLE_RPMDB ON)
+SET (ENABLE_RPMMD ON)
+FIND_PACKAGE (PkgConfig REQUIRED)
+PKG_CHECK_MODULES (RPM REQUIRED rpm)
+INCLUDE_DIRECTORIES (${RPM_INCLUDE_DIRS})
+ENDIF (RPM5)
+
+IF (MULTI_SEMANTICS)
+MESSAGE (STATUS "Enabling multi dist support")
+ADD_DEFINITIONS (-DMULTI_SEMANTICS)
+ENDIF (MULTI_SEMANTICS)
+
+INCLUDE (CheckIncludeFile)
+IF (ENABLE_RPMDB)
+ FIND_LIBRARY (RPMDB_LIBRARY NAMES rpmdb)
+
+ IF (NOT RPMDB_LIBRARY)
+ FIND_LIBRARY (RPMDB_LIBRARY NAMES rpm)
+ ENDIF (NOT RPMDB_LIBRARY)
+
+ FIND_LIBRARY (RPMIO_LIBRARY NAMES rpmio)
+ IF (RPMIO_LIBRARY)
+ SET(RPMDB_LIBRARY ${RPMIO_LIBRARY} ${RPMDB_LIBRARY})
+ ENDIF (RPMIO_LIBRARY)
+
+ IF (RPM5)
+ FIND_LIBRARY (RPMMISC_LIBRARY NAMES rpmmisc)
+ IF (RPMMISC_LIBRARY)
+ SET (RPMDB_LIBRARY ${RPMMISC_LIBRARY} ${RPMDB_LIBRARY})
+ ENDIF (RPMMISC_LIBRARY)
+ ENDIF (RPM5)
+
+ # check if rpm contains a bundled berkeley db
+ CHECK_INCLUDE_FILE(rpm/db.h HAVE_RPM_DB_H)
+ IF (NOT HAVE_RPM_DB_H)
+ FIND_LIBRARY (DB_LIBRARY NAMES db)
+ IF (DB_LIBRARY)
+ SET (RPMDB_LIBRARY ${DB_LIBRARY} ${RPMDB_LIBRARY})
+ ENDIF (DB_LIBRARY)
+ ENDIF (NOT HAVE_RPM_DB_H)
+ INCLUDE (CheckLibraryExists)
+ CHECK_LIBRARY_EXISTS(rpmio pgpDigGetParams "" HAVE_PGPDIGGETPARAMS)
+ENDIF (ENABLE_RPMDB)
+
+IF (ENABLE_PUBKEY)
+ SET (ENABLE_PGPVRFY ON)
+ENDIF (ENABLE_PUBKEY)
+
+INCLUDE (CheckFunctionExists)
+INCLUDE (TestBigEndian)
+
+CHECK_FUNCTION_EXISTS (strchrnul HAVE_STRCHRNUL)
+CHECK_FUNCTION_EXISTS (fopencookie HAVE_FOPENCOOKIE)
+CHECK_FUNCTION_EXISTS (funopen HAVE_FUNOPEN)
+TEST_BIG_ENDIAN (WORDS_BIGENDIAN)
+
+IF (${CMAKE_MAJOR_VERSION} GREATER 2)
+INCLUDE (CMakePushCheckState)
+INCLUDE (CheckCCompilerFlag)
+MACRO (check_linker_flag FLAG VAR)
+ CMAKE_PUSH_CHECK_STATE (RESET)
+ SET (CMAKE_REQUIRED_FLAGS "${FLAG}")
+ CHECK_C_COMPILER_FLAG ("" "${VAR}")
+ CMAKE_POP_CHECK_STATE ()
+ENDMACRO (check_linker_flag)
+check_linker_flag("-Wl,--as-needed" HAVE_LINKER_AS_NEEDED)
+check_linker_flag("-Wl,--version-script=${CMAKE_SOURCE_DIR}/src/libsolv.ver" HAVE_LINKER_VERSION_SCRIPT)
+ELSE (${CMAKE_MAJOR_VERSION} GREATER 2)
+SET (HAVE_LINKER_AS_NEEDED 1)
+SET (HAVE_LINKER_VERSION_SCRIPT 1)
+ENDIF (${CMAKE_MAJOR_VERSION} GREATER 2)
+
+# should create config.h with #cmakedefine instead...
+FOREACH (VAR HAVE_STRCHRNUL HAVE_FOPENCOOKIE HAVE_FUNOPEN WORDS_BIGENDIAN
+ HAVE_RPM_DB_H HAVE_PGPDIGGETPARAMS
+ ENABLE_RPMDB ENABLE_PUBKEY ENABLE_RPMMD ENABLE_RPMDB_BYRPMHEADER ENABLE_SUSEREPO ENABLE_COMPS
+ ENABLE_HELIXREPO ENABLE_MDKREPO ENABLE_ARCHREPO ENABLE_DEBIAN ENABLE_HAIKU
+ ENABLE_LZMA_COMPRESSION ENABLE_BZIP2_COMPRESSION ENABLE_PGPVRFY ENABLE_APPDATA
+ ENABLE_LINKED_PKGS ENABLE_COMPLEX_DEPS)
+ IF(${VAR})
+ ADD_DEFINITIONS (-D${VAR}=1)
+ SET (SWIG_FLAGS ${SWIG_FLAGS} -D${VAR})
+ ENDIF (${VAR})
+ENDFOREACH (VAR)
+
+SET (PACKAGE "libsolv")
+SET (VERSION "${LIBSOLV_MAJOR}.${LIBSOLV_MINOR}.${LIBSOLV_PATCH}")
+
+ADD_DEFINITIONS (-D_FILE_OFFSET_BITS=64)
+ADD_DEFINITIONS (-DVERSION=\\\"${VERSION}\\\")
+CONFIGURE_FILE (src/solvversion.h.in src/solvversion.h)
+
+SET (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Package dependency solver library")
+SET (CPACK_PACKAGE_VENDOR "SUSE")
+SET (CPACK_PACKAGE_VERSION_MAJOR ${LIBSOLV_MAJOR})
+SET (CPACK_PACKAGE_VERSION_MINOR ${LIBSOLV_MINOR})
+SET (CPACK_PACKAGE_VERSION_PATCH ${LIBSOLV_PATCH})
+SET (CPACK_GENERATOR "TBZ2")
+SET (CPACK_SOURCE_GENERATOR "TBZ2")
+SET (CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE}-${VERSION}")
+SET (CPACK_SOURCE_TOPLEVEL_TAG "Linux-Source:")
+SET (CPACK_TOPLEVEL_TAG "Linux-Source:")
+
+# The following components are regex's to match anywhere (unless anchored)
+# in absolute path + filename to find files or directories to be excluded
+# from source tarball.
+SET (CPACK_SOURCE_IGNORE_FILES
+# temporary files
+"\\\\.swp$"
+# backup files
+"~$"
+# eclipse files
+"\\\\.cdtproject$"
+"\\\\.cproject$"
+"\\\\.project$"
+"\\\\.settings/"
+# others
+"\\\\.#"
+"/#"
+"/build/"
+"/_build/"
+"/\\\\.git/"
+# used before
+"/\\\\.libs/"
+"/\\\\.deps/"
+"\\\\.o$"
+"\\\\.lo$"
+"\\\\.la$"
+"Makefile$"
+"Makefile\\\\.in$"
+# cmake cache files
+"DartConfiguration.tcl$"
+"CMakeCache.txt"
+"CMakeFiles"
+"cmake_install.cmake$"
+"CMakeLists.txt.auto$"
+"CTestTestfile.cmake"
+"CPackConfig.cmake$"
+"CPackSourceConfig.cmake$"
+"libsolv.spec$"
+)
+
+INCLUDE(CPack)
+
+####################################################################
+# INCLUDES #
+####################################################################
+
+#SET (CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)
+INCLUDE_DIRECTORIES (${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/ext ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR}/src SYSTEM )
+
+####################################################################
+
+MESSAGE (STATUS "Looking for modules in ${CMAKE_MODULE_PATH}")
+
+set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3")
+set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -g -O3")
+set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g3 -O0")
+
+SET (SYSTEM_LIBRARIES ${EXPAT_LIBRARY} ${ZLIB_LIBRARY})
+IF (ENABLE_LZMA_COMPRESSION)
+SET (SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} ${LZMA_LIBRARY})
+ENDIF (ENABLE_LZMA_COMPRESSION)
+IF (ENABLE_BZIP2_COMPRESSION)
+SET (SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} ${BZIP2_LIBRARIES})
+ENDIF (ENABLE_BZIP2_COMPRESSION)
+IF (ENABLE_RPMDB)
+SET (SYSTEM_LIBRARIES ${RPMDB_LIBRARY} ${SYSTEM_LIBRARIES})
+ENDIF (ENABLE_RPMDB)
+IF (ENABLE_HAIKU)
+SET (SYSTEM_LIBRARIES ${HAIKU_SYSTEM_LIBRARIES} ${SYSTEM_LIBRARIES})
+ENDIF (ENABLE_HAIKU)
+IF (HAVE_LINKER_AS_NEEDED)
+SET (SYSTEM_LIBRARIES "-Wl,--as-needed" ${SYSTEM_LIBRARIES})
+ENDIF (HAVE_LINKER_AS_NEEDED)
+
+ADD_SUBDIRECTORY (src)
+ADD_SUBDIRECTORY (ext)
+ADD_SUBDIRECTORY (tools)
+IF (ENABLE_PERL OR ENABLE_PYTHON OR ENABLE_RUBY OR ENABLE_TCL)
+ ADD_SUBDIRECTORY (bindings)
+ENDIF (ENABLE_PERL OR ENABLE_PYTHON OR ENABLE_RUBY OR ENABLE_TCL)
+ADD_SUBDIRECTORY (examples)
+ADD_SUBDIRECTORY (doc)
+
+MESSAGE (STATUS "Version: ${VERSION}")
+
+####################################################################
+# RPM SPEC #
+####################################################################
+
+MACRO (SPECFILE)
+ MESSAGE (STATUS "Writing spec file...")
+ CONFIGURE_FILE (${CMAKE_SOURCE_DIR}/package/libsolv.spec.in ${CMAKE_BINARY_DIR}/package/libsolv.spec @ONLY)
+ENDMACRO (SPECFILE)
+
+MACRO (PCFILE)
+ MESSAGE (STATUS "Writing pkg-config file...")
+ CONFIGURE_FILE (${CMAKE_SOURCE_DIR}/libsolv.pc.in ${CMAKE_BINARY_DIR}/libsolv.pc @ONLY)
+ INSTALL( FILES ${CMAKE_BINARY_DIR}/libsolv.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
+ENDMACRO (PCFILE)
+
+SPECFILE ()
+PCFILE ()
+
+SET (AUTOBUILD_COMMAND
+ COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/package/*.tar.bz2
+ COMMAND mkdir -p _CPack_Packages/${CPACK_TOPLEVEL_TAG}
+ COMMAND ${CMAKE_MAKE_PROGRAM} package_source
+ COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2 ${CMAKE_BINARY_DIR}/package
+ COMMAND ${CMAKE_COMMAND} -E remove ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2
+ COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/package/libsolv.changes" "${CMAKE_BINARY_DIR}/package/libsolv.changes"
+)
+
+ADD_CUSTOM_TARGET (srcpackage
+ ${AUTOBUILD_COMMAND}
+)
+
+ADD_CUSTOM_TARGET (srcpackage_local
+ ${AUTOBUILD_COMMAND}
+)
+
+ENABLE_TESTING()
+ADD_SUBDIRECTORY (test)
--- /dev/null
+
+Klaus Kaempf
+ - old language bindings
+
+Duncan Mac-Vicar Prett
+ - cmake support
+ - old ruby bindings
+ - many of the xml parsers
+
+Michael Matz
+ - repodata storage
+ - repopage compression
+ - dataiterator code
+
+Michael Schroeder
+ - overall design
+ - pool & solver implementation
+ - new language
+ - debian support
+ - mandriva/mageia support
+ - archlinux support
+
+Ingo Weinhold
+ - haiku support
+
--- /dev/null
+Compiling and installing the software
+
+Requirements:
+
+- C compiler
+- cmake
+- make
+- expat
+
+Steps to compile/install:
+
+1. mkdir build
+2. cd build
+3. cmake ..
+4. make
+
+5. make install
+
--- /dev/null
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of Novell nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
--- /dev/null
+
+This file contains the major changes between
+libsolv versions:
+
+Version 0.6.12:
+- new features:
+ * tcl bindings
+- new functions:
+ * solv_chksum_cmp
+
+Version 0.6.11:
+- new functions:
+ * pool_ids2whatprovides
+
+Version 0.6.9:
+- new features:
+ * much improved package choosing code
+ * new testcase dependency format
+ * alternatives introspection
+- new functions:
+ * pool_deb_get_autoinstalled
+ * solver_alternative2str
+ * solver_alternatives_count
+ * solver_get_alternative
+ * solver_rule2pkgrule
+ * testcase_dep2str
+
+Version 0.6.5:
+- new features:
+ * support yum style obsolete handling
+
+Version 0.6.1:
+- API change:
+ repodata_stringify() now returns the string
+- new features:
+ * add BREAK_ORPHANS and KEEP_ORPHANS solver flags
+
+Version 0.6.0:
+- ABI change: cleaned up and reordered knownid.h
+- added support for sha224/sha384/sha512
+- API change in the bindings:
+ * dropped solvid arg from most Dataiterator
+ constructors
+ * changed Datamatch results from methods to
+ attributes
+ * automatically delete the pool if the owner
+ object is freed (use the disown method to
+ get the old behavior).
+- new functions:
+ * pool_add_userinstalled_jobs
+ * solver_get_userinstalled
+
--- /dev/null
+This is libsolv, a free package dependency solver using a satisfiability
+algorithm.
+
+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 satisfiability, a well known and researched topic, for
+ resolving package dependencies.
+
+The sat-solver code has been written to aim for the newest packages,
+record the decision tree to provide introspection, and also allows to
+provide the user with suggestions on how to deal with unsolvable
+problems. It also takes advantage of the repository storage to
+minimize memory usage.
+
+Supported package formats:
+ - rpm/rpm5
+ - deb
+ - arch linux
+ - haiku
+
+Supported repository formats:
+ - rpmmd (primary, filelists, comps, deltainfo/presto, updateinfo)
+ - susetags, suse product formats
+ - mandriva/mageia (synthesis, info, files)
+ - arch linux
+ - red carpet helix format
+ - haiku
+
+Requires: cmake 2.4.x
+
+mkdir build
+cd build
+cmake ..
+make
+
+To create a package:
+make srcpackage
+see package/
--- /dev/null
+# ==================================================
+# Versioning
+# ==========
+#
+# MAJOR Major number for this branch.
+#
+# MINOR The most recent interface number this
+# library implements.
+#
+# COMPATMINOR The latest binary compatible minor number
+# this library implements.
+#
+# PATCH The implementation number of the current interface.
+#
+#
+# - The package VERSION will be MAJOR.MINOR.PATCH.
+#
+# - Libtool's -version-info will be derived from MAJOR, MINOR, PATCH
+# and COMPATMINOR (see configure.ac).
+#
+# - Changing MAJOR always breaks binary compatibility.
+#
+# - Changing MINOR doesn't break binary compatibility by default.
+# Only if COMPATMINOR is changed as well.
+#
+#
+# 1) After branching from TRUNK increment TRUNKs MAJOR and
+# start with version `MAJOR.0.0' and also set COMPATMINOR to 0.
+#
+# 2) Update the version information only immediately before a public release
+# of your software. More frequent updates are unnecessary, and only guarantee
+# that the current interface number gets larger faster.
+#
+# 3) If the library source code has changed at all since the last update,
+# then increment PATCH.
+#
+# 4) If any interfaces have been added, removed, or changed since the last
+# update, increment MINOR, and set PATCH to 0.
+#
+# 5) If any interfaces have been added since the last public release, then
+# leave COMPATMINOR unchanged. (binary compatible change)
+#
+# 6) If any interfaces have been removed since the last public release, then
+# set COMPATMINOR to MINOR. (binary incompatible change)
+#
+
+SET(LIBSOLV_SOVERSION "0")
+SET(LIBSOLVEXT_SOVERSION "0")
+
+SET(LIBSOLV_MAJOR "0")
+SET(LIBSOLV_MINOR "6")
+SET(LIBSOLV_PATCH "15")
+
--- /dev/null
+SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+
+FIND_PACKAGE (SWIG)
+
+MESSAGE (STATUS "Found SWIG version ${SWIG_VERSION}")
+SET (SWIG_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/solv.i")
+
+IF (ENABLE_PYTHON)
+ ADD_SUBDIRECTORY (python)
+ENDIF (ENABLE_PYTHON)
+IF (ENABLE_PERL)
+ ADD_SUBDIRECTORY (perl)
+ENDIF (ENABLE_PERL)
+IF (ENABLE_RUBY)
+ ADD_SUBDIRECTORY (ruby)
+ENDIF (ENABLE_RUBY)
+IF (ENABLE_TCL)
+ ADD_SUBDIRECTORY (tcl)
+ENDIF (ENABLE_TCL)
--- /dev/null
+FIND_PACKAGE (Perl)
+
+EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{ccflags}" OUTPUT_VARIABLE PERL_CCFLAGS)
+EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{archlib}.\"/CORE\"" OUTPUT_VARIABLE PERL_CORE_DIR)
+EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{ccldflags}" OUTPUT_VARIABLE PERL_CCLDFLAGS)
+EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{installsitearch}" OUTPUT_VARIABLE PERL_SITEARCHDIR)
+EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -e "use Config; print \$Config{installvendorarch}" OUTPUT_VARIABLE PERL_VENDORARCHDIR)
+
+IF (USE_VENDORDIRS)
+ SET (PERL_INSTALL_DIR ${PERL_VENDORARCHDIR})
+ELSE (USE_VENDORDIRS)
+ SET (PERL_INSTALL_DIR ${PERL_SITEARCHDIR})
+ENDIF (USE_VENDORDIRS)
+
+MESSAGE (STATUS "Perl executable: ${PERL_EXECUTABLE}")
+MESSAGE (STATUS "Perl installation dir: ${PERL_INSTALL_DIR}")
+
+ADD_CUSTOM_COMMAND (
+ OUTPUT solv_perl.c
+ COMMAND ${SWIG_EXECUTABLE} -perl ${SWIG_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_perl.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
+ COMMAND sed -i -e "s/SvTYPE(tsv) == SVt_PVHV/SvTYPE(tsv) == SVt_PVHV || SvTYPE(tsv) == SVt_PVAV/" solv_perl.c
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
+ VERBATIM
+)
+
+ADD_DEFINITIONS(${PERL_CCFLAGS} -Wno-unused -Wno-nonnull)
+LINK_DIRECTORIES (${PERL_CORE_DIR})
+INCLUDE_DIRECTORIES (${PERL_INCLUDE_PATH} ${PERL_CORE_DIR})
+
+ADD_LIBRARY (bindings_perl MODULE solv_perl.c)
+SET_TARGET_PROPERTIES (bindings_perl PROPERTIES PREFIX "" OUTPUT_NAME "solv")
+SET_TARGET_PROPERTIES (bindings_perl PROPERTIES LINK_FLAGS "${PERL_CCLDFLAGS}")
+TARGET_LINK_LIBRARIES (bindings_perl libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+INSTALL (TARGETS bindings_perl LIBRARY DESTINATION ${PERL_INSTALL_DIR})
+INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/solv.pm DESTINATION ${PERL_INSTALL_DIR})
--- /dev/null
+#SET (PythonLibs_FIND_VERSION 3)
+
+FIND_PACKAGE (PythonLibs)
+FIND_PACKAGE (PythonInterp ${PYTHONLIBS_VERSION_STRING} REQUIRED)
+EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from sys import stdout; from distutils import sysconfig; stdout.write(sysconfig.get_python_lib(True))" OUTPUT_VARIABLE PYTHON_INSTALL_DIR)
+
+IF (NOT DEFINED PYTHON_VERSION_MAJOR)
+ SET (PYTHON_VERSION_MAJOR 2)
+ENDIF (NOT DEFINED PYTHON_VERSION_MAJOR)
+IF (${PYTHON_VERSION_MAJOR} GREATER 2)
+ SET (SWIG_PY_FLAGS -DPYTHON3=1)
+ENDIF (${PYTHON_VERSION_MAJOR} GREATER 2)
+
+MESSAGE (STATUS "Python executable: ${PYTHON_EXECUTABLE}")
+MESSAGE (STATUS "Python installation dir: ${PYTHON_INSTALL_DIR}")
+
+ADD_CUSTOM_COMMAND (
+ OUTPUT solv_python.c
+ COMMAND ${SWIG_EXECUTABLE} ${SWIG_FLAGS} -python ${SWIG_PY_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_python.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
+)
+
+ADD_DEFINITIONS(-Wno-unused)
+INCLUDE_DIRECTORIES (${PYTHON_INCLUDE_PATH})
+
+ADD_LIBRARY (bindings_python MODULE solv_python.c)
+SET_TARGET_PROPERTIES (bindings_python PROPERTIES PREFIX "" OUTPUT_NAME "_solv")
+TARGET_LINK_LIBRARIES (bindings_python libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+INSTALL (TARGETS bindings_python LIBRARY DESTINATION ${PYTHON_INSTALL_DIR})
+INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/solv.py DESTINATION ${PYTHON_INSTALL_DIR})
+
--- /dev/null
+FIND_PACKAGE (Ruby)
+
+IF (USE_VENDORDIRS AND RUBY_VENDORARCH_DIR)
+ SET (RUBY_INSTALL_DIR ${RUBY_VENDORARCH_DIR})
+ELSE (USE_VENDORDIRS AND RUBY_VENDORARCH_DIR)
+ SET (RUBY_INSTALL_DIR ${RUBY_SITEARCH_DIR})
+ENDIF (USE_VENDORDIRS AND RUBY_VENDORARCH_DIR)
+
+MESSAGE (STATUS "Ruby executable: ${RUBY_EXECUTABLE}")
+MESSAGE (STATUS "Ruby installation dir: ${RUBY_INSTALL_DIR}")
+
+ADD_CUSTOM_COMMAND (
+ OUTPUT solv_ruby.c
+ COMMAND ${SWIG_EXECUTABLE} -ruby ${SWIG_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_ruby.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
+)
+
+ADD_DEFINITIONS(-Wno-unused)
+INCLUDE_DIRECTORIES (${RUBY_INCLUDE_PATH})
+
+ADD_LIBRARY (bindings_ruby MODULE solv_ruby.c)
+SET_TARGET_PROPERTIES (bindings_ruby PROPERTIES PREFIX "" OUTPUT_NAME "solv")
+TARGET_LINK_LIBRARIES (bindings_ruby libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+INSTALL (TARGETS bindings_ruby LIBRARY DESTINATION ${RUBY_INSTALL_DIR})
--- /dev/null
+/*
+ * WARNING: for perl iterator/array support you need to run
+ * sed -i -e 's/SvTYPE(tsv) == SVt_PVHV/SvTYPE(tsv) == SVt_PVHV || SvTYPE(tsv) == SVt_PVAV/'
+ * on the generated c code
+ */
+
+%module solv
+
+#ifdef SWIGRUBY
+%markfunc Pool "mark_Pool";
+#endif
+
+/**
+ ** binaryblob handling
+ **/
+
+%{
+typedef struct {
+ const void *data;
+ size_t len;
+} BinaryBlob;
+%}
+
+%typemap(in,noblock=1,fragment="SWIG_AsCharPtrAndSize") (const unsigned char *str, size_t len) (int res, char *buf = 0, size_t size = 0, int alloc = 0) {
+#if defined(SWIGTCL)
+ {
+ int bal;
+ unsigned char *ba;
+ res = SWIG_TypeError;
+ ba = Tcl_GetByteArrayFromObj($input, &bal);
+ if (ba) {
+ buf = (char *)ba;
+ size = bal;
+ res = SWIG_OK;
+ alloc = SWIG_OLDOBJ;
+ }
+ }
+#else
+ res = SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc);
+ if (buf && size)
+ size--;
+#endif
+ if (!SWIG_IsOK(res)) {
+#if defined(SWIGPYTHON)
+ const void *pybuf = 0;
+ Py_ssize_t pysize = 0;
+ res = PyObject_AsReadBuffer($input, &pybuf, &pysize);
+ if (res < 0) {
+ %argument_fail(res, "BinaryBlob", $symname, $argnum);
+ } else {
+ buf = (void *)pybuf;
+ size = pysize;
+ }
+#else
+ %argument_fail(res, "const char *", $symname, $argnum);
+#endif
+ }
+ $1 = (unsigned char *)buf;
+ $2 = size;
+}
+
+%typemap(freearg,noblock=1,match="in") (const unsigned char *str, int len) {
+ if (alloc$argnum == SWIG_NEWOBJ) %delete_array(buf$argnum);
+}
+
+%typemap(out,noblock=1,fragment="SWIG_FromCharPtrAndSize") BinaryBlob {
+#if defined(SWIGPYTHON) && defined(PYTHON3)
+ $result = $1.data ? Py_BuildValue("y#", $1.data, $1.len) : SWIG_Py_Void();
+#elif defined(SWIGTCL)
+ Tcl_SetObjResult(interp, $1.data ? Tcl_NewByteArrayObj($1.data, $1.len) : NULL);
+#else
+ $result = SWIG_FromCharPtrAndSize($1.data, $1.len);
+#if defined(SWIGPERL)
+ argvi++;
+#endif
+#endif
+}
+
+/**
+ ** Queue handling
+ **/
+
+%typemap(arginit) Queue {
+ queue_init(&$1);
+}
+%typemap(freearg) Queue {
+ queue_free(&$1);
+}
+
+#if defined(SWIGPYTHON)
+%typemap(in) Queue {
+ /* Check if is a list */
+ if (PyList_Check($input)) {
+ int size = PyList_Size($input);
+ int i = 0;
+ for (i = 0; i < size; i++) {
+ PyObject *o = PyList_GetItem($input,i);
+ int v;
+ int e = SWIG_AsVal_int(o, &v);
+ if (!SWIG_IsOK(e))
+ SWIG_exception_fail(SWIG_ArgError(e), "list must contain only integers");
+ queue_push(&$1, v);
+ }
+ } else {
+ SWIG_exception_fail(SWIG_TypeError, "list must contain only integers");
+ }
+}
+
+%typemap(out) Queue {
+ int i;
+ PyObject *o = PyList_New($1.count);
+ for (i = 0; i < $1.count; i++)
+ PyList_SetItem(o, i, SWIG_From_int($1.elements[i]));
+ queue_free(&$1);
+ $result = o;
+}
+
+%define Queue2Array(type, step, con) %{
+ int i;
+ int cnt = $1.count / step;
+ Id *idp = $1.elements;
+ PyObject *o = PyList_New(cnt);
+ for (i = 0; i < cnt; i++, idp += step)
+ {
+ Id id = *idp;
+#define result resultx
+ type result = con;
+ $typemap(out, type)
+ PyList_SetItem(o, i, $result);
+#undef result
+ }
+ queue_free(&$1);
+ $result = o;
+%}
+
+%enddef
+
+#endif /* SWIGPYTHON */
+
+#if defined(SWIGPERL)
+%typemap(in) Queue {
+ AV *av;
+ int i, size;
+ if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV)
+ SWIG_croak("Argument $argnum is not an array reference.");
+ av = (AV*)SvRV($input);
+ size = av_len(av);
+ for (i = 0; i <= size; i++) {
+ SV **sv = av_fetch(av, i, 0);
+ int v;
+ int e = SWIG_AsVal_int(*sv, &v);
+ if (!SWIG_IsOK(e)) {
+ SWIG_croak("list must contain only integers");
+ }
+ queue_push(&$1, v);
+ }
+}
+/* AV *o = newAV();
+ * av_push(o, SvREFCNT_inc(SWIG_From_int($1.elements[i])));
+ * $result = newRV_noinc((SV*)o); argvi++;
+ */
+%typemap(out) Queue {
+ int i;
+ if (argvi + $1.count + 1 >= items) {
+ EXTEND(sp, (argvi + $1.count + 1) - items + 1);
+ }
+ for (i = 0; i < $1.count; i++)
+ ST(argvi++) = SvREFCNT_inc(SWIG_From_int($1.elements[i]));
+ queue_free(&$1);
+ $result = 0;
+}
+
+%define Queue2Array(type, step, con) %{
+ int i;
+ int cnt = $1.count / step;
+ Id *idp = $1.elements;
+ if (argvi + cnt + 1 >= items) {
+ EXTEND(sp, (argvi + cnt + 1) - items + 1);
+ }
+ for (i = 0; i < cnt; i++, idp += step)
+ {
+ Id id = *idp;
+#define result resultx
+ type result = con;
+ $typemap(out, type)
+ SvREFCNT_inc(ST(argvi - 1));
+#undef result
+ }
+ queue_free(&$1);
+ $result = 0;
+%}
+%enddef
+
+#endif /* SWIGPERL */
+
+
+#if defined(SWIGRUBY)
+%typemap(in) Queue {
+ int size, i;
+ VALUE *o, ary;
+ ary = rb_Array($input);
+ size = RARRAY_LEN(ary);
+ i = 0;
+ o = RARRAY_PTR(ary);
+ for (i = 0; i < size; i++, o++) {
+ int v;
+ int e = SWIG_AsVal_int(*o, &v);
+ if (!SWIG_IsOK(e))
+ SWIG_exception_fail(SWIG_TypeError, "list must contain only integers");
+ queue_push(&$1, v);
+ }
+}
+%typemap(out) Queue {
+ int i;
+ VALUE o = rb_ary_new2($1.count);
+ for (i = 0; i < $1.count; i++)
+ rb_ary_store(o, i, SWIG_From_int($1.elements[i]));
+ queue_free(&$1);
+ $result = o;
+}
+%typemap(arginit) Queue {
+ queue_init(&$1);
+}
+%typemap(freearg) Queue {
+ queue_free(&$1);
+}
+%define Queue2Array(type, step, con) %{
+ int i;
+ int cnt = $1.count / step;
+ Id *idp = $1.elements;
+ VALUE o = rb_ary_new2(cnt);
+ for (i = 0; i < cnt; i++, idp += step)
+ {
+ Id id = *idp;
+#define result resultx
+ type result = con;
+ $typemap(out, type)
+ rb_ary_store(o, i, $result);
+#undef result
+ }
+ queue_free(&$1);
+ $result = o;
+%}
+%enddef
+
+#endif /* SWIGRUBY */
+
+#if defined(SWIGTCL)
+%typemap(in) Queue {
+ /* Check if is a list */
+ int size = 0;
+ int i = 0;
+
+ if (TCL_OK != Tcl_ListObjLength(interp, $input, &size))
+ SWIG_exception_fail(SWIG_TypeError, "argument is not a list");
+ for (i = 0; i < size; i++) {
+ Tcl_Obj *o = NULL;
+ int e, v;
+
+ if (TCL_OK != Tcl_ListObjIndex(interp, $input, i, &o))
+ SWIG_exception_fail(SWIG_IndexError, "failed to retrieve a list member");
+ e = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(o, &v);
+ if (!SWIG_IsOK(e))
+ SWIG_exception_fail(SWIG_ArgError(e), "list must contain only integers");
+ queue_push(&$1, v);
+ }
+}
+
+%typemap(out) Queue {
+ Tcl_Obj *objvx[$1.count];
+ int i;
+
+ for (i = 0; i < $1.count; i++) {
+ objvx[i] = SWIG_From_int($1.elements[i]);
+ }
+ Tcl_SetObjResult(interp, Tcl_NewListObj($1.count, objvx));
+ queue_free(&$1);
+}
+
+%define Queue2Array(type, step, con) %{
+ { /* scope is needed to make the goto of SWIG_exception_fail work */
+ int i;
+ int cnt = $1.count / step;
+ Id *idp = $1.elements;
+ Tcl_Obj *objvx[cnt];
+
+ for (i = 0; i < cnt; i++, idp += step) {
+ Id id = *idp;
+#define result resultx
+#define Tcl_SetObjResult(i, x) resultobj = x
+ type result = con;
+ Tcl_Obj *resultobj;
+ $typemap(out, type)
+ objvx[i] = resultobj;
+#undef Tcl_SetObjResult
+#undef result
+ }
+ queue_free(&$1);
+ Tcl_SetObjResult(interp, Tcl_NewListObj(cnt, objvx));
+ }
+%}
+
+%enddef
+
+%typemap(in) Queue solvejobs {
+ /* Check if is a list */
+ int size = 0;
+ int i = 0;
+
+ if (TCL_OK != Tcl_ListObjLength(interp, $input, &size))
+ SWIG_exception_fail(SWIG_TypeError, "argument is not a list");
+ for (i = 0; i < size; i++) {
+ Tcl_Obj *o = NULL;
+ void *jp;
+ Job *j;
+ int e;
+
+ if (TCL_OK != Tcl_ListObjIndex(interp, $input, i, &o))
+ SWIG_exception_fail(SWIG_IndexError, "failed to retrieve a list member");
+ e = SWIG_ConvertPtr(o, &jp ,SWIGTYPE_p_Job, 0 | 0 );
+ if (!SWIG_IsOK(e))
+ SWIG_exception_fail(SWIG_ArgError(e), "list member is not a Job");
+ j = (Job *)jp;
+ queue_push2(&$1, j->how, j->what);
+ }
+}
+
+#endif /* SWIGTCL */
+
+
+#if defined(SWIGPERL)
+
+/* work around a swig bug */
+%{
+#undef SWIG_CALLXS
+#ifdef PERL_OBJECT
+# define SWIG_CALLXS(_name) TOPMARK=MARK-PL_stack_base;_name(cv,pPerl)
+#else
+# ifndef MULTIPLICITY
+# define SWIG_CALLXS(_name) TOPMARK=MARK-PL_stack_base;_name(cv)
+# else
+# define SWIG_CALLXS(_name) TOPMARK=MARK-PL_stack_base;_name(PERL_GET_THX, cv)
+# endif
+#endif
+%}
+
+
+%define perliter(class)
+ %perlcode {
+ sub class##::FETCH {
+ my $i = ${##class##::ITERATORS}{$_[0]};
+ if ($i) {
+ $_[1] == $i->[0] - 1 ? $i->[1] : undef;
+ } else {
+ $_[0]->__getitem__($_[1]);
+ }
+ }
+ sub class##::FETCHSIZE {
+ my $i = ${##class##::ITERATORS}{$_[0]};
+ if ($i) {
+ ($i->[1] = $_[0]->__next__()) ? ++$i->[0] : 0;
+ } else {
+ $_[0]->__len__();
+ }
+ }
+ }
+%enddef
+
+%{
+
+#define SWIG_PERL_ITERATOR 0x80
+
+SWIGRUNTIMEINLINE SV *
+SWIG_Perl_NewArrayObj(SWIG_MAYBE_PERL_OBJECT void *ptr, swig_type_info *t, int flags) {
+ SV *result = sv_newmortal();
+ if (ptr && (flags & (SWIG_SHADOW | SWIG_POINTER_OWN))) {
+ SV *self;
+ SV *obj=newSV(0);
+ AV *array=newAV();
+ HV *stash;
+ sv_setref_pv(obj, (char *) SWIG_Perl_TypeProxyName(t), ptr);
+ stash=SvSTASH(SvRV(obj));
+ if (flags & SWIG_POINTER_OWN) {
+ HV *hv;
+ GV *gv=*(GV**)hv_fetch(stash, "OWNER", 5, TRUE);
+ if (!isGV(gv))
+ gv_init(gv, stash, "OWNER", 5, FALSE);
+ hv=GvHVn(gv);
+ hv_store_ent(hv, obj, newSViv(1), 0);
+ }
+ if (flags & SWIG_PERL_ITERATOR) {
+ HV *hv;
+ GV *gv=*(GV**)hv_fetch(stash, "ITERATORS", 9, TRUE);
+ AV *av=newAV();
+ if (!isGV(gv))
+ gv_init(gv, stash, "ITERATORS", 9, FALSE);
+ hv=GvHVn(gv);
+ hv_store_ent(hv, obj, newRV_inc((SV *)av), 0);
+ }
+ sv_magic((SV *)array, (SV *)obj, 'P', Nullch, 0);
+ SvREFCNT_dec(obj);
+ self=newRV_noinc((SV *)array);
+ sv_setsv(result, self);
+ SvREFCNT_dec((SV *)self);
+ sv_bless(result, stash);
+ } else {
+ sv_setref_pv(result, (char *) SWIG_Perl_TypeProxyName(t), ptr);
+ }
+ return result;
+}
+
+%}
+
+%typemap(out) Perlarray {
+ ST(argvi) = SWIG_Perl_NewArrayObj(SWIG_PERL_OBJECT_CALL SWIG_as_voidptr(result), $1_descriptor, $owner | $shadow); argvi++;
+}
+%typemap(out) Perliterator {
+ ST(argvi) = SWIG_Perl_NewArrayObj(SWIG_PERL_OBJECT_CALL SWIG_as_voidptr(result), $1_descriptor, $owner | $shadow | SWIG_PERL_ITERATOR); argvi++;
+}
+
+%typemap(out) Pool_solvable_iterator * = Perlarray;
+%typemap(out) Pool_solvable_iterator * solvables_iter = Perliterator;
+%typemap(out) Pool_repo_iterator * = Perlarray;
+%typemap(out) Pool_repo_iterator * repos_iter = Perliterator;
+%typemap(out) Repo_solvable_iterator * = Perlarray;
+%typemap(out) Repo_solvable_iterator * solvables_iter = Perliterator;
+%typemap(out) Dataiterator * = Perliterator;
+
+#endif /* SWIGPERL */
+
+
+/**
+ ** appdata handling
+ **/
+
+#if defined(SWIGPYTHON)
+typedef PyObject *AppObjectPtr;
+%typemap(in) AppObjectPtr {
+ if ($input)
+ Py_INCREF($input);
+ $1 = $input;
+}
+%typemap(out) AppObjectPtr {
+ $result = $1 ? $1 : Py_None;
+ Py_INCREF($result);
+}
+#elif defined(SWIGPERL)
+typedef SV *AppObjectPtr;
+%typemap(in) AppObjectPtr {
+ if ($input) {
+ $1 = newSV(0);
+ sv_setsv((SV *)$1, $input);
+ } else
+ $1 = (void *)0;
+}
+%typemap(out) AppObjectPtr {
+ $result = sv_2mortal($1 ? SvREFCNT_inc($1) : newSV(0));
+ argvi++;
+}
+#elif defined(SWIGRUBY)
+typedef VALUE AppObjectPtr;
+%typemap(in) AppObjectPtr {
+ $1 = (void *)$input;
+}
+%typemap(out) AppObjectPtr {
+ $result = (VALUE)$1;
+}
+#elif defined(SWIGTCL)
+typedef Tcl_Obj *AppObjectPtr;
+%typemap(in) AppObjectPtr {
+ if ($input)
+ Tcl_IncrRefCount($input);
+ $1 = (void *)$input;
+}
+%typemap(out) AppObjectPtr {
+ Tcl_SetObjResult(interp, $1 ? $1 : Tcl_NewObj());
+}
+#else
+#warning AppObjectPtr not defined for this language!
+#endif
+
+/**
+ ** FILE handling
+ **/
+
+#ifdef SWIGPYTHON
+%include "file.i"
+#else
+%fragment("SWIG_AsValFilePtr","header") {}
+#endif
+
+
+%fragment("SWIG_AsValSolvFpPtr","header", fragment="SWIG_AsValFilePtr") {
+
+SWIGINTERN int
+#ifdef SWIGRUBY
+SWIG_AsValSolvFpPtr(VALUE obj, FILE **val) {
+#elif defined(SWIGTCL)
+SWIG_AsValSolvFpPtr SWIG_TCL_DECL_ARGS_2(void *obj, FILE **val) {
+#else
+SWIG_AsValSolvFpPtr(void *obj, FILE **val) {
+#endif
+ static swig_type_info* desc = 0;
+ void *vptr = 0;
+ int ecode;
+
+ if (!desc) desc = SWIG_TypeQuery("SolvFp *");
+ if ((SWIG_ConvertPtr(obj, &vptr, desc, 0)) == SWIG_OK) {
+ if (val)
+ *val = vptr ? ((SolvFp *)vptr)->fp : 0;
+ return SWIG_OK;
+ }
+#ifdef SWIGPYTHON
+ ecode = SWIG_AsValFilePtr(obj, val);
+ if (ecode == SWIG_OK)
+ return ecode;
+#endif
+ return SWIG_TypeError;
+}
+
+#if defined(SWIGTCL)
+#define SWIG_AsValSolvFpPtr(x, y) SWIG_AsValSolvFpPtr SWIG_TCL_CALL_ARGS_2(x, y)
+#endif
+
+}
+
+
+/**
+ ** DepId handling
+ **/
+
+%fragment("SWIG_AsValDepId","header") {
+
+SWIGINTERN int
+#ifdef SWIGRUBY
+SWIG_AsValDepId(VALUE obj, int *val) {
+#elif defined(SWIGTCL)
+SWIG_AsValDepId SWIG_TCL_DECL_ARGS_2(void *obj, int *val) {
+#else
+SWIG_AsValDepId(void *obj, int *val) {
+#endif
+ static swig_type_info* desc = 0;
+ void *vptr = 0;
+ int ecode;
+ if (!desc) desc = SWIG_TypeQuery("Dep *");
+#ifdef SWIGTCL
+ ecode = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(obj, val);
+#else
+ ecode = SWIG_AsVal_int(obj, val);
+#endif
+ if (SWIG_IsOK(ecode))
+ return ecode;
+ if ((SWIG_ConvertPtr(obj, &vptr, desc, 0)) == SWIG_OK) {
+ if (val)
+ *val = vptr ? ((Dep *)vptr)->id : 0;
+ return SWIG_OK;
+ }
+ return SWIG_TypeError;
+}
+
+#ifdef SWIGTCL
+#define SWIG_AsValDepId(x, y) SWIG_AsValDepId SWIG_TCL_CALL_ARGS_2(x, y)
+#endif
+}
+
+/**
+ ** Pool disown helper
+ **/
+
+%typemap(out) disown_helper {
+#if defined(SWIGRUBY)
+ SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
+#elif defined(SWIGPYTHON)
+ SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
+#elif defined(SWIGPERL)
+ SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
+#elif defined(SWIGTCL)
+ SWIG_ConvertPtr(objv[1], &argp1, SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0);
+#else
+#warning disown_helper not implemented for this language, this is likely going to leak memory
+#endif
+
+#ifdef SWIGTCL
+ Tcl_SetObjResult(interp, SWIG_From_int((int)(0)));
+#else
+ $result = SWIG_From_int((int)(0));
+#endif
+}
+
+
+/**
+ ** misc stuff
+ **/
+
+%include "typemaps.i"
+
+%typemap(in,numinputs=0,noblock=1) XRule **OUTPUT ($*1_ltype temp) {
+ $1 = &temp;
+}
+%typemap(argout,noblock=1) XRule **OUTPUT {
+ %append_output(SWIG_NewPointerObj((void*)(*$1), SWIGTYPE_p_XRule, SWIG_POINTER_OWN | %newpointer_flags));
+}
+
+%typemaps_asval(%checkcode(POINTER), SWIG_AsValSolvFpPtr, "SWIG_AsValSolvFpPtr", FILE*);
+%typemaps_asval(%checkcode(INT32), SWIG_AsValDepId, "SWIG_AsValDepId", DepId);
+
+
+/**
+ ** the C declarations
+ **/
+
+%{
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* argh, swig undefs bool for perl */
+#ifndef bool
+typedef int bool;
+#endif
+
+#include "pool.h"
+#include "poolarch.h"
+#include "evr.h"
+#include "solver.h"
+#include "policy.h"
+#include "solverdebug.h"
+#include "repo_solv.h"
+#include "chksum.h"
+#include "selection.h"
+
+#include "repo_write.h"
+#ifdef ENABLE_RPMDB
+#include "repo_rpmdb.h"
+#endif
+#ifdef ENABLE_PUBKEY
+#include "repo_pubkey.h"
+#endif
+#ifdef ENABLE_DEBIAN
+#include "repo_deb.h"
+#endif
+#ifdef ENABLE_RPMMD
+#include "repo_rpmmd.h"
+#include "repo_updateinfoxml.h"
+#include "repo_deltainfoxml.h"
+#include "repo_repomdxml.h"
+#endif
+#ifdef ENABLE_SUSEREPO
+#include "repo_products.h"
+#include "repo_susetags.h"
+#include "repo_content.h"
+#endif
+#ifdef ENABLE_MDKREPO
+#include "repo_mdk.h"
+#endif
+#ifdef ENABLE_ARCHREPO
+#include "repo_arch.h"
+#endif
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+#include "solv_xfopen.h"
+#include "testcase.h"
+
+/* for old ruby versions */
+#ifndef RARRAY_PTR
+#define RARRAY_PTR(ary) (RARRAY(ary)->ptr)
+#endif
+#ifndef RARRAY_LEN
+#define RARRAY_LEN(ary) (RARRAY(ary)->len)
+#endif
+
+#define SOLVER_SOLUTION_ERASE -100
+#define SOLVER_SOLUTION_REPLACE -101
+#define SOLVER_SOLUTION_REPLACE_DOWNGRADE -102
+#define SOLVER_SOLUTION_REPLACE_ARCHCHANGE -103
+#define SOLVER_SOLUTION_REPLACE_VENDORCHANGE -104
+#define SOLVER_SOLUTION_REPLACE_NAMECHANGE -105
+
+typedef void *AppObjectPtr;
+typedef Id DepId;
+
+typedef struct {
+ Pool *pool;
+ Id id;
+} Dep;
+
+typedef struct {
+ Pool *pool;
+ Id id;
+} XSolvable;
+
+typedef struct {
+ Solver *solv;
+ Id id;
+} XRule;
+
+typedef struct {
+ Repo *repo;
+ Id id;
+} XRepodata;
+
+typedef struct {
+ Pool *pool;
+ Id id;
+} Pool_solvable_iterator;
+
+typedef struct {
+ Pool *pool;
+ Id id;
+} Pool_repo_iterator;
+
+typedef struct {
+ Repo *repo;
+ Id id;
+} Repo_solvable_iterator;
+
+typedef struct {
+ Pool *pool;
+ int how;
+ Id what;
+} Job;
+
+typedef struct {
+ Solver *solv;
+ Id id;
+} Problem;
+
+typedef struct {
+ Solver *solv;
+ Id problemid;
+ Id id;
+} Solution;
+
+typedef struct {
+ Solver *solv;
+ Id problemid;
+ Id solutionid;
+ Id id;
+
+ Id type;
+ Id p;
+ Id rp;
+} Solutionelement;
+
+typedef struct {
+ Solver *solv;
+ Id rid;
+ Id type;
+ Id source;
+ Id target;
+ Id dep_id;
+} Ruleinfo;
+
+typedef struct {
+ Solver *solv;
+ Id type;
+ Id rid;
+ Id from_id;
+ Id dep_id;
+ Id chosen_id;
+ Queue choices;
+ int level;
+} Alternative;
+
+typedef struct {
+ Transaction *transaction;
+ int mode;
+ Id type;
+ int count;
+ Id fromid;
+ Id toid;
+} TransactionClass;
+
+typedef struct {
+ Pool *pool;
+ Queue q;
+ int flags;
+} Selection;
+
+typedef struct {
+ FILE *fp;
+} SolvFp;
+
+typedef Dataiterator Datamatch;
+
+typedef int disown_helper;
+
+struct myappdata {
+ void *appdata;
+ int disowned;
+};
+
+
+%}
+
+/**
+ ** appdata helpers
+ **/
+
+#ifdef SWIGRUBY
+
+%{
+SWIGINTERN void appdata_disown_helper(void *appdata) {
+}
+SWIGINTERN void appdata_clr_helper(void **appdatap) {
+ *appdatap = 0;
+}
+SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
+ *appdatap = appdata;
+}
+SWIGINTERN void *appdata_get_helper(void *appdata) {
+ return appdata;
+}
+%}
+
+#elif defined(SWIGTCL)
+
+%{
+SWIGINTERN void appdata_disown_helper(void *appdata) {
+}
+SWIGINTERN void appdata_clr_helper(void **appdatap) {
+ if (*appdatap)
+ Tcl_DecrRefCount((Tcl_Obj *)(*appdatap));
+ *appdatap = 0;
+}
+SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
+ appdata_clr_helper(appdatap);
+ *appdatap = appdata;
+}
+SWIGINTERN void *appdata_get_helper(void *appdata) {
+ return appdata;
+}
+%}
+
+#elif defined(SWIGPYTHON)
+
+%{
+SWIGINTERN void appdata_disown_helper(void *appdata) {
+ struct myappdata *myappdata = appdata;
+ if (!myappdata || !myappdata->appdata || myappdata->disowned)
+ return;
+ myappdata->disowned = 1;
+ Py_DECREF((PyObject *)myappdata->appdata);
+}
+SWIGINTERN void appdata_clr_helper(void **appdatap) {
+ struct myappdata *myappdata = *(struct myappdata **)appdatap;
+ if (myappdata && myappdata->appdata && !myappdata->disowned) {
+ Py_DECREF((PyObject *)myappdata->appdata);
+ }
+ *appdatap = solv_free(myappdata);
+}
+SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
+ appdata_clr_helper(appdatap);
+ if (appdata) {
+ struct myappdata *myappdata = *appdatap = solv_calloc(sizeof(struct myappdata), 1);
+ myappdata->appdata = appdata;
+ }
+}
+SWIGINTERN void *appdata_get_helper(void *appdata) {
+ return appdata ? ((struct myappdata *)appdata)->appdata : 0;
+}
+
+%}
+
+#elif defined(SWIGPERL)
+
+%{
+SWIGINTERN void appdata_disown_helper(void *appdata) {
+ struct myappdata *myappdata = appdata;
+ SV *rsv;
+ if (!myappdata || !myappdata->appdata || myappdata->disowned)
+ return;
+ rsv = myappdata->appdata;
+ if (!SvROK(rsv))
+ return;
+ myappdata->appdata = SvRV(rsv);
+ myappdata->disowned = 1;
+ SvREFCNT_dec(rsv);
+}
+SWIGINTERN void appdata_clr_helper(void **appdatap) {
+ struct myappdata *myappdata = *(struct myappdata **)appdatap;
+ if (myappdata && myappdata->appdata && !myappdata->disowned) {
+ SvREFCNT_dec((SV *)myappdata->appdata);
+ }
+ *appdatap = solv_free(myappdata);
+}
+SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
+ appdata_clr_helper(appdatap);
+ if (appdata) {
+ struct myappdata *myappdata = *appdatap = solv_calloc(sizeof(struct myappdata), 1);
+ myappdata->appdata = appdata;
+ }
+}
+SWIGINTERN void *appdata_get_helper(void *appdata) {
+ struct myappdata *myappdata = appdata;
+ if (!myappdata || !myappdata->appdata)
+ return 0;
+ return myappdata->disowned ? newRV_noinc((SV *)myappdata->appdata) : myappdata->appdata;
+}
+
+%}
+
+#else
+#warning appdata helpers not implemented for this language
+#endif
+
+
+/**
+ ** the SWIG declarations defining the API
+ **/
+
+#ifdef SWIGRUBY
+%mixin Dataiterator "Enumerable";
+%mixin Pool_solvable_iterator "Enumerable";
+%mixin Pool_repo_iterator "Enumerable";
+%mixin Repo_solvable_iterator "Enumerable";
+#endif
+
+typedef int Id;
+
+%include "knownid.h"
+
+/* from repodata.h */
+%constant Id SOLVID_META;
+%constant Id SOLVID_POS;
+
+%constant int REL_EQ;
+%constant int REL_GT;
+%constant int REL_LT;
+%constant int REL_ARCH;
+%constant int REL_AND;
+%constant int REL_OR;
+%constant int REL_WITH;
+%constant int REL_COND;
+%constant int REL_ELSE;
+
+typedef struct {
+ Pool* const pool;
+} Selection;
+
+typedef struct {
+ Pool* const pool;
+ Id const id;
+} Dep;
+
+/* put before pool/repo so we can access the constructor */
+%nodefaultdtor Dataiterator;
+typedef struct {} Dataiterator;
+
+typedef struct {
+ Pool* const pool;
+ Id const id;
+} XSolvable;
+
+typedef struct {
+ Solver* const solv;
+ Id const type;
+ Id const dep_id;
+} Ruleinfo;
+
+typedef struct {
+ Solver* const solv;
+ Id const id;
+} XRule;
+
+typedef struct {
+ Repo* const repo;
+ Id const id;
+} XRepodata;
+
+typedef struct {} Pool_solvable_iterator;
+typedef struct {} Pool_repo_iterator;
+typedef struct {} Repo_solvable_iterator;
+
+%nodefaultctor Datamatch;
+%nodefaultdtor Datamatch;
+typedef struct {
+ Pool * const pool;
+ Repo * const repo;
+ Id const solvid;
+} Datamatch;
+
+%nodefaultctor Datapos;
+typedef struct {
+ Repo * const repo;
+} Datapos;
+
+typedef struct {
+ Pool * const pool;
+ int how;
+ Id what;
+} Job;
+
+%nodefaultctor Pool;
+%nodefaultdtor Pool;
+typedef struct {
+} Pool;
+
+%nodefaultctor Repo;
+%nodefaultdtor Repo;
+typedef struct {
+ Pool * const pool;
+ const char * const name;
+ int priority;
+ int subpriority;
+ int const nsolvables;
+} Repo;
+
+%nodefaultctor Solver;
+%nodefaultdtor Solver;
+typedef struct {
+ Pool * const pool;
+} Solver;
+
+typedef struct {
+} Chksum;
+
+#ifdef ENABLE_PUBKEY
+typedef struct {
+ Id const htype;
+ unsigned int const created;
+ unsigned int const expires;
+ const char * const keyid;
+} Solvsig;
+#endif
+
+%rename(xfopen) solvfp_xfopen;
+%rename(xfopen_fd) solvfp_xfopen_fd;
+
+%nodefaultctor SolvFp;
+typedef struct {
+} SolvFp;
+
+%newobject solvfp_xfopen;
+%newobject solvfp_xfopen_fd;
+
+SolvFp *solvfp_xfopen(const char *fn, const char *mode = 0);
+SolvFp *solvfp_xfopen_fd(const char *fn, int fd, const char *mode = 0);
+
+%{
+ SWIGINTERN SolvFp *solvfp_xfopen_fd(const char *fn, int fd, const char *mode) {
+ SolvFp *sfp;
+ FILE *fp;
+ fd = dup(fd);
+ if (fd == -1)
+ return 0;
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ fp = solv_xfopen_fd(fn, fd, mode);
+ if (!fp) {
+ close(fd);
+ return 0;
+ }
+ sfp = solv_calloc(1, sizeof(SolvFp));
+ sfp->fp = fp;
+ return sfp;
+ }
+ SWIGINTERN SolvFp *solvfp_xfopen(const char *fn, const char *mode) {
+ SolvFp *sfp;
+ FILE *fp;
+ fp = solv_xfopen(fn, mode);
+ if (!fp)
+ return 0;
+ if (fileno(fp) != -1)
+ fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ sfp = solv_calloc(1, sizeof(SolvFp));
+ sfp->fp = fp;
+ return sfp;
+ }
+%}
+
+typedef struct {
+ Solver * const solv;
+ Id const id;
+} Problem;
+
+typedef struct {
+ Solver * const solv;
+ Id const problemid;
+ Id const id;
+} Solution;
+
+typedef struct {
+ Solver *const solv;
+ Id const problemid;
+ Id const solutionid;
+ Id const id;
+ Id const type;
+} Solutionelement;
+
+%nodefaultctor Alternative;
+typedef struct {
+ Solver *const solv;
+ Id const type;
+ Id const rid;
+ Id const from_id;
+ Id const dep_id;
+ Id const chosen_id;
+ int level;
+} Alternative;
+
+%nodefaultctor Transaction;
+%nodefaultdtor Transaction;
+typedef struct {
+ Pool * const pool;
+} Transaction;
+
+typedef struct {
+ Transaction * const transaction;
+ Id const type;
+ Id const fromid;
+ Id const toid;
+ int const count;
+} TransactionClass;
+
+%extend SolvFp {
+ ~SolvFp() {
+ if ($self->fp)
+ fclose($self->fp);
+ free($self);
+ }
+ int fileno() {
+ return $self->fp ? fileno($self->fp) : -1;
+ }
+ int dup() {
+ return $self->fp ? dup(fileno($self->fp)) : -1;
+ }
+ bool write(const unsigned char *str, size_t len) {
+ return fwrite(str, len, 1, $self->fp) == 1;
+ }
+ bool flush() {
+ if (!$self->fp)
+ return 1;
+ return fflush($self->fp) == 0;
+ }
+ bool close() {
+ bool ret;
+ if (!$self->fp)
+ return 1;
+ ret = fclose($self->fp) == 0;
+ $self->fp = 0;
+ return ret;
+ }
+ void cloexec(bool state) {
+ if (!$self->fp || fileno($self->fp) == -1)
+ return;
+ fcntl(fileno($self->fp), F_SETFD, state ? FD_CLOEXEC : 0);
+ }
+}
+
+%extend Job {
+ static const Id SOLVER_SOLVABLE = SOLVER_SOLVABLE;
+ static const Id SOLVER_SOLVABLE_NAME = SOLVER_SOLVABLE_NAME;
+ static const Id SOLVER_SOLVABLE_PROVIDES = SOLVER_SOLVABLE_PROVIDES;
+ static const Id SOLVER_SOLVABLE_ONE_OF = SOLVER_SOLVABLE_ONE_OF;
+ static const Id SOLVER_SOLVABLE_REPO = SOLVER_SOLVABLE_REPO;
+ static const Id SOLVER_SOLVABLE_ALL = SOLVER_SOLVABLE_ALL;
+ static const Id SOLVER_SELECTMASK = SOLVER_SELECTMASK;
+ static const Id SOLVER_NOOP = SOLVER_NOOP;
+ static const Id SOLVER_INSTALL = SOLVER_INSTALL;
+ static const Id SOLVER_ERASE = SOLVER_ERASE;
+ static const Id SOLVER_UPDATE = SOLVER_UPDATE;
+ static const Id SOLVER_WEAKENDEPS = SOLVER_WEAKENDEPS;
+ static const Id SOLVER_MULTIVERSION = SOLVER_MULTIVERSION;
+ static const Id SOLVER_LOCK = SOLVER_LOCK;
+ static const Id SOLVER_DISTUPGRADE = SOLVER_DISTUPGRADE;
+ static const Id SOLVER_VERIFY = SOLVER_VERIFY;
+ static const Id SOLVER_DROP_ORPHANED = SOLVER_DROP_ORPHANED;
+ static const Id SOLVER_USERINSTALLED = SOLVER_USERINSTALLED;
+ static const Id SOLVER_ALLOWUNINSTALL = SOLVER_ALLOWUNINSTALL;
+ static const Id SOLVER_JOBMASK = SOLVER_JOBMASK;
+ static const Id SOLVER_WEAK = SOLVER_WEAK;
+ static const Id SOLVER_ESSENTIAL = SOLVER_ESSENTIAL;
+ static const Id SOLVER_CLEANDEPS = SOLVER_CLEANDEPS;
+ static const Id SOLVER_FORCEBEST = SOLVER_FORCEBEST;
+ static const Id SOLVER_TARGETED = SOLVER_TARGETED;
+ static const Id SOLVER_NOTBYUSER = SOLVER_NOTBYUSER;
+ static const Id SOLVER_SETEV = SOLVER_SETEV;
+ static const Id SOLVER_SETEVR = SOLVER_SETEVR;
+ static const Id SOLVER_SETARCH = SOLVER_SETARCH;
+ static const Id SOLVER_SETVENDOR = SOLVER_SETVENDOR;
+ static const Id SOLVER_SETREPO = SOLVER_SETREPO;
+ static const Id SOLVER_SETNAME = SOLVER_SETNAME;
+ static const Id SOLVER_NOAUTOSET = SOLVER_NOAUTOSET;
+ static const Id SOLVER_SETMASK = SOLVER_SETMASK;
+
+ Job(Pool *pool, int how, Id what) {
+ Job *job = solv_calloc(1, sizeof(*job));
+ job->pool = pool;
+ job->how = how;
+ job->what = what;
+ return job;
+ }
+
+ %typemap(out) Queue solvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+ %newobject solvables;
+ Queue solvables() {
+ Queue q;
+ queue_init(&q);
+ pool_job2solvables($self->pool, &q, $self->how, $self->what);
+ return q;
+ }
+#ifdef SWIGRUBY
+ %rename("isemptyupdate?") isemptyupdate;
+#endif
+ bool isemptyupdate() {
+ return pool_isemptyupdatejob($self->pool, $self->how, $self->what);
+ }
+
+#if defined(SWIGTCL)
+ %rename("==") __eq__;
+#endif
+ bool __eq__(Job *j) {
+ return $self->pool == j->pool && $self->how == j->how && $self->what == j->what;
+ }
+#if defined(SWIGTCL)
+ %rename("!=") __ne__;
+#endif
+ bool __ne__(Job *j) {
+ return !Job___eq__($self, j);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("str") __str__;
+#endif
+ const char *__str__() {
+ return pool_job2str($self->pool, $self->how, $self->what, 0);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("repr") __repr__;
+#endif
+ const char *__repr__() {
+ const char *str = pool_job2str($self->pool, $self->how, $self->what, ~0);
+ return pool_tmpjoin($self->pool, "<Job ", str, ">");
+ }
+}
+
+%extend Selection {
+ static const Id SELECTION_NAME = SELECTION_NAME;
+ static const Id SELECTION_PROVIDES = SELECTION_PROVIDES;
+ static const Id SELECTION_FILELIST = SELECTION_FILELIST;
+ static const Id SELECTION_CANON = SELECTION_CANON;
+ static const Id SELECTION_DOTARCH = SELECTION_DOTARCH;
+ static const Id SELECTION_REL = SELECTION_REL;
+ static const Id SELECTION_INSTALLED_ONLY = SELECTION_INSTALLED_ONLY;
+ static const Id SELECTION_GLOB = SELECTION_GLOB;
+ static const Id SELECTION_FLAT = SELECTION_FLAT;
+ static const Id SELECTION_NOCASE = SELECTION_NOCASE;
+ static const Id SELECTION_SOURCE_ONLY = SELECTION_SOURCE_ONLY;
+ static const Id SELECTION_WITH_SOURCE = SELECTION_WITH_SOURCE;
+
+ Selection(Pool *pool) {
+ Selection *s;
+ s = solv_calloc(1, sizeof(*s));
+ s->pool = pool;
+ return s;
+ }
+
+ ~Selection() {
+ queue_free(&$self->q);
+ solv_free($self);
+ }
+ int flags() {
+ return $self->flags;
+ }
+#ifdef SWIGRUBY
+ %rename("isempty?") isempty;
+#endif
+ bool isempty() {
+ return $self->q.count == 0;
+ }
+ void filter(Selection *lsel) {
+ if ($self->pool != lsel->pool)
+ queue_empty(&$self->q);
+ else
+ selection_filter($self->pool, &$self->q, &lsel->q);
+ }
+ void add(Selection *lsel) {
+ if ($self->pool == lsel->pool)
+ {
+ selection_add($self->pool, &$self->q, &lsel->q);
+ $self->flags |= lsel->flags;
+ }
+ }
+ void add_raw(Id how, Id what) {
+ queue_push2(&$self->q, how, what);
+ }
+ %typemap(out) Queue jobs Queue2Array(Job *, 2, new_Job(arg1->pool, id, idp[1]));
+ %newobject jobs;
+ Queue jobs(int flags) {
+ Queue q;
+ int i;
+ queue_init_clone(&q, &$self->q);
+ for (i = 0; i < q.count; i += 2)
+ q.elements[i] |= flags;
+ return q;
+ }
+
+ %typemap(out) Queue solvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+ %newobject solvables;
+ Queue solvables() {
+ Queue q;
+ queue_init(&q);
+ selection_solvables($self->pool, &$self->q, &q);
+ return q;
+ }
+
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("str") __str__;
+#endif
+ const char *__str__() {
+ return pool_selection2str($self->pool, &$self->q, 0);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("repr") __repr__;
+#endif
+ const char *__repr__() {
+ const char *str = pool_selection2str($self->pool, &$self->q, ~0);
+ return pool_tmpjoin($self->pool, "<Selection ", str, ">");
+ }
+}
+
+%extend Chksum {
+ Chksum(Id type) {
+ return solv_chksum_create(type);
+ }
+ Chksum(Id type, const char *hex) {
+ unsigned char buf[64];
+ int l = solv_chksum_len(type);
+ if (!l)
+ return 0;
+ if (solv_hex2bin(&hex, buf, sizeof(buf)) != l || hex[0])
+ return 0;
+ return solv_chksum_create_from_bin(type, buf);
+ }
+ %newobject from_bin;
+ static Chksum *from_bin(Id type, const unsigned char *str, size_t len) {
+ return len == solv_chksum_len(type) ? solv_chksum_create_from_bin(type, str) : 0;
+ }
+#if defined(SWIGPERL)
+ %perlcode {
+ undef *solv::Chksum::from_bin;
+ *solv::Chksum::from_bin = sub {
+ my $pkg = shift;
+ my $self = solvc::Chksum_from_bin(@_);
+ bless $self, $pkg if defined $self;
+ };
+ }
+#endif
+ ~Chksum() {
+ solv_chksum_free($self, 0);
+ }
+ Id const type;
+ %{
+ SWIGINTERN Id Chksum_type_get(Chksum *chk) {
+ return solv_chksum_get_type(chk);
+ }
+ %}
+ void add(const unsigned char *str, size_t len) {
+ solv_chksum_add($self, str, (int)len);
+ }
+ void add_fp(FILE *fp) {
+ char buf[4096];
+ int l;
+ while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
+ solv_chksum_add($self, buf, l);
+ rewind(fp); /* convenience */
+ }
+ void add_fd(int fd) {
+ char buf[4096];
+ int l;
+ while ((l = read(fd, buf, sizeof(buf))) > 0)
+ solv_chksum_add($self, buf, l);
+ lseek(fd, 0, 0); /* convenience */
+ }
+ void add_stat(const char *filename) {
+ struct stat stb;
+ if (stat(filename, &stb))
+ memset(&stb, 0, sizeof(stb));
+ solv_chksum_add($self, &stb.st_dev, sizeof(stb.st_dev));
+ solv_chksum_add($self, &stb.st_ino, sizeof(stb.st_ino));
+ solv_chksum_add($self, &stb.st_size, sizeof(stb.st_size));
+ solv_chksum_add($self, &stb.st_mtime, sizeof(stb.st_mtime));
+ }
+ void add_fstat(int fd) {
+ struct stat stb;
+ if (fstat(fd, &stb))
+ memset(&stb, 0, sizeof(stb));
+ solv_chksum_add($self, &stb.st_dev, sizeof(stb.st_dev));
+ solv_chksum_add($self, &stb.st_ino, sizeof(stb.st_ino));
+ solv_chksum_add($self, &stb.st_size, sizeof(stb.st_size));
+ solv_chksum_add($self, &stb.st_mtime, sizeof(stb.st_mtime));
+ }
+ BinaryBlob raw() {
+ BinaryBlob bl;
+ int l;
+ const unsigned char *b;
+ b = solv_chksum_get($self, &l);
+ bl.data = b;
+ bl.len = l;
+ return bl;
+ }
+ %newobject hex;
+ char *hex() {
+ int l;
+ const unsigned char *b;
+ char *ret;
+
+ b = solv_chksum_get($self, &l);
+ ret = solv_malloc(2 * l + 1);
+ solv_bin2hex(b, l, ret);
+ return ret;
+ }
+ const char *typestr() {
+ return solv_chksum_type2str(solv_chksum_get_type($self));
+ }
+
+#if defined(SWIGTCL)
+ %rename("==") __eq__;
+#endif
+ bool __eq__(Chksum *chk) {
+ return solv_chksum_cmp($self, chk);
+ }
+#if defined(SWIGTCL)
+ %rename("!=") __ne__;
+#endif
+ bool __ne__(Chksum *chk) {
+ return !solv_chksum_cmp($self, chk);
+ }
+#if defined(SWIGRUBY)
+ %rename("to_s") __str__;
+#endif
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("str") __str__;
+#endif
+ %newobject __str__;
+ const char *__str__() {
+ const char *str;
+ const char *h = 0;
+ if (solv_chksum_isfinished($self))
+ h = Chksum_hex($self);
+ str = solv_dupjoin(solv_chksum_type2str(solv_chksum_get_type($self)), ":", h ? h : "unfinished");
+ solv_free((void *)h);
+ return str;
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("repr") __repr__;
+#endif
+ %newobject __repr__;
+ const char *__repr__() {
+ const char *h = Chksum___str__($self);
+ const char *str = solv_dupjoin("<Chksum ", h, ">");
+ solv_free((void *)h);
+ return str;
+ }
+}
+
+%extend Pool {
+ static const int POOL_FLAG_PROMOTEEPOCH = POOL_FLAG_PROMOTEEPOCH;
+ static const int POOL_FLAG_FORBIDSELFCONFLICTS = POOL_FLAG_FORBIDSELFCONFLICTS;
+ static const int POOL_FLAG_OBSOLETEUSESPROVIDES = POOL_FLAG_OBSOLETEUSESPROVIDES;
+ static const int POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES = POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES;
+ static const int POOL_FLAG_OBSOLETEUSESCOLORS = POOL_FLAG_OBSOLETEUSESCOLORS;
+ static const int POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS = POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS;
+ static const int POOL_FLAG_NOINSTALLEDOBSOLETES = POOL_FLAG_NOINSTALLEDOBSOLETES;
+ static const int POOL_FLAG_HAVEDISTEPOCH = POOL_FLAG_HAVEDISTEPOCH;
+ static const int POOL_FLAG_NOOBSOLETESMULTIVERSION = POOL_FLAG_NOOBSOLETESMULTIVERSION;
+
+ Pool() {
+ Pool *pool = pool_create();
+ return pool;
+ }
+ void set_debuglevel(int level) {
+ pool_setdebuglevel($self, level);
+ }
+ int set_flag(int flag, int value) {
+ return pool_set_flag($self, flag, value);
+ }
+ int get_flag(int flag) {
+ return pool_get_flag($self, flag);
+ }
+ void set_rootdir(const char *rootdir) {
+ pool_set_rootdir($self, rootdir);
+ }
+ const char *get_rootdir(int flag) {
+ return pool_get_rootdir($self);
+ }
+#if defined(SWIGPYTHON)
+ %{
+ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
+ XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
+ PyObject *args = Py_BuildValue("(O)", SWIG_NewPointerObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, SWIG_POINTER_OWN | 0));
+ PyObject *result = PyEval_CallObject((PyObject *)d, args);
+ int ecode = 0;
+ int vresult = 0;
+ Py_DECREF(args);
+ if (!result)
+ return 0; /* exception */
+ ecode = SWIG_AsVal_int(result, &vresult);
+ Py_DECREF(result);
+ return SWIG_IsOK(ecode) ? vresult : 0;
+ }
+ %}
+ void clr_loadcallback() {
+ if ($self->loadcallback == loadcallback) {
+ PyObject *obj = $self->loadcallbackdata;
+ Py_DECREF(obj);
+ pool_setloadcallback($self, 0, 0);
+ }
+ }
+ void set_loadcallback(PyObject *callable) {
+ Pool_clr_loadcallback($self);
+ if (callable) {
+ Py_INCREF(callable);
+ pool_setloadcallback($self, loadcallback, callable);
+ }
+ }
+#elif defined(SWIGPERL)
+%{
+ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
+ int count;
+ int ret = 0;
+ dSP;
+ XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(SWIG_NewPointerObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, SWIG_OWNER | SWIG_SHADOW));
+ PUTBACK;
+ count = perl_call_sv((SV *)d, G_EVAL|G_SCALAR);
+ SPAGAIN;
+ if (count)
+ ret = POPi;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ return ret;
+ }
+%}
+ void clr_loadcallback() {
+ if ($self->loadcallback == loadcallback) {
+ SvREFCNT_dec($self->loadcallbackdata);
+ pool_setloadcallback($self, 0, 0);
+ }
+ }
+ void set_loadcallback(SV *callable) {
+ Pool_clr_loadcallback($self);
+ if (callable) {
+ SvREFCNT_inc(callable);
+ pool_setloadcallback($self, loadcallback, callable);
+ }
+ }
+#elif defined(SWIGRUBY)
+%{
+ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
+ XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
+ VALUE callable = (VALUE)d;
+ VALUE rd = SWIG_NewPointerObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, SWIG_POINTER_OWN | 0);
+ VALUE res = rb_funcall(callable, rb_intern("call"), 1, rd);
+ return res == Qtrue;
+ }
+ SWIGINTERN void mark_Pool(void *ptr) {
+ Pool *pool = ptr;
+ if (pool->loadcallback == loadcallback && pool->loadcallbackdata) {
+ VALUE callable = (VALUE)pool->loadcallbackdata;
+ rb_gc_mark(callable);
+ }
+ }
+%}
+ void clr_loadcallback() {
+ pool_setloadcallback($self, 0, 0);
+ }
+ %typemap(in, numinputs=0) VALUE callable {
+ $1 = rb_block_given_p() ? rb_block_proc() : 0;
+ }
+ void set_loadcallback(VALUE callable) {
+ pool_setloadcallback($self, callable ? loadcallback : 0, (void *)callable);
+ }
+#elif defined(SWIGTCL)
+ %{
+ typedef struct {
+ Tcl_Interp *interp;
+ Tcl_Obj *obj;
+ } tcl_callback_t;
+ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
+ tcl_callback_t *callback_var = (tcl_callback_t *)d;
+ Tcl_Interp *interp = callback_var->interp;
+ XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
+ int result, ecode = 0, vresult = 0;
+ Tcl_Obj *objvx[2];
+ objvx[0] = callback_var->obj;
+ objvx[1] = SWIG_NewInstanceObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, 0);
+ Tcl_IncrRefCount(objvx[1]);
+ result = Tcl_EvalObjv(interp, sizeof(objvx)/sizeof(*objvx), objvx, TCL_EVAL_GLOBAL);
+ Tcl_DecrRefCount(objvx[1]);
+ if (result != TCL_OK)
+ return 0; /* exception */
+ ecode = SWIG_AsVal_int(interp, Tcl_GetObjResult(interp), &vresult);
+ return SWIG_IsOK(ecode) ? vresult : 0;
+ }
+ %}
+ void clr_loadcallback() {
+ if ($self->loadcallback == loadcallback) {
+ tcl_callback_t *callback_var = $self->loadcallbackdata;
+ Tcl_DecrRefCount(callback_var->obj);
+ solv_free(callback_var);
+ pool_setloadcallback($self, 0, 0);
+ }
+ }
+ void set_loadcallback(Tcl_Obj *callable, Tcl_Interp *interp) {
+ Pool_clr_loadcallback($self);
+ if (callable) {
+ tcl_callback_t *callback_var = solv_malloc(sizeof(tcl_callback_t));
+ Tcl_IncrRefCount(callable);
+ callback_var->interp = interp;
+ callback_var->obj = callable;
+ pool_setloadcallback($self, loadcallback, callback_var);
+ }
+ }
+#else
+#warning loadcallback not implemented for this language
+#endif
+
+ ~Pool() {
+ Pool *pool = $self;
+ Id repoid;
+ Repo *repo;
+ FOR_REPOS(repoid, repo)
+ appdata_clr_helper(&repo->appdata);
+ Pool_clr_loadcallback(pool);
+ appdata_clr_helper(&pool->appdata);
+ pool_free(pool);
+ }
+ disown_helper free() {
+ Pool *pool = $self;
+ Id repoid;
+ Repo *repo;
+ FOR_REPOS(repoid, repo)
+ appdata_clr_helper(&repo->appdata);
+ Pool_clr_loadcallback(pool);
+ appdata_clr_helper(&pool->appdata);
+ pool_free(pool);
+ return 0;
+ }
+ disown_helper disown() {
+ return 0;
+ }
+ AppObjectPtr appdata;
+ %{
+ SWIGINTERN void Pool_appdata_set(Pool *pool, AppObjectPtr appdata) {
+ appdata_set_helper(&pool->appdata, appdata);
+ }
+ SWIGINTERN AppObjectPtr Pool_appdata_get(Pool *pool) {
+ return appdata_get_helper(pool->appdata);
+ }
+ %}
+ void appdata_disown() {
+ appdata_disown_helper($self->appdata);
+ }
+
+ Id str2id(const char *str, bool create=1) {
+ return pool_str2id($self, str, create);
+ }
+ %newobject Dep;
+ Dep *Dep(const char *str, bool create=1) {
+ Id id = pool_str2id($self, str, create);
+ return new_Dep($self, id);
+ }
+ const char *id2str(Id id) {
+ return pool_id2str($self, id);
+ }
+ const char *dep2str(Id id) {
+ return pool_dep2str($self, id);
+ }
+ Id rel2id(Id name, Id evr, int flags, bool create=1) {
+ return pool_rel2id($self, name, evr, flags, create);
+ }
+ Id id2langid(Id id, const char *lang, bool create=1) {
+ return pool_id2langid($self, id, lang, create);
+ }
+ void setarch(const char *arch = 0) {
+ struct utsname un;
+ if (!arch) {
+ if (uname(&un)) {
+ perror("uname");
+ return;
+ }
+ arch = un.machine;
+ }
+ pool_setarch($self, arch);
+ }
+ Repo *add_repo(const char *name) {
+ return repo_create($self, name);
+ }
+ const char *lookup_str(Id entry, Id keyname) {
+ return pool_lookup_str($self, entry, keyname);
+ }
+ Id lookup_id(Id entry, Id keyname) {
+ return pool_lookup_id($self, entry, keyname);
+ }
+ unsigned long long lookup_num(Id entry, Id keyname, unsigned long long notfound = 0) {
+ return pool_lookup_num($self, entry, keyname, notfound);
+ }
+ bool lookup_void(Id entry, Id keyname) {
+ return pool_lookup_void($self, entry, keyname);
+ }
+ %newobject lookup_checksum;
+ Chksum *lookup_checksum(Id entry, Id keyname) {
+ Id type = 0;
+ const unsigned char *b = pool_lookup_bin_checksum($self, entry, keyname, &type);
+ return solv_chksum_create_from_bin(type, b);
+ }
+
+ %newobject Dataiterator;
+ Dataiterator *Dataiterator(Id key, const char *match = 0, int flags = 0) {
+ return new_Dataiterator($self, 0, 0, key, match, flags);
+ }
+ %newobject Dataiterator_solvid;
+ Dataiterator *Dataiterator_solvid(Id p, Id key, const char *match = 0, int flags = 0) {
+ return new_Dataiterator($self, 0, p, key, match, flags);
+ }
+ const char *solvid2str(Id solvid) {
+ return pool_solvid2str($self, solvid);
+ }
+ void addfileprovides() {
+ pool_addfileprovides($self);
+ }
+ Queue addfileprovides_queue() {
+ Queue r;
+ queue_init(&r);
+ pool_addfileprovides_queue($self, &r, 0);
+ return r;
+ }
+ void createwhatprovides() {
+ pool_createwhatprovides($self);
+ }
+
+ %newobject id2solvable;
+ XSolvable *id2solvable(Id id) {
+ return new_XSolvable($self, id);
+ }
+ %newobject solvables;
+ Pool_solvable_iterator * const solvables;
+ %{
+ SWIGINTERN Pool_solvable_iterator * Pool_solvables_get(Pool *pool) {
+ return new_Pool_solvable_iterator(pool);
+ }
+ %}
+ %newobject solvables_iter;
+ Pool_solvable_iterator * solvables_iter() {
+ return new_Pool_solvable_iterator($self);
+ }
+
+ Repo *id2repo(Id id) {
+ if (id < 1 || id >= $self->nrepos)
+ return 0;
+ return pool_id2repo($self, id);
+ }
+
+ %newobject repos;
+ Pool_repo_iterator * const repos;
+ %{
+ SWIGINTERN Pool_repo_iterator * Pool_repos_get(Pool *pool) {
+ return new_Pool_repo_iterator(pool);
+ }
+ %}
+ %newobject repos_iter;
+ Pool_repo_iterator * repos_iter() {
+ return new_Pool_repo_iterator($self);
+ }
+
+ Repo *installed;
+ const char * const errstr;
+ %{
+ SWIGINTERN void Pool_installed_set(Pool *pool, Repo *installed) {
+ pool_set_installed(pool, installed);
+ }
+ SWIGINTERN Repo *Pool_installed_get(Pool *pool) {
+ return pool->installed;
+ }
+ SWIGINTERN const char *Pool_errstr_get(Pool *pool) {
+ return pool_errstr(pool);
+ }
+ %}
+
+ Queue matchprovidingids(const char *match, int flags) {
+ Pool *pool = $self;
+ Queue q;
+ Id id;
+ queue_init(&q);
+ if (!flags) {
+ for (id = 1; id < pool->ss.nstrings; id++)
+ if (pool->whatprovides[id])
+ queue_push(&q, id);
+ } else {
+ Datamatcher ma;
+ if (!datamatcher_init(&ma, match, flags)) {
+ for (id = 1; id < pool->ss.nstrings; id++)
+ if (pool->whatprovides[id] && datamatcher_match(&ma, pool_id2str(pool, id)))
+ queue_push(&q, id);
+ datamatcher_free(&ma);
+ }
+ }
+ return q;
+ }
+
+ %newobject Job;
+ Job *Job(int how, Id what) {
+ return new_Job($self, how, what);
+ }
+
+ %typemap(out) Queue whatprovides Queue2Array(XSolvable *, 1, new_XSolvable(arg1, id));
+ %newobject whatprovides;
+ Queue whatprovides(DepId dep) {
+ Pool *pool = $self;
+ Queue q;
+ Id p, pp;
+ queue_init(&q);
+ FOR_PROVIDES(p, pp, dep)
+ queue_push(&q, p);
+ return q;
+ }
+
+ Id towhatprovides(Queue q) {
+ return pool_queuetowhatprovides($self, &q);
+ }
+
+ %typemap(out) Queue whatmatchesdep Queue2Array(XSolvable *, 1, new_XSolvable(arg1, id));
+ %newobject whatmatchesdep;
+ Queue whatmatchesdep(Id keyname, DepId dep, Id marker = -1) {
+ Queue q;
+ queue_init(&q);
+ pool_whatmatchesdep($self, keyname, dep, &q, marker);
+ return q;
+ }
+
+#ifdef SWIGRUBY
+ %rename("isknownarch?") isknownarch;
+#endif
+ bool isknownarch(DepId id) {
+ Pool *pool = $self;
+ if (!id || id == ID_EMPTY)
+ return 0;
+ if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
+ return 1;
+ if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
+ return 0;
+ return 1;
+ }
+
+ %newobject Solver;
+ Solver *Solver() {
+ return solver_create($self);
+ }
+
+ %newobject Selection;
+ Selection *Selection() {
+ return new_Selection($self);
+ }
+ %newobject Selection_all;
+ Selection *Selection_all(int setflags=0) {
+ Selection *sel = new_Selection($self);
+ queue_push2(&sel->q, SOLVER_SOLVABLE_ALL | setflags, 0);
+ return sel;
+ }
+ %newobject select;
+ Selection *select(const char *name, int flags) {
+ Selection *sel = new_Selection($self);
+ sel->flags = selection_make($self, &sel->q, name, flags);
+ return sel;
+ }
+
+ void setpooljobs_helper(Queue jobs) {
+ queue_free(&$self->pooljobs);
+ queue_init_clone(&$self->pooljobs, &jobs);
+ }
+ %typemap(out) Queue getpooljobs Queue2Array(Job *, 2, new_Job(arg1, id, idp[1]));
+ %newobject getpooljobs;
+ Queue getpooljobs() {
+ Queue q;
+ queue_init_clone(&q, &$self->pooljobs);
+ return q;
+ }
+
+#if defined(SWIGPYTHON)
+ %pythoncode {
+ def setpooljobs(self, jobs):
+ j = []
+ for job in jobs: j += [job.how, job.what]
+ self.setpooljobs_helper(j)
+ }
+#endif
+#if defined(SWIGPERL)
+ %perlcode {
+ sub solv::Solver::setpooljobs {
+ my ($self, $jobs) = @_;
+ my @j = map {($_->{'how'}, $_->{'what'})} @$jobs;
+ return $self->setpooljobs_helper(\@j);
+ }
+ }
+#endif
+#if defined(SWIGRUBY)
+%init %{
+rb_eval_string(
+ "class Solv::Pool\n"
+ " def setpooljobs(jobs)\n"
+ " jl = []\n"
+ " jobs.each do |j| ; jl << j.how << j.what ; end\n"
+ " setpooljobs_helper(jl)\n"
+ " end\n"
+ "end\n"
+ );
+%}
+#endif
+}
+
+%extend Repo {
+ static const int REPO_REUSE_REPODATA = REPO_REUSE_REPODATA;
+ static const int REPO_NO_INTERNALIZE = REPO_NO_INTERNALIZE;
+ static const int REPO_LOCALPOOL = REPO_LOCALPOOL;
+ static const int REPO_USE_LOADING = REPO_USE_LOADING;
+ static const int REPO_EXTEND_SOLVABLES = REPO_EXTEND_SOLVABLES;
+ static const int REPO_USE_ROOTDIR = REPO_USE_ROOTDIR;
+ static const int REPO_NO_LOCATION = REPO_NO_LOCATION;
+ static const int SOLV_ADD_NO_STUBS = SOLV_ADD_NO_STUBS; /* repo_solv */
+#ifdef ENABLE_SUSEREPO
+ static const int SUSETAGS_RECORD_SHARES = SUSETAGS_RECORD_SHARES; /* repo_susetags */
+#endif
+
+ void free(bool reuseids = 0) {
+ appdata_clr_helper(&$self->appdata);
+ repo_free($self, reuseids);
+ }
+ void empty(bool reuseids = 0) {
+ repo_empty($self, reuseids);
+ }
+#ifdef SWIGRUBY
+ %rename("isempty?") isempty;
+#endif
+ bool isempty() {
+ return !$self->nsolvables;
+ }
+
+ AppObjectPtr appdata;
+ %{
+ SWIGINTERN void Repo_appdata_set(Repo *repo, AppObjectPtr appdata) {
+ appdata_set_helper(&repo->appdata, appdata);
+ }
+ SWIGINTERN AppObjectPtr Repo_appdata_get(Repo *repo) {
+ return appdata_get_helper(repo->appdata);
+ }
+ %}
+
+ bool add_solv(const char *name, int flags = 0) {
+ FILE *fp = fopen(name, "r");
+ int r;
+ if (!fp)
+ return 0;
+ r = repo_add_solv($self, fp, flags);
+ fclose(fp);
+ return r == 0;
+ }
+ bool add_solv(FILE *fp, int flags = 0) {
+ return repo_add_solv($self, fp, flags) == 0;
+ }
+
+ %newobject add_solvable;
+ XSolvable *add_solvable() {
+ Id solvid = repo_add_solvable($self);
+ return new_XSolvable($self->pool, solvid);
+ }
+
+#ifdef ENABLE_RPMDB
+ bool add_rpmdb(int flags = 0) {
+ return repo_add_rpmdb($self, 0, flags) == 0;
+ }
+ bool add_rpmdb_reffp(FILE *reffp, int flags = 0) {
+ return repo_add_rpmdb_reffp($self, reffp, flags) == 0;
+ }
+ %newobject add_rpm;
+ XSolvable *add_rpm(const char *name, int flags = 0) {
+ return new_XSolvable($self->pool, repo_add_rpm($self, name, flags));
+ }
+#endif
+#ifdef ENABLE_PUBKEY
+#ifdef ENABLE_RPMDB
+ bool add_rpmdb_pubkeys(int flags = 0) {
+ return repo_add_rpmdb_pubkeys($self, flags) == 0;
+ }
+#endif
+ %newobject add_pubkey;
+ XSolvable *add_pubkey(const char *keyfile, int flags = 0) {
+ return new_XSolvable($self->pool, repo_add_pubkey($self, keyfile, flags));
+ }
+ bool add_keyring(FILE *fp, int flags = 0) {
+ return repo_add_keyring($self, fp, flags);
+ }
+ bool add_keydir(const char *keydir, const char *suffix, int flags = 0) {
+ return repo_add_keydir($self, keydir, suffix, flags);
+ }
+#endif
+#ifdef ENABLE_RPMMD
+ bool add_rpmmd(FILE *fp, const char *language, int flags = 0) {
+ return repo_add_rpmmd($self, fp, language, flags) == 0;
+ }
+ bool add_repomdxml(FILE *fp, int flags = 0) {
+ return repo_add_repomdxml($self, fp, flags) == 0;
+ }
+ bool add_updateinfoxml(FILE *fp, int flags = 0) {
+ return repo_add_updateinfoxml($self, fp, flags) == 0;
+ }
+ bool add_deltainfoxml(FILE *fp, int flags = 0) {
+ return repo_add_deltainfoxml($self, fp, flags) == 0;
+ }
+#endif
+#ifdef ENABLE_DEBIAN
+ bool add_debdb(int flags = 0) {
+ return repo_add_debdb($self, flags) == 0;
+ }
+ bool add_debpackages(FILE *fp, int flags = 0) {
+ return repo_add_debpackages($self, fp, flags) == 0;
+ }
+ %newobject add_deb;
+ XSolvable *add_deb(const char *name, int flags = 0) {
+ return new_XSolvable($self->pool, repo_add_deb($self, name, flags));
+ }
+#endif
+#ifdef ENABLE_SUSEREPO
+ bool add_susetags(FILE *fp, Id defvendor, const char *language, int flags = 0) {
+ return repo_add_susetags($self, fp, defvendor, language, flags) == 0;
+ }
+ bool add_content(FILE *fp, int flags = 0) {
+ return repo_add_content($self, fp, flags) == 0;
+ }
+ bool add_products(const char *proddir, int flags = 0) {
+ return repo_add_products($self, proddir, flags) == 0;
+ }
+#endif
+#ifdef ENABLE_MDKREPO
+ bool add_mdk(FILE *fp, int flags = 0) {
+ return repo_add_mdk($self, fp, flags) == 0;
+ }
+ bool add_mdk_info(FILE *fp, int flags = 0) {
+ return repo_add_mdk_info($self, fp, flags) == 0;
+ }
+#endif
+#ifdef ENABLE_ARCHREPO
+ bool add_arch_repo(FILE *fp, int flags = 0) {
+ return repo_add_arch_repo($self, fp, flags) == 0;
+ }
+ bool add_arch_local(const char *dir, int flags = 0) {
+ return repo_add_arch_local($self, dir, flags) == 0;
+ }
+ %newobject add_arch_pkg;
+ XSolvable *add_arch_pkg(const char *name, int flags = 0) {
+ return new_XSolvable($self->pool, repo_add_arch_pkg($self, name, flags));
+ }
+#endif
+#ifdef SUSE
+ bool add_autopattern(int flags = 0) {
+ return repo_add_autopattern($self, flags) == 0;
+ }
+#endif
+ void internalize() {
+ repo_internalize($self);
+ }
+ bool write(FILE *fp) {
+ return repo_write($self, fp) == 0;
+ }
+ /* HACK, remove if no longer needed! */
+ bool write_first_repodata(FILE *fp) {
+ int oldnrepodata = $self->nrepodata;
+ int res;
+ $self->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata;
+ res = repo_write($self, fp);
+ $self->nrepodata = oldnrepodata;
+ return res == 0;
+ }
+
+ %newobject Dataiterator;
+ Dataiterator *Dataiterator(Id key, const char *match = 0, int flags = 0) {
+ return new_Dataiterator($self->pool, $self, 0, key, match, flags);
+ }
+ %newobject Dataiterator_meta;
+ Dataiterator *Dataiterator_meta(Id key, const char *match = 0, int flags = 0) {
+ return new_Dataiterator($self->pool, $self, SOLVID_META, key, match, flags);
+ }
+
+ Id const id;
+ %{
+ SWIGINTERN Id Repo_id_get(Repo *repo) {
+ return repo->repoid;
+ }
+ %}
+ %newobject solvables;
+ Repo_solvable_iterator * const solvables;
+ %{
+ SWIGINTERN Repo_solvable_iterator * Repo_solvables_get(Repo *repo) {
+ return new_Repo_solvable_iterator(repo);
+ }
+ %}
+ %newobject meta;
+ Datapos * const meta;
+ %{
+ SWIGINTERN Datapos * Repo_meta_get(Repo *repo) {
+ Datapos *pos = solv_calloc(1, sizeof(*pos));
+ pos->solvid = SOLVID_META;
+ pos->repo = repo;
+ return pos;
+ }
+ %}
+
+ %newobject solvables_iter;
+ Repo_solvable_iterator *solvables_iter() {
+ return new_Repo_solvable_iterator($self);
+ }
+
+ %newobject add_repodata;
+ XRepodata *add_repodata(int flags = 0) {
+ Repodata *rd = repo_add_repodata($self, flags);
+ return new_XRepodata($self, rd->repodataid);
+ }
+
+ void create_stubs() {
+ Repodata *data;
+ if (!$self->nrepodata)
+ return;
+ data = repo_id2repodata($self, $self->nrepodata - 1);
+ if (data->state != REPODATA_STUB)
+ (void)repodata_create_stubs(data);
+ }
+#ifdef SWIGRUBY
+ %rename("iscontiguous?") iscontiguous;
+#endif
+ bool iscontiguous() {
+ int i;
+ for (i = $self->start; i < $self->end; i++)
+ if ($self->pool->solvables[i].repo != $self)
+ return 0;
+ return 1;
+ }
+ %newobject first_repodata;
+ XRepodata *first_repodata() {
+ Repodata *data;
+ int i;
+ if ($self->nrepodata < 2)
+ return 0;
+ /* make sure all repodatas but the first are extensions */
+ data = repo_id2repodata($self, 1);
+ if (data->loadcallback)
+ return 0;
+ for (i = 2; i < $self->nrepodata; i++)
+ {
+ data = repo_id2repodata($self, i);
+ if (!data->loadcallback)
+ return 0; /* oops, not an extension */
+ }
+ return new_XRepodata($self, 1);
+ }
+
+ %newobject Selection;
+ Selection *Selection(int setflags=0) {
+ Selection *sel = new_Selection($self->pool);
+ setflags |= SOLVER_SETREPO;
+ queue_push2(&sel->q, SOLVER_SOLVABLE_REPO | setflags, $self->repoid);
+ return sel;
+ }
+
+#ifdef ENABLE_PUBKEY
+ %newobject find_pubkey;
+ XSolvable *find_pubkey(const char *keyid) {
+ return new_XSolvable($self->pool, repo_find_pubkey($self, keyid));
+ }
+#endif
+
+#if defined(SWIGTCL)
+ %rename("==") __eq__;
+#endif
+ bool __eq__(Repo *repo) {
+ return $self == repo;
+ }
+#if defined(SWIGTCL)
+ %rename("!=") __ne__;
+#endif
+ bool __ne__(Repo *repo) {
+ return $self != repo;
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("str") __str__;
+#endif
+ %newobject __str__;
+ const char *__str__() {
+ char buf[20];
+ if ($self->name)
+ return solv_strdup($self->name);
+ sprintf(buf, "Repo#%d", $self->repoid);
+ return solv_strdup(buf);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("repr") __repr__;
+#endif
+ %newobject __repr__;
+ const char *__repr__() {
+ char buf[20];
+ if ($self->name)
+ {
+ sprintf(buf, "<Repo #%d ", $self->repoid);
+ return solv_dupjoin(buf, $self->name, ">");
+ }
+ sprintf(buf, "<Repo #%d>", $self->repoid);
+ return solv_strdup(buf);
+ }
+}
+
+%extend Dataiterator {
+ static const int SEARCH_STRING = SEARCH_STRING;
+ static const int SEARCH_STRINGSTART = SEARCH_STRINGSTART;
+ static const int SEARCH_STRINGEND = SEARCH_STRINGEND;
+ static const int SEARCH_SUBSTRING = SEARCH_SUBSTRING;
+ static const int SEARCH_GLOB = SEARCH_GLOB;
+ static const int SEARCH_REGEX = SEARCH_REGEX;
+ static const int SEARCH_NOCASE = SEARCH_NOCASE;
+ static const int SEARCH_FILES = SEARCH_FILES;
+ static const int SEARCH_COMPLETE_FILELIST = SEARCH_COMPLETE_FILELIST;
+ static const int SEARCH_CHECKSUMS = SEARCH_CHECKSUMS;
+
+ Dataiterator(Pool *pool, Repo *repo, Id p, Id key, const char *match, int flags) {
+ Dataiterator *di = solv_calloc(1, sizeof(*di));
+ dataiterator_init(di, pool, repo, p, key, match, flags);
+ return di;
+ }
+ ~Dataiterator() {
+ dataiterator_free($self);
+ solv_free($self);
+ }
+#if defined(SWIGPYTHON)
+ %pythoncode {
+ def __iter__(self): return self
+ }
+#ifndef PYTHON3
+ %rename("next") __next__();
+#endif
+ %exception __next__ {
+ $action
+ if (!result) {
+ PyErr_SetString(PyExc_StopIteration,"no more matches");
+ return NULL;
+ }
+ }
+#endif
+#ifdef SWIGPERL
+ perliter(solv::Dataiterator)
+#endif
+ %newobject __next__;
+ Datamatch *__next__() {
+ Dataiterator *ndi;
+ if (!dataiterator_step($self)) {
+ return 0;
+ }
+ ndi = solv_calloc(1, sizeof(*ndi));
+ dataiterator_init_clone(ndi, $self);
+ dataiterator_strdup(ndi);
+ return ndi;
+ }
+#ifdef SWIGRUBY
+ void each() {
+ Datamatch *d;
+ while ((d = Dataiterator___next__($self)) != 0) {
+ rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(d), SWIGTYPE_p_Datamatch, SWIG_POINTER_OWN | 0));
+ }
+ }
+#endif
+ void prepend_keyname(Id key) {
+ dataiterator_prepend_keyname($self, key);
+ }
+ void skip_solvable() {
+ dataiterator_skip_solvable($self);
+ }
+}
+
+%extend Datapos {
+ Id lookup_id(Id keyname) {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ Id r;
+ pool->pos = *$self;
+ r = pool_lookup_id(pool, SOLVID_POS, keyname);
+ pool->pos = oldpos;
+ return r;
+ }
+ const char *lookup_str(Id keyname) {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ const char *r;
+ pool->pos = *$self;
+ r = pool_lookup_str(pool, SOLVID_POS, keyname);
+ pool->pos = oldpos;
+ return r;
+ }
+ unsigned long long lookup_num(Id keyname, unsigned long long notfound = 0) {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ unsigned long long r;
+ pool->pos = *$self;
+ r = pool_lookup_num(pool, SOLVID_POS, keyname, notfound);
+ pool->pos = oldpos;
+ return r;
+ }
+ bool lookup_void(Id keyname) {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ int r;
+ pool->pos = *$self;
+ r = pool_lookup_void(pool, SOLVID_POS, keyname);
+ pool->pos = oldpos;
+ return r;
+ }
+ %newobject lookup_checksum;
+ Chksum *lookup_checksum(Id keyname) {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ Id type = 0;
+ const unsigned char *b;
+ pool->pos = *$self;
+ b = pool_lookup_bin_checksum(pool, SOLVID_POS, keyname, &type);
+ pool->pos = oldpos;
+ return solv_chksum_create_from_bin(type, b);
+ }
+ const char *lookup_deltaseq() {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ const char *seq;
+ pool->pos = *$self;
+ seq = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
+ if (seq) {
+ seq = pool_tmpjoin(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
+ seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
+ }
+ pool->pos = oldpos;
+ return seq;
+ }
+ const char *lookup_deltalocation(unsigned int *OUTPUT) {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ const char *loc;
+ pool->pos = *$self;
+ loc = pool_lookup_deltalocation(pool, SOLVID_POS, OUTPUT);
+ pool->pos = oldpos;
+ return loc;
+ }
+ Queue lookup_idarray(Id keyname) {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ Queue r;
+ queue_init(&r);
+ pool->pos = *$self;
+ pool_lookup_idarray(pool, SOLVID_POS, keyname, &r);
+ pool->pos = oldpos;
+ return r;
+ }
+ %newobject Dataiterator;
+ Dataiterator *Dataiterator(Id key, const char *match = 0, int flags = 0) {
+ Pool *pool = $self->repo->pool;
+ Datapos oldpos = pool->pos;
+ Dataiterator *di;
+ pool->pos = *$self;
+ di = new_Dataiterator(pool, 0, SOLVID_POS, key, match, flags);
+ pool->pos = oldpos;
+ return di;
+ }
+}
+
+%extend Datamatch {
+ ~Datamatch() {
+ dataiterator_free($self);
+ solv_free($self);
+ }
+ %newobject solvable;
+ XSolvable * const solvable;
+ Id const key_id;
+ const char * const key_idstr;
+ Id const type_id;
+ const char * const type_idstr;
+ Id const id;
+ const char * const idstr;
+ const char * const str;
+ BinaryBlob const binary;
+ unsigned long long const num;
+ unsigned int const num2;
+ %{
+ SWIGINTERN XSolvable *Datamatch_solvable_get(Dataiterator *di) {
+ return new_XSolvable(di->pool, di->solvid);
+ }
+ SWIGINTERN Id Datamatch_key_id_get(Dataiterator *di) {
+ return di->key->name;
+ }
+ SWIGINTERN const char *Datamatch_key_idstr_get(Dataiterator *di) {
+ return pool_id2str(di->pool, di->key->name);
+ }
+ SWIGINTERN Id Datamatch_type_id_get(Dataiterator *di) {
+ return di->key->type;
+ }
+ SWIGINTERN const char *Datamatch_type_idstr_get(Dataiterator *di) {
+ return pool_id2str(di->pool, di->key->type);
+ }
+ SWIGINTERN Id Datamatch_id_get(Dataiterator *di) {
+ return di->kv.id;
+ }
+ SWIGINTERN const char *Datamatch_idstr_get(Dataiterator *di) {
+ if (di->data && (di->key->type == REPOKEY_TYPE_DIR || di->key->type == REPOKEY_TYPE_DIRSTRARRAY || di->key->type == REPOKEY_TYPE_DIRNUMNUMARRAY))
+ return repodata_dir2str(di->data, di->kv.id, 0);
+ if (di->data && di->data->localpool)
+ return stringpool_id2str(&di->data->spool, di->kv.id);
+ return pool_id2str(di->pool, di->kv.id);
+ }
+ SWIGINTERN const char * const Datamatch_str_get(Dataiterator *di) {
+ return di->kv.str;
+ }
+ SWIGINTERN BinaryBlob Datamatch_binary_get(Dataiterator *di) {
+ BinaryBlob bl;
+ bl.data = 0;
+ bl.len = 0;
+ if (di->key->type == REPOKEY_TYPE_BINARY)
+ {
+ bl.data = di->kv.str;
+ bl.len = di->kv.num;
+ }
+ else if ((bl.len = solv_chksum_len(di->key->type)) != 0)
+ bl.data = di->kv.str;
+ return bl;
+ }
+ SWIGINTERN unsigned long long Datamatch_num_get(Dataiterator *di) {
+ if (di->key->type == REPOKEY_TYPE_NUM)
+ return SOLV_KV_NUM64(&di->kv);
+ return di->kv.num;
+ }
+ SWIGINTERN unsigned int Datamatch_num2_get(Dataiterator *di) {
+ return di->kv.num2;
+ }
+ %}
+ %newobject pos;
+ Datapos *pos() {
+ Pool *pool = $self->pool;
+ Datapos *pos, oldpos = pool->pos;
+ dataiterator_setpos($self);
+ pos = solv_calloc(1, sizeof(*pos));
+ *pos = pool->pos;
+ pool->pos = oldpos;
+ return pos;
+ }
+ %newobject parentpos;
+ Datapos *parentpos() {
+ Pool *pool = $self->pool;
+ Datapos *pos, oldpos = pool->pos;
+ dataiterator_setpos_parent($self);
+ pos = solv_calloc(1, sizeof(*pos));
+ *pos = pool->pos;
+ pool->pos = oldpos;
+ return pos;
+ }
+#if defined(SWIGPERL)
+ /* cannot use str here because swig reports a bogus conflict... */
+ %rename("stringify") __str__;
+ %perlcode {
+ *solv::Datamatch::str = *solvc::Datamatch_stringify;
+ }
+#endif
+#if defined(SWIGTCL)
+ %rename("stringify") __str__;
+#endif
+ const char *__str__() {
+ KeyValue kv = $self->kv;
+ const char *str = repodata_stringify($self->pool, $self->data, $self->key, &kv, SEARCH_FILES | SEARCH_CHECKSUMS);
+ return str ? str : "";
+ }
+}
+
+%extend Pool_solvable_iterator {
+ Pool_solvable_iterator(Pool *pool) {
+ Pool_solvable_iterator *s;
+ s = solv_calloc(1, sizeof(*s));
+ s->pool = pool;
+ return s;
+ }
+#if defined(SWIGPYTHON)
+ %pythoncode {
+ def __iter__(self): return self
+ }
+#ifndef PYTHON3
+ %rename("next") __next__();
+#endif
+ %exception __next__ {
+ $action
+ if (!result) {
+ PyErr_SetString(PyExc_StopIteration,"no more matches");
+ return NULL;
+ }
+ }
+#endif
+#ifdef SWIGPERL
+ perliter(solv::Pool_solvable_iterator)
+#endif
+ %newobject __next__;
+ XSolvable *__next__() {
+ Pool *pool = $self->pool;
+ if ($self->id >= pool->nsolvables)
+ return 0;
+ while (++$self->id < pool->nsolvables)
+ if (pool->solvables[$self->id].repo)
+ return new_XSolvable(pool, $self->id);
+ return 0;
+ }
+#ifdef SWIGRUBY
+ void each() {
+ XSolvable *n;
+ while ((n = Pool_solvable_iterator___next__($self)) != 0) {
+ rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(n), SWIGTYPE_p_XSolvable, SWIG_POINTER_OWN | 0));
+ }
+ }
+#endif
+ %newobject __getitem__;
+ XSolvable *__getitem__(Id key) {
+ Pool *pool = $self->pool;
+ if (key > 0 && key < pool->nsolvables && pool->solvables[key].repo)
+ return new_XSolvable(pool, key);
+ return 0;
+ }
+ int __len__() {
+ return $self->pool->nsolvables;
+ }
+}
+
+%extend Pool_repo_iterator {
+ Pool_repo_iterator(Pool *pool) {
+ Pool_repo_iterator *s;
+ s = solv_calloc(1, sizeof(*s));
+ s->pool = pool;
+ return s;
+ }
+#if defined(SWIGPYTHON)
+ %pythoncode {
+ def __iter__(self): return self
+ }
+#ifndef PYTHON3
+ %rename("next") __next__();
+#endif
+ %exception __next__ {
+ $action
+ if (!result) {
+ PyErr_SetString(PyExc_StopIteration,"no more matches");
+ return NULL;
+ }
+ }
+#endif
+#ifdef SWIGPERL
+ perliter(solv::Pool_repo_iterator)
+#endif
+ %newobject __next__;
+ Repo *__next__() {
+ Pool *pool = $self->pool;
+ if ($self->id >= pool->nrepos)
+ return 0;
+ while (++$self->id < pool->nrepos) {
+ Repo *r = pool_id2repo(pool, $self->id);
+ if (r)
+ return r;
+ }
+ return 0;
+ }
+#ifdef SWIGRUBY
+ void each() {
+ Repo *n;
+ while ((n = Pool_repo_iterator___next__($self)) != 0) {
+ rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(n), SWIGTYPE_p_Repo, SWIG_POINTER_OWN | 0));
+ }
+ }
+#endif
+ Repo *__getitem__(Id key) {
+ Pool *pool = $self->pool;
+ if (key > 0 && key < pool->nrepos)
+ return pool_id2repo(pool, key);
+ return 0;
+ }
+ int __len__() {
+ return $self->pool->nrepos;
+ }
+}
+
+%extend Repo_solvable_iterator {
+ Repo_solvable_iterator(Repo *repo) {
+ Repo_solvable_iterator *s;
+ s = solv_calloc(1, sizeof(*s));
+ s->repo = repo;
+ return s;
+ }
+#if defined(SWIGPYTHON)
+ %pythoncode {
+ def __iter__(self): return self
+ }
+#ifndef PYTHON3
+ %rename("next") __next__();
+#endif
+ %exception __next__ {
+ $action
+ if (!result) {
+ PyErr_SetString(PyExc_StopIteration,"no more matches");
+ return NULL;
+ }
+ }
+#endif
+#ifdef SWIGPERL
+ perliter(solv::Repo_solvable_iterator)
+#endif
+ %newobject __next__;
+ XSolvable *__next__() {
+ Repo *repo = $self->repo;
+ Pool *pool = repo->pool;
+ if (repo->start > 0 && $self->id < repo->start)
+ $self->id = repo->start - 1;
+ if ($self->id >= repo->end)
+ return 0;
+ while (++$self->id < repo->end)
+ if (pool->solvables[$self->id].repo == repo)
+ return new_XSolvable(pool, $self->id);
+ return 0;
+ }
+#ifdef SWIGRUBY
+ void each() {
+ XSolvable *n;
+ while ((n = Repo_solvable_iterator___next__($self)) != 0) {
+ rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(n), SWIGTYPE_p_XSolvable, SWIG_POINTER_OWN | 0));
+ }
+ }
+#endif
+ %newobject __getitem__;
+ XSolvable *__getitem__(Id key) {
+ Repo *repo = $self->repo;
+ Pool *pool = repo->pool;
+ if (key > 0 && key < pool->nsolvables && pool->solvables[key].repo == repo)
+ return new_XSolvable(pool, key);
+ return 0;
+ }
+ int __len__() {
+ return $self->repo->pool->nsolvables;
+ }
+}
+
+%extend Dep {
+ Dep(Pool *pool, Id id) {
+ Dep *s;
+ if (!id)
+ return 0;
+ s = solv_calloc(1, sizeof(*s));
+ s->pool = pool;
+ s->id = id;
+ return s;
+ }
+ %newobject Rel;
+ Dep *Rel(int flags, DepId evrid, bool create=1) {
+ Id id = pool_rel2id($self->pool, $self->id, evrid, flags, create);
+ if (!id)
+ return 0;
+ return new_Dep($self->pool, id);
+ }
+ %newobject Selection_name;
+ Selection *Selection_name(int setflags=0) {
+ Selection *sel = new_Selection($self->pool);
+ if (ISRELDEP($self->id)) {
+ Reldep *rd = GETRELDEP($self->pool, $self->id);
+ if (rd->flags == REL_EQ) {
+ setflags |= $self->pool->disttype == DISTTYPE_DEB || strchr(pool_id2str($self->pool, rd->evr), '-') != 0 ? SOLVER_SETEVR : SOLVER_SETEV;
+ if (ISRELDEP(rd->name))
+ rd = GETRELDEP($self->pool, rd->name);
+ }
+ if (rd->flags == REL_ARCH)
+ setflags |= SOLVER_SETARCH;
+ }
+ queue_push2(&sel->q, SOLVER_SOLVABLE_NAME | setflags, $self->id);
+ return sel;
+ }
+ %newobject Selection_provides;
+ Selection *Selection_provides(int setflags=0) {
+ Selection *sel = new_Selection($self->pool);
+ if (ISRELDEP($self->id)) {
+ Reldep *rd = GETRELDEP($self->pool, $self->id);
+ if (rd->flags == REL_ARCH)
+ setflags |= SOLVER_SETARCH;
+ }
+ queue_push2(&sel->q, SOLVER_SOLVABLE_PROVIDES | setflags, $self->id);
+ return sel;
+ }
+ const char *str() {
+ return pool_dep2str($self->pool, $self->id);
+ }
+#if defined(SWIGTCL)
+ %rename("==") __eq__;
+#endif
+ bool __eq__(Dep *s) {
+ return $self->pool == s->pool && $self->id == s->id;
+ }
+#if defined(SWIGTCL)
+ %rename("!=") __ne__;
+#endif
+ bool __ne__(Dep *s) {
+ return !Dep___eq__($self, s);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("str") __str__;
+#endif
+ const char *__str__() {
+ return pool_dep2str($self->pool, $self->id);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("repr") __repr__;
+#endif
+ %newobject __repr__;
+ const char *__repr__() {
+ char buf[20];
+ sprintf(buf, "<Id #%d ", $self->id);
+ return solv_dupjoin(buf, pool_dep2str($self->pool, $self->id), ">");
+ }
+}
+
+%extend XSolvable {
+ XSolvable(Pool *pool, Id id) {
+ XSolvable *s;
+ if (!id || id >= pool->nsolvables)
+ return 0;
+ s = solv_calloc(1, sizeof(*s));
+ s->pool = pool;
+ s->id = id;
+ return s;
+ }
+ const char *str() {
+ return pool_solvid2str($self->pool, $self->id);
+ }
+ const char *lookup_str(Id keyname) {
+ return pool_lookup_str($self->pool, $self->id, keyname);
+ }
+ Id lookup_id(Id keyname) {
+ return pool_lookup_id($self->pool, $self->id, keyname);
+ }
+ unsigned long long lookup_num(Id keyname, unsigned long long notfound = 0) {
+ return pool_lookup_num($self->pool, $self->id, keyname, notfound);
+ }
+ bool lookup_void(Id keyname) {
+ return pool_lookup_void($self->pool, $self->id, keyname);
+ }
+ %newobject lookup_checksum;
+ Chksum *lookup_checksum(Id keyname) {
+ Id type = 0;
+ const unsigned char *b = pool_lookup_bin_checksum($self->pool, $self->id, keyname, &type);
+ return solv_chksum_create_from_bin(type, b);
+ }
+ Queue lookup_idarray(Id keyname, Id marker = -1) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ Queue r;
+ queue_init(&r);
+ solvable_lookup_deparray(s, keyname, &r, marker);
+ return r;
+ }
+ %typemap(out) Queue lookup_deparray Queue2Array(Dep *, 1, new_Dep(arg1->pool, id));
+ %newobject lookup_deparray;
+ Queue lookup_deparray(Id keyname, Id marker = -1) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ Queue r;
+ queue_init(&r);
+ solvable_lookup_deparray(s, keyname, &r, marker);
+ return r;
+ }
+ const char *lookup_location(unsigned int *OUTPUT) {
+ return solvable_lookup_location($self->pool->solvables + $self->id, OUTPUT);
+ }
+ %newobject Dataiterator;
+ Dataiterator *Dataiterator(Id key, const char *match = 0, int flags = 0) {
+ return new_Dataiterator($self->pool, 0, $self->id, key, match, flags);
+ }
+#ifdef SWIGRUBY
+ %rename("installable?") installable;
+#endif
+ bool installable() {
+ return pool_installable($self->pool, pool_id2solvable($self->pool, $self->id));
+ }
+#ifdef SWIGRUBY
+ %rename("isinstalled?") isinstalled;
+#endif
+ bool isinstalled() {
+ Pool *pool = $self->pool;
+ return pool->installed && pool_id2solvable(pool, $self->id)->repo == pool->installed;
+ }
+
+ const char *name;
+ %{
+ SWIGINTERN void XSolvable_name_set(XSolvable *xs, const char *name) {
+ Pool *pool = xs->pool;
+ pool->solvables[xs->id].name = pool_str2id(pool, name, 1);
+ }
+ SWIGINTERN const char *XSolvable_name_get(XSolvable *xs) {
+ Pool *pool = xs->pool;
+ return pool_id2str(pool, pool->solvables[xs->id].name);
+ }
+ %}
+ Id nameid;
+ %{
+ SWIGINTERN void XSolvable_nameid_set(XSolvable *xs, Id nameid) {
+ xs->pool->solvables[xs->id].name = nameid;
+ }
+ SWIGINTERN Id XSolvable_nameid_get(XSolvable *xs) {
+ return xs->pool->solvables[xs->id].name;
+ }
+ %}
+ const char *evr;
+ %{
+ SWIGINTERN void XSolvable_evr_set(XSolvable *xs, const char *evr) {
+ Pool *pool = xs->pool;
+ pool->solvables[xs->id].evr = pool_str2id(pool, evr, 1);
+ }
+ SWIGINTERN const char *XSolvable_evr_get(XSolvable *xs) {
+ Pool *pool = xs->pool;
+ return pool_id2str(pool, pool->solvables[xs->id].evr);
+ }
+ %}
+ Id evrid;
+ %{
+ SWIGINTERN void XSolvable_evrid_set(XSolvable *xs, Id evrid) {
+ xs->pool->solvables[xs->id].evr = evrid;
+ }
+ SWIGINTERN Id XSolvable_evrid_get(XSolvable *xs) {
+ return xs->pool->solvables[xs->id].evr;
+ }
+ %}
+ const char *arch;
+ %{
+ SWIGINTERN void XSolvable_arch_set(XSolvable *xs, const char *arch) {
+ Pool *pool = xs->pool;
+ pool->solvables[xs->id].arch = pool_str2id(pool, arch, 1);
+ }
+ SWIGINTERN const char *XSolvable_arch_get(XSolvable *xs) {
+ Pool *pool = xs->pool;
+ return pool_id2str(pool, pool->solvables[xs->id].arch);
+ }
+ %}
+ Id archid;
+ %{
+ SWIGINTERN void XSolvable_archid_set(XSolvable *xs, Id archid) {
+ xs->pool->solvables[xs->id].arch = archid;
+ }
+ SWIGINTERN Id XSolvable_archid_get(XSolvable *xs) {
+ return xs->pool->solvables[xs->id].arch;
+ }
+ %}
+ const char *vendor;
+ %{
+ SWIGINTERN void XSolvable_vendor_set(XSolvable *xs, const char *vendor) {
+ Pool *pool = xs->pool;
+ pool->solvables[xs->id].vendor = pool_str2id(pool, vendor, 1);
+ }
+ SWIGINTERN const char *XSolvable_vendor_get(XSolvable *xs) {
+ Pool *pool = xs->pool;
+ return pool_id2str(pool, pool->solvables[xs->id].vendor);
+ }
+ %}
+ Id vendorid;
+ %{
+ SWIGINTERN void XSolvable_vendorid_set(XSolvable *xs, Id vendorid) {
+ xs->pool->solvables[xs->id].vendor = vendorid;
+ }
+ SWIGINTERN Id XSolvable_vendorid_get(XSolvable *xs) {
+ return xs->pool->solvables[xs->id].vendor;
+ }
+ %}
+ Repo * const repo;
+ %{
+ SWIGINTERN Repo *XSolvable_repo_get(XSolvable *xs) {
+ return xs->pool->solvables[xs->id].repo;
+ }
+ %}
+
+ /* old interface, please use the generic add_deparray instead */
+ void add_provides(DepId id, Id marker = -1) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ marker = solv_depmarker(SOLVABLE_PROVIDES, marker);
+ s->provides = repo_addid_dep(s->repo, s->provides, id, marker);
+ }
+ void add_obsoletes(DepId id) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ s->obsoletes = repo_addid_dep(s->repo, s->obsoletes, id, 0);
+ }
+ void add_conflicts(DepId id) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ s->conflicts = repo_addid_dep(s->repo, s->conflicts, id, 0);
+ }
+ void add_requires(DepId id, Id marker = -1) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ marker = solv_depmarker(SOLVABLE_REQUIRES, marker);
+ s->requires = repo_addid_dep(s->repo, s->requires, id, marker);
+ }
+ void add_recommends(DepId id) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ s->recommends = repo_addid_dep(s->repo, s->recommends, id, 0);
+ }
+ void add_suggests(DepId id) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ s->suggests = repo_addid_dep(s->repo, s->suggests, id, 0);
+ }
+ void add_supplements(DepId id) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ s->supplements = repo_addid_dep(s->repo, s->supplements, id, 0);
+ }
+ void add_enhances(DepId id) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ s->enhances = repo_addid_dep(s->repo, s->enhances, id, 0);
+ }
+
+ void unset(Id keyname) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ repo_unset(s->repo, $self->id, keyname);
+ }
+
+ void add_deparray(Id keyname, DepId id, Id marker = -1) {
+ Solvable *s = $self->pool->solvables + $self->id;
+ solvable_add_deparray(s, keyname, id, marker);
+ }
+
+ %newobject Selection;
+ Selection *Selection(int setflags=0) {
+ Selection *sel = new_Selection($self->pool);
+ queue_push2(&sel->q, SOLVER_SOLVABLE | setflags, $self->id);
+ return sel;
+ }
+
+#ifdef SWIGRUBY
+ %rename("identical?") identical;
+#endif
+ bool identical(XSolvable *s2) {
+ return solvable_identical($self->pool->solvables + $self->id, s2->pool->solvables + s2->id);
+ }
+ int evrcmp(XSolvable *s2) {
+ return pool_evrcmp($self->pool, $self->pool->solvables[$self->id].evr, s2->pool->solvables[s2->id].evr, EVRCMP_COMPARE);
+ }
+
+#if defined(SWIGTCL)
+ %rename("==") __eq__;
+#endif
+ bool __eq__(XSolvable *s) {
+ return $self->pool == s->pool && $self->id == s->id;
+ }
+#if defined(SWIGTCL)
+ %rename("!=") __ne__;
+#endif
+ bool __ne__(XSolvable *s) {
+ return !XSolvable___eq__($self, s);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("str") __str__;
+#endif
+ const char *__str__() {
+ return pool_solvid2str($self->pool, $self->id);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("repr") __repr__;
+#endif
+ %newobject __repr__;
+ const char *__repr__() {
+ char buf[20];
+ sprintf(buf, "<Solvable #%d ", $self->id);
+ return solv_dupjoin(buf, pool_solvid2str($self->pool, $self->id), ">");
+ }
+}
+
+%extend Problem {
+ Problem(Solver *solv, Id id) {
+ Problem *p;
+ p = solv_calloc(1, sizeof(*p));
+ p->solv = solv;
+ p->id = id;
+ return p;
+ }
+ %newobject findproblemrule;
+ XRule *findproblemrule() {
+ Id r = solver_findproblemrule($self->solv, $self->id);
+ return new_XRule($self->solv, r);
+ }
+ %newobject findallproblemrules;
+ %typemap(out) Queue findallproblemrules Queue2Array(XRule *, 1, new_XRule(arg1->solv, id));
+ Queue findallproblemrules(int unfiltered=0) {
+ Solver *solv = $self->solv;
+ Id probr;
+ int i, j;
+ Queue q;
+ queue_init(&q);
+ solver_findallproblemrules(solv, $self->id, &q);
+ if (!unfiltered)
+ {
+ for (i = j = 0; i < q.count; i++)
+ {
+ SolverRuleinfo rclass;
+ probr = q.elements[i];
+ rclass = solver_ruleclass(solv, probr);
+ if (rclass == SOLVER_RULE_UPDATE || rclass == SOLVER_RULE_JOB)
+ continue;
+ q.elements[j++] = probr;
+ }
+ if (j)
+ queue_truncate(&q, j);
+ }
+ return q;
+ }
+ int solution_count() {
+ return solver_solution_count($self->solv, $self->id);
+ }
+ %typemap(out) Queue solutions Queue2Array(Solution *, 1, new_Solution(arg1, id));
+ %newobject solutions;
+ Queue solutions() {
+ Queue q;
+ int i, cnt;
+ queue_init(&q);
+ cnt = solver_solution_count($self->solv, $self->id);
+ for (i = 1; i <= cnt; i++)
+ queue_push(&q, i);
+ return q;
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("str") __str__;
+#endif
+ const char *__str__() {
+ return solver_problem2str($self->solv, $self->id);
+ }
+}
+
+%extend Solution {
+ Solution(Problem *p, Id id) {
+ Solution *s;
+ s = solv_calloc(1, sizeof(*s));
+ s->solv = p->solv;
+ s->problemid = p->id;
+ s->id = id;
+ return s;
+ }
+ int element_count() {
+ return solver_solutionelement_count($self->solv, $self->problemid, $self->id);
+ }
+
+ %typemap(out) Queue elements Queue2Array(Solutionelement *, 4, new_Solutionelement(arg1->solv, arg1->problemid, arg1->id, id, idp[1], idp[2], idp[3]));
+ %newobject elements;
+ Queue elements(bool expandreplaces=0) {
+ Queue q;
+ int i, cnt;
+ queue_init(&q);
+ cnt = solver_solutionelement_count($self->solv, $self->problemid, $self->id);
+ for (i = 1; i <= cnt; i++)
+ {
+ Id p, rp, type;
+ solver_next_solutionelement($self->solv, $self->problemid, $self->id, i - 1, &p, &rp);
+ if (p > 0) {
+ type = rp ? SOLVER_SOLUTION_REPLACE : SOLVER_SOLUTION_ERASE;
+ } else {
+ type = p;
+ p = rp;
+ rp = 0;
+ }
+ if (type == SOLVER_SOLUTION_REPLACE && expandreplaces) {
+ int illegal = policy_is_illegal(self->solv, self->solv->pool->solvables + p, self->solv->pool->solvables + rp, 0);
+ if (illegal) {
+ if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0) {
+ queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_DOWNGRADE);
+ queue_push2(&q, p, rp);
+ }
+ if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0) {
+ queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_ARCHCHANGE);
+ queue_push2(&q, p, rp);
+ }
+ if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0) {
+ queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_VENDORCHANGE);
+ queue_push2(&q, p, rp);
+ }
+ if ((illegal & POLICY_ILLEGAL_NAMECHANGE) != 0) {
+ queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_NAMECHANGE);
+ queue_push2(&q, p, rp);
+ }
+ continue;
+ }
+ }
+ queue_push2(&q, i, type);
+ queue_push2(&q, p, rp);
+ }
+ return q;
+ }
+}
+
+%extend Solutionelement {
+ Solutionelement(Solver *solv, Id problemid, Id solutionid, Id id, Id type, Id p, Id rp) {
+ Solutionelement *e;
+ e = solv_calloc(1, sizeof(*e));
+ e->solv = solv;
+ e->problemid = problemid;
+ e->solutionid = id;
+ e->id = id;
+ e->type = type;
+ e->p = p;
+ e->rp = rp;
+ return e;
+ }
+ const char *str() {
+ Id p = $self->type;
+ Id rp = $self->p;
+ int illegal = 0;
+ if (p == SOLVER_SOLUTION_ERASE)
+ {
+ p = rp;
+ rp = 0;
+ }
+ else if (p == SOLVER_SOLUTION_REPLACE)
+ {
+ p = rp;
+ rp = $self->rp;
+ }
+ else if (p == SOLVER_SOLUTION_REPLACE_DOWNGRADE)
+ illegal = POLICY_ILLEGAL_DOWNGRADE;
+ else if (p == SOLVER_SOLUTION_REPLACE_ARCHCHANGE)
+ illegal = POLICY_ILLEGAL_ARCHCHANGE;
+ else if (p == SOLVER_SOLUTION_REPLACE_VENDORCHANGE)
+ illegal = POLICY_ILLEGAL_VENDORCHANGE;
+ else if (p == SOLVER_SOLUTION_REPLACE_NAMECHANGE)
+ illegal = POLICY_ILLEGAL_NAMECHANGE;
+ if (illegal)
+ return pool_tmpjoin($self->solv->pool, "allow ", policy_illegal2str($self->solv, illegal, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp), 0);
+ return solver_solutionelement2str($self->solv, p, rp);
+ }
+ %typemap(out) Queue replaceelements Queue2Array(Solutionelement *, 1, new_Solutionelement(arg1->solv, arg1->problemid, arg1->solutionid, arg1->id, id, arg1->p, arg1->rp));
+ %newobject replaceelements;
+ Queue replaceelements() {
+ Queue q;
+ int illegal;
+
+ queue_init(&q);
+ if ($self->type != SOLVER_SOLUTION_REPLACE || $self->p <= 0 || $self->rp <= 0)
+ illegal = 0;
+ else
+ illegal = policy_is_illegal($self->solv, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp, 0);
+ if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0)
+ queue_push(&q, SOLVER_SOLUTION_REPLACE_DOWNGRADE);
+ if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0)
+ queue_push(&q, SOLVER_SOLUTION_REPLACE_ARCHCHANGE);
+ if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0)
+ queue_push(&q, SOLVER_SOLUTION_REPLACE_VENDORCHANGE);
+ if ((illegal & POLICY_ILLEGAL_NAMECHANGE) != 0)
+ queue_push(&q, SOLVER_SOLUTION_REPLACE_NAMECHANGE);
+ if (!q.count)
+ queue_push(&q, $self->type);
+ return q;
+ }
+ int illegalreplace() {
+ if ($self->type != SOLVER_SOLUTION_REPLACE || $self->p <= 0 || $self->rp <= 0)
+ return 0;
+ return policy_is_illegal($self->solv, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp, 0);
+ }
+ %newobject solvable;
+ XSolvable * const solvable;
+ %newobject replacement;
+ XSolvable * const replacement;
+ int const jobidx;
+ %{
+ SWIGINTERN XSolvable *Solutionelement_solvable_get(Solutionelement *e) {
+ return new_XSolvable(e->solv->pool, e->p);
+ }
+ SWIGINTERN XSolvable *Solutionelement_replacement_get(Solutionelement *e) {
+ return new_XSolvable(e->solv->pool, e->rp);
+ }
+ SWIGINTERN int Solutionelement_jobidx_get(Solutionelement *e) {
+ if (e->type != SOLVER_SOLUTION_JOB && e->type != SOLVER_SOLUTION_POOLJOB)
+ return -1;
+ return (e->p - 1) / 2;
+ }
+ %}
+ %newobject Job;
+ Job *Job() {
+ Id extraflags = solver_solutionelement_extrajobflags($self->solv, $self->problemid, $self->solutionid);
+ if ($self->type == SOLVER_SOLUTION_JOB || $self->type == SOLVER_SOLUTION_POOLJOB)
+ return new_Job($self->solv->pool, SOLVER_NOOP, 0);
+ if ($self->type == SOLVER_SOLUTION_INFARCH || $self->type == SOLVER_SOLUTION_DISTUPGRADE || $self->type == SOLVER_SOLUTION_BEST)
+ return new_Job($self->solv->pool, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_NOTBYUSER|extraflags, $self->p);
+ if ($self->type == SOLVER_SOLUTION_REPLACE || $self->type == SOLVER_SOLUTION_REPLACE_DOWNGRADE || $self->type == SOLVER_SOLUTION_REPLACE_ARCHCHANGE || $self->type == SOLVER_SOLUTION_REPLACE_VENDORCHANGE || $self->type == SOLVER_SOLUTION_REPLACE_NAMECHANGE)
+ return new_Job($self->solv->pool, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_NOTBYUSER|extraflags, $self->rp);
+ if ($self->type == SOLVER_SOLUTION_ERASE)
+ return new_Job($self->solv->pool, SOLVER_ERASE|SOLVER_SOLVABLE|extraflags, $self->p);
+ return 0;
+ }
+}
+
+%extend Solver {
+ static const int SOLVER_RULE_UNKNOWN = SOLVER_RULE_UNKNOWN;
+ static const int SOLVER_RULE_PKG = SOLVER_RULE_PKG;
+ static const int SOLVER_RULE_PKG_NOT_INSTALLABLE = SOLVER_RULE_PKG_NOT_INSTALLABLE;
+ static const int SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP = SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP;
+ static const int SOLVER_RULE_PKG_REQUIRES = SOLVER_RULE_PKG_REQUIRES;
+ static const int SOLVER_RULE_PKG_SELF_CONFLICT = SOLVER_RULE_PKG_SELF_CONFLICT;
+ static const int SOLVER_RULE_PKG_CONFLICTS = SOLVER_RULE_PKG_CONFLICTS;
+ static const int SOLVER_RULE_PKG_SAME_NAME = SOLVER_RULE_PKG_SAME_NAME;
+ static const int SOLVER_RULE_PKG_OBSOLETES = SOLVER_RULE_PKG_OBSOLETES;
+ static const int SOLVER_RULE_PKG_IMPLICIT_OBSOLETES = SOLVER_RULE_PKG_IMPLICIT_OBSOLETES;
+ static const int SOLVER_RULE_PKG_INSTALLED_OBSOLETES = SOLVER_RULE_PKG_INSTALLED_OBSOLETES;
+ static const int SOLVER_RULE_UPDATE = SOLVER_RULE_UPDATE;
+ static const int SOLVER_RULE_FEATURE = SOLVER_RULE_FEATURE;
+ static const int SOLVER_RULE_JOB = SOLVER_RULE_JOB;
+ static const int SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP = SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP;
+ static const int SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM = SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM;
+ static const int SOLVER_RULE_JOB_UNKNOWN_PACKAGE = SOLVER_RULE_JOB_UNKNOWN_PACKAGE;
+ static const int SOLVER_RULE_JOB_UNSUPPORTED = SOLVER_RULE_JOB_UNSUPPORTED;
+ static const int SOLVER_RULE_DISTUPGRADE = SOLVER_RULE_DISTUPGRADE;
+ static const int SOLVER_RULE_INFARCH = SOLVER_RULE_INFARCH;
+ static const int SOLVER_RULE_CHOICE = SOLVER_RULE_CHOICE;
+ static const int SOLVER_RULE_LEARNT = SOLVER_RULE_LEARNT;
+
+ static const int SOLVER_SOLUTION_JOB = SOLVER_SOLUTION_JOB;
+ static const int SOLVER_SOLUTION_POOLJOB = SOLVER_SOLUTION_POOLJOB;
+ static const int SOLVER_SOLUTION_INFARCH = SOLVER_SOLUTION_INFARCH;
+ static const int SOLVER_SOLUTION_DISTUPGRADE = SOLVER_SOLUTION_DISTUPGRADE;
+ static const int SOLVER_SOLUTION_BEST = SOLVER_SOLUTION_BEST;
+ static const int SOLVER_SOLUTION_ERASE = SOLVER_SOLUTION_ERASE;
+ static const int SOLVER_SOLUTION_REPLACE = SOLVER_SOLUTION_REPLACE;
+ static const int SOLVER_SOLUTION_REPLACE_DOWNGRADE = SOLVER_SOLUTION_REPLACE_DOWNGRADE;
+ static const int SOLVER_SOLUTION_REPLACE_ARCHCHANGE = SOLVER_SOLUTION_REPLACE_ARCHCHANGE;
+ static const int SOLVER_SOLUTION_REPLACE_VENDORCHANGE = SOLVER_SOLUTION_REPLACE_VENDORCHANGE;
+ static const int SOLVER_SOLUTION_REPLACE_NAMECHANGE = SOLVER_SOLUTION_REPLACE_NAMECHANGE;
+
+ static const int POLICY_ILLEGAL_DOWNGRADE = POLICY_ILLEGAL_DOWNGRADE;
+ static const int POLICY_ILLEGAL_ARCHCHANGE = POLICY_ILLEGAL_ARCHCHANGE;
+ static const int POLICY_ILLEGAL_VENDORCHANGE = POLICY_ILLEGAL_VENDORCHANGE;
+ static const int POLICY_ILLEGAL_NAMECHANGE = POLICY_ILLEGAL_NAMECHANGE;
+
+ static const int SOLVER_FLAG_ALLOW_DOWNGRADE = SOLVER_FLAG_ALLOW_DOWNGRADE;
+ static const int SOLVER_FLAG_ALLOW_ARCHCHANGE = SOLVER_FLAG_ALLOW_ARCHCHANGE;
+ static const int SOLVER_FLAG_ALLOW_VENDORCHANGE = SOLVER_FLAG_ALLOW_VENDORCHANGE;
+ static const int SOLVER_FLAG_ALLOW_NAMECHANGE = SOLVER_FLAG_ALLOW_NAMECHANGE;
+ static const int SOLVER_FLAG_ALLOW_UNINSTALL = SOLVER_FLAG_ALLOW_UNINSTALL;
+ static const int SOLVER_FLAG_NO_UPDATEPROVIDE = SOLVER_FLAG_NO_UPDATEPROVIDE;
+ static const int SOLVER_FLAG_SPLITPROVIDES = SOLVER_FLAG_SPLITPROVIDES;
+ static const int SOLVER_FLAG_IGNORE_RECOMMENDED = SOLVER_FLAG_IGNORE_RECOMMENDED;
+ static const int SOLVER_FLAG_ADD_ALREADY_RECOMMENDED = SOLVER_FLAG_ADD_ALREADY_RECOMMENDED;
+ static const int SOLVER_FLAG_NO_INFARCHCHECK = SOLVER_FLAG_NO_INFARCHCHECK;
+ static const int SOLVER_FLAG_BEST_OBEY_POLICY = SOLVER_FLAG_BEST_OBEY_POLICY;
+ static const int SOLVER_FLAG_NO_AUTOTARGET = SOLVER_FLAG_NO_AUTOTARGET;
+ static const int SOLVER_FLAG_DUP_ALLOW_DOWNGRADE = SOLVER_FLAG_DUP_ALLOW_DOWNGRADE;
+ static const int SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE = SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE;
+ static const int SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE = SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE;
+ static const int SOLVER_FLAG_DUP_ALLOW_NAMECHANGE = SOLVER_FLAG_DUP_ALLOW_NAMECHANGE;
+ static const int SOLVER_FLAG_KEEP_ORPHANS = SOLVER_FLAG_KEEP_ORPHANS;
+ static const int SOLVER_FLAG_BREAK_ORPHANS = SOLVER_FLAG_BREAK_ORPHANS;
+ static const int SOLVER_FLAG_FOCUS_INSTALLED = SOLVER_FLAG_FOCUS_INSTALLED;
+ static const int SOLVER_FLAG_YUM_OBSOLETES = SOLVER_FLAG_YUM_OBSOLETES;
+ static const int SOLVER_FLAG_NEED_UPDATEPROVIDE = SOLVER_FLAG_NEED_UPDATEPROVIDE;
+
+ static const int SOLVER_REASON_UNRELATED = SOLVER_REASON_UNRELATED;
+ static const int SOLVER_REASON_UNIT_RULE = SOLVER_REASON_UNIT_RULE;
+ static const int SOLVER_REASON_KEEP_INSTALLED = SOLVER_REASON_KEEP_INSTALLED;
+ static const int SOLVER_REASON_RESOLVE_JOB = SOLVER_REASON_RESOLVE_JOB;
+ static const int SOLVER_REASON_UPDATE_INSTALLED = SOLVER_REASON_UPDATE_INSTALLED;
+ static const int SOLVER_REASON_CLEANDEPS_ERASE = SOLVER_REASON_CLEANDEPS_ERASE;
+ static const int SOLVER_REASON_RESOLVE = SOLVER_REASON_RESOLVE;
+ static const int SOLVER_REASON_WEAKDEP = SOLVER_REASON_WEAKDEP;
+ static const int SOLVER_REASON_RESOLVE_ORPHAN = SOLVER_REASON_RESOLVE_ORPHAN;
+ static const int SOLVER_REASON_RECOMMENDED = SOLVER_REASON_RECOMMENDED;
+ static const int SOLVER_REASON_SUPPLEMENTED = SOLVER_REASON_SUPPLEMENTED;
+
+ /* legacy */
+ static const int SOLVER_RULE_RPM = SOLVER_RULE_RPM;
+
+ ~Solver() {
+ solver_free($self);
+ }
+
+ int set_flag(int flag, int value) {
+ return solver_set_flag($self, flag, value);
+ }
+ int get_flag(int flag) {
+ return solver_get_flag($self, flag);
+ }
+#if defined(SWIGPYTHON)
+ %pythoncode {
+ def solve(self, jobs):
+ j = []
+ for job in jobs: j += [job.how, job.what]
+ return self.solve_helper(j)
+ }
+#endif
+#if defined(SWIGPERL)
+ %perlcode {
+ sub solv::Solver::solve {
+ my ($self, $jobs) = @_;
+ my @j = map {($_->{'how'}, $_->{'what'})} @$jobs;
+ return $self->solve_helper(\@j);
+ }
+ }
+#endif
+#if defined(SWIGRUBY)
+%init %{
+rb_eval_string(
+ "class Solv::Solver\n"
+ " def solve(jobs)\n"
+ " jl = []\n"
+ " jobs.each do |j| ; jl << j.how << j.what ; end\n"
+ " solve_helper(jl)\n"
+ " end\n"
+ "end\n"
+ );
+%}
+#endif
+ %typemap(out) Queue solve_helper Queue2Array(Problem *, 1, new_Problem(arg1, id));
+ %newobject solve_helper;
+ Queue solve_helper(Queue jobs) {
+ Queue q;
+ int i, cnt;
+ queue_init(&q);
+ solver_solve($self, &jobs);
+ cnt = solver_problem_count($self);
+ for (i = 1; i <= cnt; i++)
+ queue_push(&q, i);
+ return q;
+ }
+#if defined(SWIGTCL)
+ %typemap(out) Queue solve Queue2Array(Problem *, 1, new_Problem(arg1, id));
+ %newobject solve;
+ Queue solve(Queue solvejobs) {
+ Queue q;
+ int i, cnt;
+ queue_init(&q);
+ solver_solve($self, &solvejobs);
+ cnt = solver_problem_count($self);
+ for (i = 1; i <= cnt; i++)
+ queue_push(&q, i);
+ return q;
+ }
+#endif
+
+ %newobject transaction;
+ Transaction *transaction() {
+ return solver_create_transaction($self);
+ }
+
+ int describe_decision(XSolvable *s, XRule **OUTPUT) {
+ int ruleid;
+ int reason = solver_describe_decision($self, s->id, &ruleid);
+ *OUTPUT = new_XRule($self, ruleid);
+ return reason;
+ }
+
+ %newobject describe_weakdep_decision_raw;
+ Queue describe_weakdep_decision_raw(XSolvable *s) {
+ Queue q;
+ queue_init(&q);
+ solver_describe_weakdep_decision($self, s->id, &q);
+ return q;
+ }
+#if defined(SWIGPYTHON)
+ %pythoncode {
+ def describe_weakdep_decision(self, s):
+ d = iter(self.describe_weakdep_decision_raw(s))
+ return [ (t, XSolvable(self.pool, sid), Dep(self.pool, id)) for t, sid, id in zip(d, d, d) ]
+ }
+#endif
+#if defined(SWIGPERL)
+ %perlcode {
+ sub solv::Solver::describe_weakdep_decision {
+ my ($self, $s) = @_;
+ my $pool = $self->{'pool'};
+ my @res;
+ my @d = $self->describe_weakdep_decision_raw($s);
+ push @res, [ splice(@d, 0, 3) ] while @d;
+ return map { [ $_->[0], solv::XSolvable->new($pool, $_->[1]), solv::Dep->new($pool, $_->[2]) ] } @res;
+ }
+ }
+#endif
+#if defined(SWIGRUBY)
+%init %{
+rb_eval_string(
+ "class Solv::Solver\n"
+ " def describe_weakdep_decision(s)\n"
+ " self.describe_weakdep_decision_raw(s).each_slice(3).map { |t, sid, id| [ t, Solv::XSolvable.new(self.pool, sid), Solv::Dep.new(self.pool, id)] }\n"
+ " end\n"
+ "end\n"
+ );
+%}
+#endif
+
+ int alternatives_count() {
+ return solver_alternatives_count($self);
+ }
+
+ %newobject alternative;
+ Alternative *alternative(Id aid) {
+ Alternative *a = solv_calloc(1, sizeof(*a));
+ a->solv = $self;
+ queue_init(&a->choices);
+ a->type = solver_get_alternative($self, aid, &a->dep_id, &a->from_id, &a->chosen_id, &a->choices, &a->level);
+ if (!a->type) {
+ queue_free(&a->choices);
+ solv_free(a);
+ return 0;
+ }
+ if (a->type == SOLVER_ALTERNATIVE_TYPE_RULE) {
+ a->rid = a->dep_id;
+ a->dep_id = 0;
+ }
+ return a;
+ }
+
+ %typemap(out) Queue all_alternatives Queue2Array(Alternative *, 1, Solver_alternative(arg1, id));
+ %newobject all_alternatives;
+ Queue all_alternatives() {
+ Queue q;
+ int i, cnt;
+ queue_init(&q);
+ cnt = solver_alternatives_count($self);
+ for (i = 1; i <= cnt; i++)
+ queue_push(&q, i);
+ return q;
+ }
+
+ bool write_testcase(const char *dir) {
+ return testcase_write($self, dir, TESTCASE_RESULT_TRANSACTION | TESTCASE_RESULT_PROBLEMS, 0, 0);
+ }
+}
+
+%extend Transaction {
+ static const int SOLVER_TRANSACTION_IGNORE = SOLVER_TRANSACTION_IGNORE;
+ static const int SOLVER_TRANSACTION_ERASE = SOLVER_TRANSACTION_ERASE;
+ static const int SOLVER_TRANSACTION_REINSTALLED = SOLVER_TRANSACTION_REINSTALLED;
+ static const int SOLVER_TRANSACTION_DOWNGRADED = SOLVER_TRANSACTION_DOWNGRADED;
+ static const int SOLVER_TRANSACTION_CHANGED = SOLVER_TRANSACTION_CHANGED;
+ static const int SOLVER_TRANSACTION_UPGRADED = SOLVER_TRANSACTION_UPGRADED;
+ static const int SOLVER_TRANSACTION_OBSOLETED = SOLVER_TRANSACTION_OBSOLETED;
+ static const int SOLVER_TRANSACTION_INSTALL = SOLVER_TRANSACTION_INSTALL;
+ static const int SOLVER_TRANSACTION_REINSTALL = SOLVER_TRANSACTION_REINSTALL;
+ static const int SOLVER_TRANSACTION_DOWNGRADE = SOLVER_TRANSACTION_DOWNGRADE;
+ static const int SOLVER_TRANSACTION_CHANGE = SOLVER_TRANSACTION_CHANGE;
+ static const int SOLVER_TRANSACTION_UPGRADE = SOLVER_TRANSACTION_UPGRADE;
+ static const int SOLVER_TRANSACTION_OBSOLETES = SOLVER_TRANSACTION_OBSOLETES;
+ static const int SOLVER_TRANSACTION_MULTIINSTALL = SOLVER_TRANSACTION_MULTIINSTALL;
+ static const int SOLVER_TRANSACTION_MULTIREINSTALL = SOLVER_TRANSACTION_MULTIREINSTALL;
+ static const int SOLVER_TRANSACTION_MAXTYPE = SOLVER_TRANSACTION_MAXTYPE;
+ static const int SOLVER_TRANSACTION_SHOW_ACTIVE = SOLVER_TRANSACTION_SHOW_ACTIVE;
+ static const int SOLVER_TRANSACTION_SHOW_ALL = SOLVER_TRANSACTION_SHOW_ALL;
+ static const int SOLVER_TRANSACTION_SHOW_OBSOLETES = SOLVER_TRANSACTION_SHOW_OBSOLETES;
+ static const int SOLVER_TRANSACTION_SHOW_MULTIINSTALL = SOLVER_TRANSACTION_SHOW_MULTIINSTALL;
+ static const int SOLVER_TRANSACTION_CHANGE_IS_REINSTALL = SOLVER_TRANSACTION_CHANGE_IS_REINSTALL;
+ static const int SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE = SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE;
+ static const int SOLVER_TRANSACTION_MERGE_VENDORCHANGES = SOLVER_TRANSACTION_MERGE_VENDORCHANGES;
+ static const int SOLVER_TRANSACTION_MERGE_ARCHCHANGES = SOLVER_TRANSACTION_MERGE_ARCHCHANGES;
+ static const int SOLVER_TRANSACTION_RPM_ONLY = SOLVER_TRANSACTION_RPM_ONLY;
+ static const int SOLVER_TRANSACTION_ARCHCHANGE = SOLVER_TRANSACTION_ARCHCHANGE;
+ static const int SOLVER_TRANSACTION_VENDORCHANGE = SOLVER_TRANSACTION_VENDORCHANGE;
+ static const int SOLVER_TRANSACTION_KEEP_ORDERDATA = SOLVER_TRANSACTION_KEEP_ORDERDATA;
+ ~Transaction() {
+ transaction_free($self);
+ }
+#ifdef SWIGRUBY
+ %rename("isempty?") isempty;
+#endif
+ bool isempty() {
+ return $self->steps.count == 0;
+ }
+
+ %newobject othersolvable;
+ XSolvable *othersolvable(XSolvable *s) {
+ Id op = transaction_obs_pkg($self, s->id);
+ return new_XSolvable($self->pool, op);
+ }
+
+ %typemap(out) Queue allothersolvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+ %newobject allothersolvables;
+ Queue allothersolvables(XSolvable *s) {
+ Queue q;
+ queue_init(&q);
+ transaction_all_obs_pkgs($self, s->id, &q);
+ return q;
+ }
+
+ %typemap(out) Queue classify Queue2Array(TransactionClass *, 4, new_TransactionClass(arg1, arg2, id, idp[1], idp[2], idp[3]));
+ %newobject classify;
+ Queue classify(int mode = 0) {
+ Queue q;
+ queue_init(&q);
+ transaction_classify($self, mode, &q);
+ return q;
+ }
+
+ /* deprecated, use newsolvables instead */
+ %typemap(out) Queue newpackages Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+ %newobject newpackages;
+ Queue newpackages() {
+ Queue q;
+ int cut;
+ queue_init(&q);
+ cut = transaction_installedresult(self, &q);
+ queue_truncate(&q, cut);
+ return q;
+ }
+
+ /* deprecated, use keptsolvables instead */
+ %typemap(out) Queue keptpackages Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+ %newobject keptpackages;
+ Queue keptpackages() {
+ Queue q;
+ int cut;
+ queue_init(&q);
+ cut = transaction_installedresult(self, &q);
+ if (cut)
+ queue_deleten(&q, 0, cut);
+ return q;
+ }
+
+ %typemap(out) Queue newsolvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+ %newobject newsolvables;
+ Queue newsolvables() {
+ Queue q;
+ int cut;
+ queue_init(&q);
+ cut = transaction_installedresult(self, &q);
+ queue_truncate(&q, cut);
+ return q;
+ }
+
+ %typemap(out) Queue keptsolvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+ %newobject keptsolvables;
+ Queue keptsolvables() {
+ Queue q;
+ int cut;
+ queue_init(&q);
+ cut = transaction_installedresult(self, &q);
+ if (cut)
+ queue_deleten(&q, 0, cut);
+ return q;
+ }
+
+ %typemap(out) Queue steps Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+ %newobject steps;
+ Queue steps() {
+ Queue q;
+ queue_init_clone(&q, &$self->steps);
+ return q;
+ }
+
+ int steptype(XSolvable *s, int mode) {
+ return transaction_type($self, s->id, mode);
+ }
+ int calc_installsizechange() {
+ return transaction_calc_installsizechange($self);
+ }
+ void order(int flags=0) {
+ transaction_order($self, flags);
+ }
+}
+
+%extend TransactionClass {
+ TransactionClass(Transaction *trans, int mode, Id type, int count, Id fromid, Id toid) {
+ TransactionClass *cl = solv_calloc(1, sizeof(*cl));
+ cl->transaction = trans;
+ cl->mode = mode;
+ cl->type = type;
+ cl->count = count;
+ cl->fromid = fromid;
+ cl->toid = toid;
+ return cl;
+ }
+ %typemap(out) Queue solvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->transaction->pool, id));
+ %newobject solvables;
+ Queue solvables() {
+ Queue q;
+ queue_init(&q);
+ transaction_classify_pkgs($self->transaction, $self->mode, $self->type, $self->fromid, $self->toid, &q);
+ return q;
+ }
+ const char * const fromstr;
+ const char * const tostr;
+ %{
+ SWIGINTERN const char *TransactionClass_fromstr_get(TransactionClass *cl) {
+ return pool_id2str(cl->transaction->pool, cl->fromid);
+ }
+ SWIGINTERN const char *TransactionClass_tostr_get(TransactionClass *cl) {
+ return pool_id2str(cl->transaction->pool, cl->toid);
+ }
+ %}
+}
+
+%extend XRule {
+ XRule(Solver *solv, Id id) {
+ if (!id)
+ return 0;
+ XRule *xr = solv_calloc(1, sizeof(*xr));
+ xr->solv = solv;
+ xr->id = id;
+ return xr;
+ }
+ int const type;
+ %{
+ SWIGINTERN int XRule_type_get(XRule *xr) {
+ return solver_ruleclass(xr->solv, xr->id);
+ }
+ %}
+ %newobject info;
+ Ruleinfo *info() {
+ Id type, source, target, dep;
+ type = solver_ruleinfo($self->solv, $self->id, &source, &target, &dep);
+ return new_Ruleinfo($self, type, source, target, dep);
+ }
+ %typemap(out) Queue allinfos Queue2Array(Ruleinfo *, 4, new_Ruleinfo(arg1, id, idp[1], idp[2], idp[3]));
+ %newobject allinfos;
+ Queue allinfos() {
+ Queue q;
+ queue_init(&q);
+ solver_allruleinfos($self->solv, $self->id, &q);
+ return q;
+ }
+
+#if defined(SWIGTCL)
+ %rename("==") __eq__;
+#endif
+ bool __eq__(XRule *xr) {
+ return $self->solv == xr->solv && $self->id == xr->id;
+ }
+#if defined(SWIGTCL)
+ %rename("!=") __ne__;
+#endif
+ bool __ne__(XRule *xr) {
+ return !XRule___eq__($self, xr);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("repr") __repr__;
+#endif
+ %newobject __repr__;
+ const char *__repr__() {
+ char buf[20];
+ sprintf(buf, "<Rule #%d>", $self->id);
+ return solv_strdup(buf);
+ }
+}
+
+%extend Ruleinfo {
+ Ruleinfo(XRule *r, Id type, Id source, Id target, Id dep_id) {
+ Ruleinfo *ri = solv_calloc(1, sizeof(*ri));
+ ri->solv = r->solv;
+ ri->rid = r->id;
+ ri->type = type;
+ ri->source = source;
+ ri->target = target;
+ ri->dep_id = dep_id;
+ return ri;
+ }
+ %newobject solvable;
+ XSolvable * const solvable;
+ %newobject othersolvable;
+ XSolvable * const othersolvable;
+ %newobject dep;
+ Dep * const dep;
+ %{
+ SWIGINTERN XSolvable *Ruleinfo_solvable_get(Ruleinfo *ri) {
+ return new_XSolvable(ri->solv->pool, ri->source);
+ }
+ SWIGINTERN XSolvable *Ruleinfo_othersolvable_get(Ruleinfo *ri) {
+ return new_XSolvable(ri->solv->pool, ri->target);
+ }
+ SWIGINTERN Dep *Ruleinfo_dep_get(Ruleinfo *ri) {
+ return new_Dep(ri->solv->pool, ri->dep_id);
+ }
+ %}
+ const char *problemstr() {
+ return solver_problemruleinfo2str($self->solv, $self->type, $self->source, $self->target, $self->dep_id);
+ }
+}
+
+%extend XRepodata {
+ XRepodata(Repo *repo, Id id) {
+ XRepodata *xr = solv_calloc(1, sizeof(*xr));
+ xr->repo = repo;
+ xr->id = id;
+ return xr;
+ }
+ Id new_handle() {
+ return repodata_new_handle(repo_id2repodata($self->repo, $self->id));
+ }
+ void set_id(Id solvid, Id keyname, DepId id) {
+ repodata_set_id(repo_id2repodata($self->repo, $self->id), solvid, keyname, id);
+ }
+ void set_str(Id solvid, Id keyname, const char *str) {
+ repodata_set_str(repo_id2repodata($self->repo, $self->id), solvid, keyname, str);
+ }
+ void set_poolstr(Id solvid, Id keyname, const char *str) {
+ repodata_set_poolstr(repo_id2repodata($self->repo, $self->id), solvid, keyname, str);
+ }
+ void add_idarray(Id solvid, Id keyname, DepId id) {
+ repodata_add_idarray(repo_id2repodata($self->repo, $self->id), solvid, keyname, id);
+ }
+ void add_flexarray(Id solvid, Id keyname, Id handle) {
+ repodata_add_flexarray(repo_id2repodata($self->repo, $self->id), solvid, keyname, handle);
+ }
+ void set_checksum(Id solvid, Id keyname, Chksum *chksum) {
+ const unsigned char *buf = solv_chksum_get(chksum, 0);
+ if (buf)
+ repodata_set_bin_checksum(repo_id2repodata($self->repo, $self->id), solvid, keyname, solv_chksum_get_type(chksum), buf);
+ }
+ const char *lookup_str(Id solvid, Id keyname) {
+ return repodata_lookup_str(repo_id2repodata($self->repo, $self->id), solvid, keyname);
+ }
+ Queue lookup_idarray(Id solvid, Id keyname) {
+ Queue r;
+ queue_init(&r);
+ repodata_lookup_idarray(repo_id2repodata($self->repo, $self->id), solvid, keyname, &r);
+ return r;
+ }
+ %newobject lookup_checksum;
+ Chksum *lookup_checksum(Id solvid, Id keyname) {
+ Id type = 0;
+ const unsigned char *b = repodata_lookup_bin_checksum(repo_id2repodata($self->repo, $self->id), solvid, keyname, &type);
+ return solv_chksum_create_from_bin(type, b);
+ }
+ void internalize() {
+ repodata_internalize(repo_id2repodata($self->repo, $self->id));
+ }
+ void create_stubs() {
+ Repodata *data = repo_id2repodata($self->repo, $self->id);
+ data = repodata_create_stubs(data);
+ $self->id = data->repodataid;
+ }
+ bool write(FILE *fp) {
+ return repodata_write(repo_id2repodata($self->repo, $self->id), fp) == 0;
+ }
+ bool add_solv(FILE *fp, int flags = 0) {
+ Repodata *data = repo_id2repodata($self->repo, $self->id);
+ int r, oldstate = data->state;
+ data->state = REPODATA_LOADING;
+ r = repo_add_solv(data->repo, fp, flags | REPO_USE_LOADING);
+ if (r || data->state == REPODATA_LOADING)
+ data->state = oldstate;
+ return r;
+ }
+ void extend_to_repo() {
+ Repodata *data = repo_id2repodata($self->repo, $self->id);
+ repodata_extend_block(data, data->repo->start, data->repo->end - data->repo->start);
+ }
+#if defined(SWIGTCL)
+ %rename("==") __eq__;
+#endif
+ bool __eq__(XRepodata *xr) {
+ return $self->repo == xr->repo && $self->id == xr->id;
+ }
+#if defined(SWIGTCL)
+ %rename("!=") __ne__;
+#endif
+ bool __ne__(XRepodata *xr) {
+ return !XRepodata___eq__($self, xr);
+ }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("repr") __repr__;
+#endif
+ %newobject __repr__;
+ const char *__repr__() {
+ char buf[20];
+ sprintf(buf, "<Repodata #%d>", $self->id);
+ return solv_strdup(buf);
+ }
+}
+
+#ifdef ENABLE_PUBKEY
+%extend Solvsig {
+ Solvsig(FILE *fp) {
+ return solvsig_create(fp);
+ }
+ ~Solvsig() {
+ solvsig_free($self);
+ }
+ %newobject Chksum;
+ Chksum *Chksum() {
+ return $self->htype ? (Chksum *)solv_chksum_create($self->htype) : 0;
+ }
+#ifdef ENABLE_PGPVRFY
+ %newobject verify;
+ XSolvable *verify(Repo *repo, Chksum *chksum) {
+ Id p = solvsig_verify($self, repo, chksum);
+ return new_XSolvable(repo->pool, p);
+ }
+#endif
+}
+#endif
+
+%extend Alternative {
+ static const int SOLVER_ALTERNATIVE_TYPE_RULE = SOLVER_ALTERNATIVE_TYPE_RULE;
+ static const int SOLVER_ALTERNATIVE_TYPE_RECOMMENDS = SOLVER_ALTERNATIVE_TYPE_RECOMMENDS;
+ static const int SOLVER_ALTERNATIVE_TYPE_SUGGESTS = SOLVER_ALTERNATIVE_TYPE_SUGGESTS;
+
+ ~Alternative() {
+ queue_free(&$self->choices);
+ solv_free($self);
+ }
+ %newobject chosen;
+ XSolvable * const chosen;
+ %newobject rule;
+ XRule * const rule;
+ %newobject depsolvable;
+ XSolvable * const depsolvable;
+ %newobject dep;
+ Dep * const dep;
+ %{
+ SWIGINTERN XSolvable *Alternative_chosen_get(Alternative *a) {
+ return new_XSolvable(a->solv->pool, a->chosen_id);
+ }
+ SWIGINTERN XRule *Alternative_rule_get(Alternative *a) {
+ return new_XRule(a->solv, a->rid);
+ }
+ SWIGINTERN XSolvable *Alternative_depsolvable_get(Alternative *a) {
+ return new_XSolvable(a->solv->pool, a->from_id);
+ }
+ SWIGINTERN Dep *Alternative_dep_get(Alternative *a) {
+ return new_Dep(a->solv->pool, a->dep_id);
+ }
+ %}
+
+ Queue choices_raw() {
+ Queue r;
+ queue_init_clone(&r, &$self->choices);
+ return r;
+ }
+
+ %typemap(out) Queue choices Queue2Array(XSolvable *, 1, new_XSolvable(arg1->solv->pool, id));
+ Queue choices() {
+ int i;
+ Queue r;
+ queue_init_clone(&r, &$self->choices);
+ for (i = 0; i < r.count; i++)
+ if (r.elements[i] < 0)
+ r.elements[i] = -r.elements[i];
+ return r;
+ }
+
+#if defined(SWIGPERL) || defined(SWIGTCL)
+ %rename("str") __str__;
+#endif
+ const char *__str__() {
+ return solver_alternative2str($self->solv, $self->type, $self->type == SOLVER_ALTERNATIVE_TYPE_RULE ? $self->rid : $self->dep_id, $self->from_id);
+ }
+}
+
+#if defined(SWIGTCL)
+%init %{
+ Tcl_Eval(interp,
+"proc solv::iter {varname iter body} {\n"\
+" while 1 {\n"\
+" set value [$iter __next__]\n"\
+" if {$value eq \"NULL\"} { break }\n"\
+" uplevel [list set $varname $value]\n"\
+" set code [catch {uplevel $body} result]\n"\
+" switch -exact -- $code {\n"\
+" 0 {}\n"\
+" 3 { return }\n"\
+" 4 {}\n"\
+" default { return -code $code $result }\n"\
+" }\n"\
+" }\n"\
+"}\n"
+ );
+%}
+#endif
+
--- /dev/null
+FIND_PACKAGE (TCL)
+
+SET (SWIG_TCL_FLAGS -namespace -pkgversion ${VERSION})
+
+EXECUTE_PROCESS (
+ COMMAND echo "puts -nonewline [lindex [::tcl::tm::list] end]"
+ COMMAND ${TCL_TCLSH}
+ OUTPUT_VARIABLE TCL_INSTALL_DIR
+)
+
+MESSAGE (STATUS "Tclsh executable: ${TCL_TCLSH}")
+MESSAGE (STATUS "Tcl installation dir: ${TCL_INSTALL_DIR}")
+
+ADD_CUSTOM_COMMAND (
+ OUTPUT solv_tcl.c
+ COMMAND ${SWIG_EXECUTABLE} ${SWIG_FLAGS} -tcl ${SWIG_TCL_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_tcl.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
+)
+
+ADD_DEFINITIONS(-Wno-unused)
+INCLUDE_DIRECTORIES (${TCL_INCLUDE_PATH})
+
+ADD_LIBRARY (bindings_tcl SHARED solv_tcl.c)
+SET_TARGET_PROPERTIES (bindings_tcl PROPERTIES PREFIX "" OUTPUT_NAME "solv-${VERSION}" INSTALL_NAME_DIR "${TCL_INSTALL_DIR}")
+TARGET_LINK_LIBRARIES (bindings_tcl libsolvext libsolv ${TCL_LIBRARY} ${SYSTEM_LIBRARIES})
+INSTALL (TARGETS bindings_tcl LIBRARY DESTINATION ${TCL_INSTALL_DIR})
+
+ADD_CUSTOM_COMMAND (
+ OUTPUT solv.tm
+ COMMAND sed -e "s/__VERSION__/${VERSION}/" ${CMAKE_SOURCE_DIR}/bindings/tcl/solv.tm.in >${CMAKE_CURRENT_BINARY_DIR}/solv.tm
+ DEPENDS ${CMAKE_SOURCE_DIR}/bindings/tcl/solv.tm.in
+ COMMENT "Creating Tcl module to load libsolv"
+)
+ADD_CUSTOM_TARGET (solv_tm ALL DEPENDS solv.tm)
+SET_SOURCE_FILES_PROPERTIES (solv.tm PROPERTIES GENERATED TRUE)
+
+INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/solv.tm DESTINATION ${TCL_INSTALL_DIR} RENAME solv-${VERSION}.tm)
--- /dev/null
+package require Tcl
+
+#package provide solv __VERSION__
+load [::file join [::file dirname [::info script]] "solv-__VERSION__[::info sharedlibextension]"]
--- /dev/null
+
+IF (CHECK_INCLUDE_DIR)
+ # Already in cache, be silent
+ SET(CHECK_FIND_QUIETLY TRUE)
+ENDIF (CHECK_INCLUDE_DIR)
+
+FIND_PATH(CHECK_INCLUDE_DIR NAMES check.h)
+
+# Look for the library.
+FIND_LIBRARY(CHECK_LIBRARY NAMES check)
+
+IF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.4)
+ # handle the QUIETLY and REQUIRED arguments and set CHECK_FOUND to TRUE if
+ # all listed variables are TRUE
+ INCLUDE(FindPackageHandleStandardArgs)
+
+ FIND_PACKAGE_HANDLE_STANDARD_ARGS(Check "Please install 'check' and 'check-devel' packages" CHECK_LIBRARY CHECK_INCLUDE_DIR)
+ENDIF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.4)
+
+IF(CHECK_FOUND)
+ SET( CHECK_LIBRARIES ${CHECK_LIBRARY} )
+ELSE(CHECK_FOUND)
+ SET( CHECK_LIBRARIES )
+ENDIF(CHECK_FOUND)
+
+MARK_AS_ADVANCED(CHECK_INCLUDE_DIR)
+MARK_AS_ADVANCED(CHECK_LIBRARY)
--- /dev/null
+# - Find expat
+# Find the native EXPAT headers and libraries.
+#
+# EXPAT_INCLUDE_DIRS - where to find expat.h, etc.
+# EXPAT_LIBRARIES - List of libraries when using expat.
+# EXPAT_FOUND - True if expat found.
+
+# Look for the header file.
+FIND_PATH(EXPAT_INCLUDE_DIR NAMES expat.h)
+MARK_AS_ADVANCED(EXPAT_INCLUDE_DIR)
+
+# Look for the library.
+FIND_LIBRARY(EXPAT_LIBRARY NAMES expat)
+MARK_AS_ADVANCED(EXPAT_LIBRARY)
+
+# Copy the results to the output variables.
+IF(EXPAT_INCLUDE_DIR AND EXPAT_LIBRARY)
+ SET(EXPAT_FOUND 1)
+ SET(EXPAT_LIBRARIES ${EXPAT_LIBRARY})
+ SET(EXPAT_INCLUDE_DIRS ${EXPAT_INCLUDE_DIR})
+ELSE(EXPAT_INCLUDE_DIR AND EXPAT_LIBRARY)
+ SET(EXPAT_FOUND 0)
+ SET(EXPAT_LIBRARIES)
+ SET(EXPAT_INCLUDE_DIRS)
+ENDIF(EXPAT_INCLUDE_DIR AND EXPAT_LIBRARY)
+
+# Report the results.
+IF(NOT EXPAT_FOUND)
+ SET(EXPAT_DIR_MESSAGE
+ "EXPAT was not found. Make sure EXPAT_LIBRARY and EXPAT_INCLUDE_DIR are set.")
+ IF(NOT EXPAT_FIND_QUIETLY)
+ MESSAGE(STATUS "${EXPAT_DIR_MESSAGE}")
+ ELSE(NOT EXPAT_FIND_QUIETLY)
+ IF(EXPAT_FIND_REQUIRED)
+ MESSAGE(FATAL_ERROR "${EXPAT_DIR_MESSAGE}")
+ ENDIF(EXPAT_FIND_REQUIRED)
+ ENDIF(NOT EXPAT_FIND_QUIETLY)
+ENDIF(NOT EXPAT_FOUND)
--- /dev/null
+# - Find lzma
+# Find the native LZMA headers and library
+#
+# LZMA_INCLUDE_DIR - where to find lzma.h, etc.
+# LZMA_LIBRARIES - List of libraries when using liblzma.
+# LZMA_FOUND - True if liblzma found.
+
+IF (LZMA_INCLUDE_DIR)
+ # Already in cache, be silent
+ SET(LZMA_FIND_QUIETLY TRUE)
+ENDIF (LZMA_INCLUDE_DIR)
+
+FIND_PATH(LZMA_INCLUDE_DIR lzma.h)
+FIND_LIBRARY(LZMA_LIBRARY NAMES lzma liblzma)
+
+# handle the QUIETLY and REQUIRED arguments and set LZMA_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZMA DEFAULT_MSG LZMA_LIBRARY LZMA_INCLUDE_DIR)
+
+IF(LZMA_FOUND)
+ SET( LZMA_LIBRARIES ${LZMA_LIBRARY} )
+ELSE(LZMA_FOUND)
+ SET( LZMA_LIBRARIES )
+ENDIF(LZMA_FOUND)
--- /dev/null
+# FindLibSolv - Find libsolv headers and libraries.
+#
+# Sample:
+#
+# SET( LibSolv_USE_STATIC_LIBS OFF )
+# FIND_PACKAGE( LibSolv REQUIRED ext )
+# IF( LibSolv_FOUND )
+# INCLUDE_DIRECTORIES( ${LibSolv_INCLUDE_DIRS} )
+# TARGET_LINK_LIBRARIES( ... ${LibSolv_LIBRARIES} )
+# ENDIF()
+#
+# Variables used by this module need to be set before calling find_package
+# (not that they are cmale cased like the modiulemane itself):
+#
+# LibSolv_USE_STATIC_LIBS Can be set to ON to force the use of the static
+# libsolv libraries. Defaults to OFF.
+#
+# Supported components:
+#
+# ext Also include libsolvext
+#
+# Variables provided by this module:
+#
+# LibSolv_FOUND Include dir, libsolv and all extra libraries
+# specified in the COMPONENTS list were found.
+#
+# LibSolv_LIBRARIES Link to these to use all the libraries you specified.
+#
+# LibSolv_INCLUDE_DIRS Include directories.
+#
+# For each component you specify in find_package(), the following (UPPER-CASE)
+# variables are set to pick and choose components instead of just using LibSolv_LIBRARIES:
+#
+# LIBSOLV_FOUND TRUE if libsolv was found
+# LIBSOLV_LIBRARY libsolv libraries
+#
+# LIBSOLV_${COMPONENT}_FOUND TRUE if the library component was found
+# LIBSOLV_${COMPONENT}_LIBRARY The libraries for the specified component
+#
+
+# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
+IF(LibSolv_USE_STATIC_LIBS)
+ SET( _ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
+ SET(CMAKE_FIND_LIBRARY_SUFFIXES .a )
+ENDIF()
+
+# Look for the header files
+UNSET(LibSolv_INCLUDE_DIRS CACHE)
+FIND_PATH(LibSolv_INCLUDE_DIRS NAMES solv/solvable.h)
+
+# Look for the core library
+UNSET(LIBSOLV_LIBRARY CACHE)
+FIND_LIBRARY(LIBSOLV_LIBRARY NAMES solv)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibSolv DEFAULT_MSG LIBSOLV_LIBRARY LibSolv_INCLUDE_DIRS)
+MARK_AS_ADVANCED(
+ LIBSOLV_FOUND
+ LIBSOLV_LIBRARY
+)
+
+# Prepare return values and collectiong more components
+SET(LibSolv_FOUND ${LIBSOLV_FOUND})
+SET(LibSolv_LIBRARIES ${LIBSOLV_LIBRARY})
+MARK_AS_ADVANCED(
+ LibSolv_FOUND
+ LibSolv_LIBRARIES
+ LibSolv_INCLUDE_DIRS
+)
+
+# Look for components
+FOREACH(COMPONENT ${LibSolv_FIND_COMPONENTS})
+ STRING(TOUPPER ${COMPONENT} _UPPERCOMPONENT)
+ UNSET(LIBSOLV_${_UPPERCOMPONENT}_LIBRARY CACHE)
+ FIND_LIBRARY(LIBSOLV_${_UPPERCOMPONENT}_LIBRARY NAMES solv${COMPONENT})
+ SET(LibSolv_${COMPONENT}_FIND_REQUIRED ${LibSolv_FIND_REQUIRED})
+ SET(LibSolv_${COMPONENT}_FIND_QUIETLY ${LibSolv_FIND_QUIETLY})
+ FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibSolv_${COMPONENT} DEFAULT_MSG LIBSOLV_${_UPPERCOMPONENT}_LIBRARY)
+ MARK_AS_ADVANCED(
+ LIBSOLV_${_UPPERCOMPONENT}_FOUND
+ LIBSOLV_${_UPPERCOMPONENT}_LIBRARY
+ )
+ IF(LIBSOLV_${_UPPERCOMPONENT}_FOUND)
+ SET(LibSolv_LIBRARIES ${LibSolv_LIBRARIES} ${LIBSOLV_${_UPPERCOMPONENT}_LIBRARY})
+ ELSE()
+ SET(LibSolv_FOUND FALSE)
+ ENDIF()
+ENDFOREACH()
+
+# restore CMAKE_FIND_LIBRARY_SUFFIXES
+IF(Solv_USE_STATIC_LIBS)
+ SET(CMAKE_FIND_LIBRARY_SUFFIXES ${_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES} )
+ENDIF()
+
+IF(LibSolv_FOUND AND NOT LibSolv_FIND_QUIETLY)
+ MESSAGE(STATUS "Found LibSolv: ${LibSolv_INCLUDE_DIRS} ${LibSolv_LIBRARIES}")
+ENDIF()
--- /dev/null
+# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> ... )
+#
+# This function is intended to be used in FindXXX.cmake modules files.
+# It handles the REQUIRED, QUIET and version-related arguments to FIND_PACKAGE().
+# It also sets the <UPPERCASED_NAME>_FOUND variable.
+# The package is considered found if all variables <var1>... listed contain
+# valid results, e.g. valid filepaths.
+#
+# There are two modes of this function. The first argument in both modes is
+# the name of the Find-module where it is called (in original casing).
+#
+# The first simple mode looks like this:
+# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> (DEFAULT_MSG|"Custom failure message") <var1>...<varN> )
+# If the variables <var1> to <varN> are all valid, then <UPPERCASED_NAME>_FOUND
+# will be set to TRUE.
+# If DEFAULT_MSG is given as second argument, then the function will generate
+# itself useful success and error messages. You can also supply a custom error message
+# for the failure case. This is not recommended.
+#
+# The second mode is more powerful and also supports version checking:
+# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME [REQUIRED_VARS <var1>...<varN>]
+# [VERSION_VAR <versionvar>]
+# [HANDLE_COMPONENTS]
+# [CONFIG_MODE]
+# [FAIL_MESSAGE "Custom failure message"] )
+#
+# As above, if <var1> through <varN> are all valid, <UPPERCASED_NAME>_FOUND
+# will be set to TRUE.
+# After REQUIRED_VARS the variables which are required for this package are listed.
+# Following VERSION_VAR the name of the variable can be specified which holds
+# the version of the package which has been found. If this is done, this version
+# will be checked against the (potentially) specified required version used
+# in the find_package() call. The EXACT keyword is also handled. The default
+# messages include information about the required version and the version
+# which has been actually found, both if the version is ok or not.
+# If the package supports components, use the HANDLE_COMPONENTS option to enable
+# handling them. In this case, find_package_handle_standard_args() will report
+# which components have been found and which are missing, and the <NAME>_FOUND
+# variable will be set to FALSE if any of the required components (i.e. not the
+# ones listed after OPTIONAL_COMPONENTS) are missing.
+# Use the option CONFIG_MODE if your FindXXX.cmake module is a wrapper for
+# a find_package(... NO_MODULE) call. In this case VERSION_VAR will be set
+# to <NAME>_VERSION and the macro will automatically check whether the
+# Config module was found.
+# Via FAIL_MESSAGE a custom failure message can be specified, if this is not
+# used, the default message will be displayed.
+#
+# Example for mode 1:
+#
+# FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibXml2 DEFAULT_MSG LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR)
+#
+# LibXml2 is considered to be found, if both LIBXML2_LIBRARY and
+# LIBXML2_INCLUDE_DIR are valid. Then also LIBXML2_FOUND is set to TRUE.
+# If it is not found and REQUIRED was used, it fails with FATAL_ERROR,
+# independent whether QUIET was used or not.
+# If it is found, success will be reported, including the content of <var1>.
+# On repeated Cmake runs, the same message won't be printed again.
+#
+# Example for mode 2:
+#
+# FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON REQUIRED_VARS BISON_EXECUTABLE
+# VERSION_VAR BISON_VERSION)
+# In this case, BISON is considered to be found if the variable(s) listed
+# after REQUIRED_VAR are all valid, i.e. BISON_EXECUTABLE in this case.
+# Also the version of BISON will be checked by using the version contained
+# in BISON_VERSION.
+# Since no FAIL_MESSAGE is given, the default messages will be printed.
+#
+# Another example for mode 2:
+#
+# FIND_PACKAGE(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4)
+# FIND_PACKAGE_HANDLE_STANDARD_ARGS(Automoc4 CONFIG_MODE)
+# In this case, FindAutmoc4.cmake wraps a call to FIND_PACKAGE(Automoc4 NO_MODULE)
+# and adds an additional search directory for automoc4.
+# The following FIND_PACKAGE_HANDLE_STANDARD_ARGS() call produces a proper
+# success/error message.
+
+#=============================================================================
+# Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the names of Kitware, Inc., the Insight Software Consortium,
+# nor the names of their contributors may be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+INCLUDE(FindPackageMessage)
+INCLUDE(_CMakeParseArguments)
+
+# internal helper macro
+MACRO(_FPHSA_FAILURE_MESSAGE _msg)
+ IF (${_NAME}_FIND_REQUIRED)
+ MESSAGE(FATAL_ERROR "${_msg}")
+ ELSE (${_NAME}_FIND_REQUIRED)
+ IF (NOT ${_NAME}_FIND_QUIETLY)
+ MESSAGE(STATUS "${_msg}")
+ ENDIF (NOT ${_NAME}_FIND_QUIETLY)
+ ENDIF (${_NAME}_FIND_REQUIRED)
+ENDMACRO(_FPHSA_FAILURE_MESSAGE _msg)
+
+
+# internal helper macro to generate the failure message when used in CONFIG_MODE:
+MACRO(_FPHSA_HANDLE_FAILURE_CONFIG_MODE)
+ # <name>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found:
+ IF(${_NAME}_CONFIG)
+ _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing: ${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})")
+ ELSE(${_NAME}_CONFIG)
+ # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version.
+ # List them all in the error message:
+ IF(${_NAME}_CONSIDERED_CONFIGS)
+ SET(configsText "")
+ LIST(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount)
+ MATH(EXPR configsCount "${configsCount} - 1")
+ FOREACH(currentConfigIndex RANGE ${configsCount})
+ LIST(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename)
+ LIST(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version)
+ SET(configsText "${configsText} ${filename} (version ${version})\n")
+ ENDFOREACH(currentConfigIndex)
+ _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}")
+
+ ELSE(${_NAME}_CONSIDERED_CONFIGS)
+ # Simple case: No Config-file was found at all:
+ _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}")
+ ENDIF(${_NAME}_CONSIDERED_CONFIGS)
+ ENDIF(${_NAME}_CONFIG)
+ENDMACRO(_FPHSA_HANDLE_FAILURE_CONFIG_MODE)
+
+
+FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
+
+# set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in
+# new extended or in the "old" mode:
+ SET(options CONFIG_MODE HANDLE_COMPONENTS)
+ SET(oneValueArgs FAIL_MESSAGE VERSION_VAR)
+ SET(multiValueArgs REQUIRED_VARS)
+ SET(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} )
+ LIST(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX)
+
+ IF(${INDEX} EQUAL -1)
+ SET(FPHSA_FAIL_MESSAGE ${_FIRST_ARG})
+ SET(FPHSA_REQUIRED_VARS ${ARGN})
+ SET(FPHSA_VERSION_VAR)
+ ELSE(${INDEX} EQUAL -1)
+
+ CMAKE_PARSE_ARGUMENTS(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN})
+
+ IF(FPHSA_UNPARSED_ARGUMENTS)
+ MESSAGE(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"")
+ ENDIF(FPHSA_UNPARSED_ARGUMENTS)
+
+ IF(NOT FPHSA_FAIL_MESSAGE)
+ SET(FPHSA_FAIL_MESSAGE "DEFAULT_MSG")
+ ENDIF(NOT FPHSA_FAIL_MESSAGE)
+ ENDIF(${INDEX} EQUAL -1)
+
+# now that we collected all arguments, process them
+
+ IF("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG")
+ SET(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}")
+ ENDIF("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG")
+
+ # In config-mode, we rely on the variable <package>_CONFIG, which is set by find_package()
+ # when it successfully found the config-file, including version checking:
+ IF(FPHSA_CONFIG_MODE)
+ LIST(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG)
+ LIST(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS)
+ SET(FPHSA_VERSION_VAR ${_NAME}_VERSION)
+ ENDIF(FPHSA_CONFIG_MODE)
+
+ IF(NOT FPHSA_REQUIRED_VARS)
+ MESSAGE(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()")
+ ENDIF(NOT FPHSA_REQUIRED_VARS)
+
+ LIST(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR)
+
+ STRING(TOUPPER ${_NAME} _NAME_UPPER)
+ STRING(TOLOWER ${_NAME} _NAME_LOWER)
+
+ # collect all variables which were not found, so they can be printed, so the
+ # user knows better what went wrong (#6375)
+ SET(MISSING_VARS "")
+ SET(DETAILS "")
+ SET(${_NAME_UPPER}_FOUND TRUE)
+ # check if all passed variables are valid
+ FOREACH(_CURRENT_VAR ${FPHSA_REQUIRED_VARS})
+ IF(NOT ${_CURRENT_VAR})
+ SET(${_NAME_UPPER}_FOUND FALSE)
+ SET(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}")
+ ELSE(NOT ${_CURRENT_VAR})
+ SET(DETAILS "${DETAILS}[${${_CURRENT_VAR}}]")
+ ENDIF(NOT ${_CURRENT_VAR})
+ ENDFOREACH(_CURRENT_VAR)
+
+ # component handling
+ SET(FOUND_COMPONENTS_MSG "")
+ SET(MISSING_COMPONENTS_MSG "")
+
+ IF(FPHSA_HANDLE_COMPONENTS)
+ FOREACH(comp ${${_NAME}_FIND_COMPONENTS})
+ IF(${_NAME}_${comp}_FOUND)
+
+ IF(NOT FOUND_COMPONENTS_MSG)
+ SET(FOUND_COMPONENTS_MSG "found components: ")
+ ENDIF()
+ SET(FOUND_COMPONENTS_MSG "${FOUND_COMPONENTS_MSG} ${comp}")
+
+ ELSE()
+
+ IF(NOT MISSING_COMPONENTS_MSG)
+ SET(MISSING_COMPONENTS_MSG "missing components: ")
+ ENDIF()
+ SET(MISSING_COMPONENTS_MSG "${MISSING_COMPONENTS_MSG} ${comp}")
+
+ IF(${_NAME}_FIND_REQUIRED_${comp})
+ SET(${_NAME_UPPER}_FOUND FALSE)
+ SET(MISSING_VARS "${MISSING_VARS} ${comp}")
+ ENDIF()
+
+ ENDIF()
+ ENDFOREACH(comp)
+ SET(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}")
+ SET(DETAILS "${DETAILS}[c${COMPONENT_MSG}]")
+ ENDIF(FPHSA_HANDLE_COMPONENTS)
+
+ # version handling:
+ SET(VERSION_MSG "")
+ SET(VERSION_OK TRUE)
+ SET(VERSION ${${FPHSA_VERSION_VAR}} )
+ IF (${_NAME}_FIND_VERSION)
+
+ IF(VERSION)
+
+ IF(${_NAME}_FIND_VERSION_EXACT) # exact version required
+ IF (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}")
+ SET(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"")
+ SET(VERSION_OK FALSE)
+ ELSE (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}")
+ SET(VERSION_MSG "(found suitable exact version \"${VERSION}\")")
+ ENDIF (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}")
+
+ ELSE(${_NAME}_FIND_VERSION_EXACT) # minimum version specified:
+ IF ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}")
+ SET(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"")
+ SET(VERSION_OK FALSE)
+ ELSE ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}")
+ SET(VERSION_MSG "(found suitable version \"${VERSION}\", required is \"${${_NAME}_FIND_VERSION}\")")
+ ENDIF ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}")
+ ENDIF(${_NAME}_FIND_VERSION_EXACT)
+
+ ELSE(VERSION)
+
+ # if the package was not found, but a version was given, add that to the output:
+ IF(${_NAME}_FIND_VERSION_EXACT)
+ SET(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")")
+ ELSE(${_NAME}_FIND_VERSION_EXACT)
+ SET(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")")
+ ENDIF(${_NAME}_FIND_VERSION_EXACT)
+
+ ENDIF(VERSION)
+ ELSE (${_NAME}_FIND_VERSION)
+ IF(VERSION)
+ SET(VERSION_MSG "(found version \"${VERSION}\")")
+ ENDIF(VERSION)
+ ENDIF (${_NAME}_FIND_VERSION)
+
+ IF(VERSION_OK)
+ SET(DETAILS "${DETAILS}[v${VERSION}(${${_NAME}_FIND_VERSION})]")
+ ELSE(VERSION_OK)
+ SET(${_NAME_UPPER}_FOUND FALSE)
+ ENDIF(VERSION_OK)
+
+
+ # print the result:
+ IF (${_NAME_UPPER}_FOUND)
+ FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}")
+ ELSE (${_NAME_UPPER}_FOUND)
+
+ IF(FPHSA_CONFIG_MODE)
+ _FPHSA_HANDLE_FAILURE_CONFIG_MODE()
+ ELSE(FPHSA_CONFIG_MODE)
+ IF(NOT VERSION_OK)
+ _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})")
+ ELSE(NOT VERSION_OK)
+ _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}")
+ ENDIF(NOT VERSION_OK)
+ ENDIF(FPHSA_CONFIG_MODE)
+
+ ENDIF (${_NAME_UPPER}_FOUND)
+
+ SET(${_NAME_UPPER}_FOUND ${${_NAME_UPPER}_FOUND} PARENT_SCOPE)
+
+ENDFUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _FIRST_ARG)
--- /dev/null
+# - Find Ruby
+# This module finds if Ruby is installed and determines where the include files
+# and libraries are. Ruby 1.8 and 1.9 are supported.
+#
+# The minimum required version of Ruby can be specified using the
+# standard syntax, e.g. FIND_PACKAGE(Ruby 1.8)
+#
+# It also determines what the name of the library is. This
+# code sets the following variables:
+#
+# RUBY_EXECUTABLE = full path to the ruby binary
+# RUBY_INCLUDE_DIRS = include dirs to be used when using the ruby library
+# RUBY_LIBRARY = full path to the ruby library
+# RUBY_VERSION = the version of ruby which was found, e.g. "1.8.7"
+# RUBY_FOUND = set to true if ruby ws found successfully
+#
+# RUBY_INCLUDE_PATH = same as RUBY_INCLUDE_DIRS, only provided for compatibility reasons, don't use it
+
+#=============================================================================
+# Copyright 2004-2009 Kitware, Inc.
+# Copyright 2008-2009 Alexander Neundorf <neundorf@kde.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the names of Kitware, Inc., the Insight Software Consortium,
+# nor the names of their contributors may be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+# RUBY_ARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"archdir"@:>@)'`
+# RUBY_SITEARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitearchdir"@:>@)'`
+# RUBY_SITEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitelibdir"@:>@)'`
+# RUBY_LIBDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"libdir"@:>@)'`
+# RUBY_LIBRUBYARG=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"LIBRUBYARG_SHARED"@:>@)'`
+
+# uncomment the following line to get debug output for this file
+# SET(_RUBY_DEBUG_OUTPUT TRUE)
+
+# Determine the list of possible names of the ruby executable depending
+# on which version of ruby is required
+SET(_RUBY_POSSIBLE_EXECUTABLE_NAMES ruby)
+
+# if 1.9 is required, don't look for ruby18 and ruby1.8, default to version 1.8
+IF(Ruby_FIND_VERSION_MAJOR AND Ruby_FIND_VERSION_MINOR)
+ SET(Ruby_FIND_VERSION_SHORT_NODOT "${Ruby_FIND_VERSION_MAJOR}${RUBY_FIND_VERSION_MINOR}")
+ELSE(Ruby_FIND_VERSION_MAJOR AND Ruby_FIND_VERSION_MINOR)
+ SET(Ruby_FIND_VERSION_SHORT_NODOT "18")
+ENDIF(Ruby_FIND_VERSION_MAJOR AND Ruby_FIND_VERSION_MINOR)
+
+SET(_RUBY_POSSIBLE_EXECUTABLE_NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES} ruby1.9 ruby19)
+
+# if we want a version below 1.9, also look for ruby 1.8
+IF("${Ruby_FIND_VERSION_SHORT_NODOT}" VERSION_LESS "19")
+ SET(_RUBY_POSSIBLE_EXECUTABLE_NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES} ruby1.8 ruby18)
+ENDIF("${Ruby_FIND_VERSION_SHORT_NODOT}" VERSION_LESS "19")
+
+FIND_PROGRAM(RUBY_EXECUTABLE NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES})
+
+
+IF(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
+ FUNCTION(_RUBY_CONFIG_VAR RBVAR OUTVAR)
+ EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['${RBVAR}']"
+ RESULT_VARIABLE _RUBY_SUCCESS
+ OUTPUT_VARIABLE _RUBY_OUTPUT
+ ERROR_QUIET)
+ IF(_RUBY_SUCCESS OR NOT _RUBY_OUTPUT)
+ EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['${RBVAR}']"
+ RESULT_VARIABLE _RUBY_SUCCESS
+ OUTPUT_VARIABLE _RUBY_OUTPUT
+ ERROR_QUIET)
+ ENDIF(_RUBY_SUCCESS OR NOT _RUBY_OUTPUT)
+ SET(${OUTVAR} "${_RUBY_OUTPUT}" PARENT_SCOPE)
+ ENDFUNCTION(_RUBY_CONFIG_VAR)
+
+
+ # query the ruby version
+ _RUBY_CONFIG_VAR("MAJOR" RUBY_VERSION_MAJOR)
+ _RUBY_CONFIG_VAR("MINOR" RUBY_VERSION_MINOR)
+ _RUBY_CONFIG_VAR("TEENY" RUBY_VERSION_PATCH)
+
+ # query the different directories
+ _RUBY_CONFIG_VAR("archdir" RUBY_ARCH_DIR)
+ _RUBY_CONFIG_VAR("arch" RUBY_ARCH)
+ _RUBY_CONFIG_VAR("rubyhdrdir" RUBY_HDR_DIR)
+ _RUBY_CONFIG_VAR("libdir" RUBY_POSSIBLE_LIB_DIR)
+ _RUBY_CONFIG_VAR("rubylibdir" RUBY_RUBY_LIB_DIR)
+
+ # site_ruby
+ _RUBY_CONFIG_VAR("sitearchdir" RUBY_SITEARCH_DIR)
+ _RUBY_CONFIG_VAR("sitelibdir" RUBY_SITELIB_DIR)
+
+ # vendor_ruby available ?
+ EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print 'true' unless RbConfig::CONFIG['vendorarchdir'].nil?"
+ OUTPUT_VARIABLE RUBY_HAS_VENDOR_RUBY ERROR_QUIET)
+
+ IF(RUBY_HAS_VENDOR_RUBY)
+ _RUBY_CONFIG_VAR("vendorlibdir" RUBY_VENDORLIB_DIR)
+ _RUBY_CONFIG_VAR("vendorarchdir" RUBY_VENDORARCH_DIR)
+ ENDIF(RUBY_HAS_VENDOR_RUBY)
+
+ # save the results in the cache so we don't have to run ruby the next time again
+ SET(RUBY_VERSION_MAJOR ${RUBY_VERSION_MAJOR} CACHE PATH "The Ruby major version" FORCE)
+ SET(RUBY_VERSION_MINOR ${RUBY_VERSION_MINOR} CACHE PATH "The Ruby minor version" FORCE)
+ SET(RUBY_VERSION_PATCH ${RUBY_VERSION_PATCH} CACHE PATH "The Ruby patch version" FORCE)
+ SET(RUBY_ARCH_DIR ${RUBY_ARCH_DIR} CACHE PATH "The Ruby arch dir" FORCE)
+ SET(RUBY_HDR_DIR ${RUBY_HDR_DIR} CACHE PATH "The Ruby header dir (1.9)" FORCE)
+ SET(RUBY_POSSIBLE_LIB_DIR ${RUBY_POSSIBLE_LIB_DIR} CACHE PATH "The Ruby lib dir" FORCE)
+ SET(RUBY_RUBY_LIB_DIR ${RUBY_RUBY_LIB_DIR} CACHE PATH "The Ruby ruby-lib dir" FORCE)
+ SET(RUBY_SITEARCH_DIR ${RUBY_SITEARCH_DIR} CACHE PATH "The Ruby site arch dir" FORCE)
+ SET(RUBY_SITELIB_DIR ${RUBY_SITELIB_DIR} CACHE PATH "The Ruby site lib dir" FORCE)
+ SET(RUBY_HAS_VENDOR_RUBY ${RUBY_HAS_VENDOR_RUBY} CACHE BOOL "Vendor Ruby is available" FORCE)
+ SET(RUBY_VENDORARCH_DIR ${RUBY_VENDORARCH_DIR} CACHE PATH "The Ruby vendor arch dir" FORCE)
+ SET(RUBY_VENDORLIB_DIR ${RUBY_VENDORLIB_DIR} CACHE PATH "The Ruby vendor lib dir" FORCE)
+
+ MARK_AS_ADVANCED(
+ RUBY_ARCH_DIR
+ RUBY_ARCH
+ RUBY_HDR_DIR
+ RUBY_POSSIBLE_LIB_DIR
+ RUBY_RUBY_LIB_DIR
+ RUBY_SITEARCH_DIR
+ RUBY_SITELIB_DIR
+ RUBY_HAS_VENDOR_RUBY
+ RUBY_VENDORARCH_DIR
+ RUBY_VENDORLIB_DIR
+ RUBY_VERSION_MAJOR
+ RUBY_VERSION_MINOR
+ RUBY_VERSION_PATCH
+ )
+ENDIF(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
+
+# In case RUBY_EXECUTABLE could not be executed (e.g. cross compiling)
+# try to detect which version we found. This is not too good.
+IF(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
+ # by default assume 1.8.0
+ SET(RUBY_VERSION_MAJOR 1)
+ SET(RUBY_VERSION_MINOR 8)
+ SET(RUBY_VERSION_PATCH 0)
+ # check whether we found 1.9.x
+ IF(${RUBY_EXECUTABLE} MATCHES "ruby1.?9" OR RUBY_HDR_DIR)
+ SET(RUBY_VERSION_MAJOR 1)
+ SET(RUBY_VERSION_MINOR 9)
+ ENDIF(${RUBY_EXECUTABLE} MATCHES "ruby1.?9" OR RUBY_HDR_DIR)
+ENDIF(RUBY_EXECUTABLE AND NOT RUBY_VERSION_MAJOR)
+
+IF(RUBY_VERSION_MAJOR)
+ SET(RUBY_VERSION "${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH}")
+ SET(_RUBY_VERSION_SHORT "${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}")
+ SET(_RUBY_VERSION_SHORT_NODOT "${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}")
+ SET(_RUBY_NODOT_VERSION "${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}${RUBY_VERSION_PATCH}")
+ENDIF(RUBY_VERSION_MAJOR)
+
+FIND_PATH(RUBY_INCLUDE_DIR
+ NAMES ruby.h
+ HINTS
+ ${RUBY_HDR_DIR}
+ ${RUBY_ARCH_DIR}
+ /usr/lib/ruby/${_RUBY_VERSION_SHORT}/i586-linux-gnu/ )
+
+SET(RUBY_INCLUDE_DIRS ${RUBY_INCLUDE_DIR} )
+
+# if ruby > 1.8 is required or if ruby > 1.8 was found, search for the config.h dir
+IF( "${Ruby_FIND_VERSION_SHORT_NODOT}" GREATER 18 OR "${_RUBY_VERSION_SHORT_NODOT}" GREATER 18 OR RUBY_HDR_DIR)
+ FIND_PATH(RUBY_CONFIG_INCLUDE_DIR
+ NAMES ruby/config.h config.h
+ HINTS
+ ${RUBY_HDR_DIR}/${RUBY_ARCH}
+ ${RUBY_ARCH_DIR}
+ )
+
+ SET(RUBY_INCLUDE_DIRS ${RUBY_INCLUDE_DIRS} ${RUBY_CONFIG_INCLUDE_DIR} )
+ENDIF( "${Ruby_FIND_VERSION_SHORT_NODOT}" GREATER 18 OR "${_RUBY_VERSION_SHORT_NODOT}" GREATER 18 OR RUBY_HDR_DIR)
+
+
+# Determine the list of possible names for the ruby library
+SET(_RUBY_POSSIBLE_LIB_NAMES ruby ruby-static ruby${_RUBY_VERSION_SHORT} ruby${_RUBY_VERSION_SHORT_NODOT} ruby-${_RUBY_VERSION_SHORT} ruby-${RUBY_VERSION})
+
+IF(WIN32)
+ SET( _RUBY_MSVC_RUNTIME "" )
+ IF( MSVC60 )
+ SET( _RUBY_MSVC_RUNTIME "60" )
+ ENDIF( MSVC60 )
+ IF( MSVC70 )
+ SET( _RUBY_MSVC_RUNTIME "70" )
+ ENDIF( MSVC70 )
+ IF( MSVC71 )
+ SET( _RUBY_MSVC_RUNTIME "71" )
+ ENDIF( MSVC71 )
+ IF( MSVC80 )
+ SET( _RUBY_MSVC_RUNTIME "80" )
+ ENDIF( MSVC80 )
+ IF( MSVC90 )
+ SET( _RUBY_MSVC_RUNTIME "90" )
+ ENDIF( MSVC90 )
+
+ LIST(APPEND _RUBY_POSSIBLE_LIB_NAMES
+ "msvcr${_RUBY_MSVC_RUNTIME}-ruby${_RUBY_NODOT_VERSION}"
+ "msvcr${_RUBY_MSVC_RUNTIME}-ruby${_RUBY_NODOT_VERSION}-static"
+ "msvcrt-ruby${_RUBY_NODOT_VERSION}"
+ "msvcrt-ruby${_RUBY_NODOT_VERSION}-static" )
+ENDIF(WIN32)
+
+FIND_LIBRARY(RUBY_LIBRARY NAMES ${_RUBY_POSSIBLE_LIB_NAMES} HINTS ${RUBY_POSSIBLE_LIB_DIR} )
+
+INCLUDE(FindPackageHandleStandardArgs)
+SET(_RUBY_REQUIRED_VARS RUBY_EXECUTABLE RUBY_INCLUDE_DIR RUBY_LIBRARY)
+IF(_RUBY_VERSION_SHORT_NODOT GREATER 18)
+ LIST(APPEND _RUBY_REQUIRED_VARS RUBY_CONFIG_INCLUDE_DIR)
+ENDIF(_RUBY_VERSION_SHORT_NODOT GREATER 18)
+
+IF(_RUBY_DEBUG_OUTPUT)
+ MESSAGE(STATUS "--------FindRuby.cmake debug------------")
+ MESSAGE(STATUS "_RUBY_POSSIBLE_EXECUTABLE_NAMES: ${_RUBY_POSSIBLE_EXECUTABLE_NAMES}")
+ MESSAGE(STATUS "_RUBY_POSSIBLE_LIB_NAMES: ${_RUBY_POSSIBLE_LIB_NAMES}")
+ MESSAGE(STATUS "RUBY_ARCH_DIR: ${RUBY_ARCH_DIR}")
+ MESSAGE(STATUS "RUBY_HDR_DIR: ${RUBY_HDR_DIR}")
+ MESSAGE(STATUS "RUBY_POSSIBLE_LIB_DIR: ${RUBY_POSSIBLE_LIB_DIR}")
+ MESSAGE(STATUS "Found RUBY_VERSION: \"${RUBY_VERSION}\" , short: \"${_RUBY_VERSION_SHORT}\", nodot: \"${_RUBY_VERSION_SHORT_NODOT}\"")
+ MESSAGE(STATUS "_RUBY_REQUIRED_VARS: ${_RUBY_REQUIRED_VARS}")
+ MESSAGE(STATUS "RUBY_EXECUTABLE: ${RUBY_EXECUTABLE}")
+ MESSAGE(STATUS "RUBY_LIBRARY: ${RUBY_LIBRARY}")
+ MESSAGE(STATUS "RUBY_INCLUDE_DIR: ${RUBY_INCLUDE_DIR}")
+ MESSAGE(STATUS "RUBY_CONFIG_INCLUDE_DIR: ${RUBY_CONFIG_INCLUDE_DIR}")
+ MESSAGE(STATUS "--------------------")
+ENDIF(_RUBY_DEBUG_OUTPUT)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Ruby REQUIRED_VARS ${_RUBY_REQUIRED_VARS}
+ VERSION_VAR RUBY_VERSION )
+
+MARK_AS_ADVANCED(
+ RUBY_EXECUTABLE
+ RUBY_LIBRARY
+ RUBY_INCLUDE_DIR
+ RUBY_CONFIG_INCLUDE_DIR
+ )
+
+# Set some variables for compatibility with previous version of this file
+SET(RUBY_POSSIBLE_LIB_PATH ${RUBY_POSSIBLE_LIB_DIR})
+SET(RUBY_RUBY_LIB_PATH ${RUBY_RUBY_LIB_DIR})
+SET(RUBY_INCLUDE_PATH ${RUBY_INCLUDE_DIRS})
--- /dev/null
+# CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords> <multi_value_keywords> args...)
+#
+# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions for
+# parsing the arguments given to that macro or function.
+# It processes the arguments and defines a set of variables which hold the
+# values of the respective options.
+#
+# The <options> argument contains all options for the respective macro,
+# i.e. keywords which can be used when calling the macro without any value
+# following, like e.g. the OPTIONAL keyword of the install() command.
+#
+# The <one_value_keywords> argument contains all keywords for this macro
+# which are followed by one value, like e.g. DESTINATION keyword of the
+# install() command.
+#
+# The <multi_value_keywords> argument contains all keywords for this macro
+# which can be followed by more than one value, like e.g. the TARGETS or
+# FILES keywords of the install() command.
+#
+# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the
+# keywords listed in <options>, <one_value_keywords> and
+# <multi_value_keywords> a variable composed of the given <prefix>
+# followed by "_" and the name of the respective keyword.
+# These variables will then hold the respective value from the argument list.
+# For the <options> keywords this will be TRUE or FALSE.
+#
+# All remaining arguments are collected in a variable
+# <prefix>_UNPARSED_ARGUMENTS, this can be checked afterwards to see whether
+# your macro was called with unrecognized parameters.
+#
+# As an example here a my_install() macro, which takes similar arguments as the
+# real install() command:
+#
+# function(MY_INSTALL)
+# set(options OPTIONAL FAST)
+# set(oneValueArgs DESTINATION RENAME)
+# set(multiValueArgs TARGETS CONFIGURATIONS)
+# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+# ...
+#
+# Assume my_install() has been called like this:
+# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub)
+#
+# After the cmake_parse_arguments() call the macro will have set the following
+# variables:
+# MY_INSTALL_OPTIONAL = TRUE
+# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install()
+# MY_INSTALL_DESTINATION = "bin"
+# MY_INSTALL_RENAME = "" (was not used)
+# MY_INSTALL_TARGETS = "foo;bar"
+# MY_INSTALL_CONFIGURATIONS = "" (was not used)
+# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL"
+#
+# You can the continue and process these variables.
+#
+# Keywords terminate lists of values, e.g. if directly after a one_value_keyword
+# another recognized keyword follows, this is interpreted as the beginning of
+# the new option.
+# E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in
+# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would
+# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor.
+
+#=============================================================================
+# Copyright 2010 Alexander Neundorf <neundorf@kde.org>
+#
+# Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the names of Kitware, Inc., the Insight Software Consortium,
+# nor the names of their contributors may be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+
+if(__CMAKE_PARSE_ARGUMENTS_INCLUDED)
+ return()
+endif()
+set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE)
+
+
+function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames)
+ # first set all result variables to empty/FALSE
+ foreach(arg_name ${_singleArgNames} ${_multiArgNames})
+ set(${prefix}_${arg_name})
+ endforeach(arg_name)
+
+ foreach(option ${_optionNames})
+ set(${prefix}_${option} FALSE)
+ endforeach(option)
+
+ set(${prefix}_UNPARSED_ARGUMENTS)
+
+ set(insideValues FALSE)
+ set(currentArgName)
+
+ # now iterate over all arguments and fill the result variables
+ foreach(currentArg ${ARGN})
+ list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword
+ list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword
+ list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword
+
+ if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1)
+ if(insideValues)
+ if("${insideValues}" STREQUAL "SINGLE")
+ set(${prefix}_${currentArgName} ${currentArg})
+ set(insideValues FALSE)
+ elseif("${insideValues}" STREQUAL "MULTI")
+ list(APPEND ${prefix}_${currentArgName} ${currentArg})
+ endif()
+ else(insideValues)
+ list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg})
+ endif(insideValues)
+ else()
+ if(NOT ${optionIndex} EQUAL -1)
+ set(${prefix}_${currentArg} TRUE)
+ set(insideValues FALSE)
+ elseif(NOT ${singleArgIndex} EQUAL -1)
+ set(currentArgName ${currentArg})
+ set(${prefix}_${currentArgName})
+ set(insideValues "SINGLE")
+ elseif(NOT ${multiArgIndex} EQUAL -1)
+ set(currentArgName ${currentArg})
+ set(${prefix}_${currentArgName})
+ set(insideValues "MULTI")
+ endif()
+ endif()
+
+ endforeach(currentArg)
+
+ # propagate the result variables to the caller:
+ foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames})
+ set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE)
+ endforeach(arg_name)
+ set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE)
+
+endfunction(CMAKE_PARSE_ARGUMENTS _options _singleArgs _multiArgs)
--- /dev/null
+
+SET (libsolv_MANPAGES3
+ libsolv.3 libsolv-bindings.3 libsolv-constantids.3 libsolv-history.3
+ libsolv-pool.3)
+
+SET (libsolv_MANPAGES1
+ mergesolv.1 dumpsolv.1 installcheck.1 testsolv.1)
+
+IF (ENABLE_RPMDB)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} rpmdb2solv.1 rpms2solv.1)
+ENDIF (ENABLE_RPMDB)
+
+IF (ENABLE_RPMMD)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} repomdxml2solv.1 rpmmd2solv.1 updateinfoxml2solv.1 deltainfoxml2solv.1)
+ENDIF (ENABLE_RPMMD)
+
+IF (ENABLE_HELIXREPO)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} helix2solv.1)
+ENDIF (ENABLE_HELIXREPO)
+
+IF (ENABLE_SUSEREPO)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} susetags2solv.1)
+ENDIF (ENABLE_SUSEREPO)
+
+IF (ENABLE_COMPS)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} comps2solv.1)
+ENDIF (ENABLE_COMPS)
+
+IF (ENABLE_DEBIAN)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} deb2solv.1)
+ENDIF (ENABLE_DEBIAN)
+
+IF (ENABLE_MDKREPO)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} mdk2solv.1)
+ENDIF (ENABLE_MDKREPO)
+
+IF (ENABLE_ARCHREPO)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} archpkgs2solv.1 archrepo2solv.1)
+ENDIF (ENABLE_ARCHREPO)
+
+IF (ENABLE_APPDATA)
+SET (libsolv_MANPAGES1 ${libsolv_MANPAGES1} appdata2solv.1)
+ENDIF (ENABLE_APPDATA)
+
+INSTALL(FILES
+ ${libsolv_MANPAGES3}
+ DESTINATION "${MAN_INSTALL_DIR}/man3")
+
+INSTALL(FILES
+ ${libsolv_MANPAGES1}
+ DESTINATION "${MAN_INSTALL_DIR}/man1")
--- /dev/null
+
+man: man3 man1
+
+man3: libsolv.3 libsolv-bindings.3 libsolv-constantids.3 libsolv-history.3 libsolv-pool.3
+
+man1: mergesolv.1 dumpsolv.1 installcheck.1 testsolv.1 rpmdb2solv.1 rpms2solv.1 \
+ rpmmd2solv.1 repomdxml2solv.1 updateinfoxml2solv.1 deltainfoxml2solv.1 \
+ helix2solv.1 susetags2solv.1 comps2solv.1 deb2solv.1 mdk2solv.1 \
+ archpkgs2solv.1 archrepo2solv.1 appdata2solv.1
+
+html: libsolv.html libsolv-bindings.html libsolv-constantids.html libsolv-history.html libsolv-pool.html
+
+.SUFFIXES: .html .3 .1 .txt
+
+.txt.1:
+ a2x -f manpage $<
+
+.txt.3:
+ a2x -f manpage $<
+
+.txt.html:
+ a2x -f xhtml $<
--- /dev/null
+'\" t
+.\" Title: appdata2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "APPDATA2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+appdata2solv \- convert application meta data into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBappdata2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The appdata format contains metadata about application\&. It can be available both in repositories (for available applications) and in the installed system (for installed applications)\&. The appdata2solv tool reads the metadata from stdin and writes the parsed data as solv file to standard output\&. The parser will create \fBapplication:\fR pseudo packages for each entry\&.
+.PP
+\fB\-d\fR \fIAPPDATADIR\fR
+.RS 4
+Do not read from standard input, instead scan the specified directory for appdata entries\&.
+\fIAPPDATADIR\fR
+is normally set to
+\fB/usr/share/appdata\fR\&.
+.RE
+.PP
+\fB\-r\fR \fIROOTDIR\fR
+.RS 4
+Use
+\fIROOTDIR\fR
+as root directory\&.
+.RE
+.SH "SEE ALSO"
+.sp
+mergesolv(1)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+appdata2solv(1)
+===============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+appdata2solv - convert application meta data into a solv file
+
+Synopsis
+--------
+*appdata2solv* ['OPTIONS']
+
+Description
+-----------
+The appdata format contains metadata about application. It can
+be available both in repositories (for available applications)
+and in the installed system (for installed applications).
+The appdata2solv tool reads the metadata from stdin and
+writes the parsed data as solv file to standard output. The
+parser will create *application:* pseudo packages for each entry.
+
+*-d* 'APPDATADIR'::
+Do not read from standard input, instead scan the specified
+directory for appdata entries. 'APPDATADIR' is normally
+set to */usr/share/appdata*.
+
+*-r* 'ROOTDIR'::
+Use 'ROOTDIR' as root directory.
+
+
+See Also
+--------
+mergesolv(1)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: archpkgs2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "ARCHPKGS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+archpkgs2solv \- convert one or more Arch package files into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBarchpkgs2solv\fR [\fIOPTIONS\fR] \fIPKG1\&.pkg\&.xz\fR \&...
+.SH "DESCRIPTION"
+.sp
+The archpkgs2solv tool converts the meta data from one or more Arch Linux packages into the solv file written to standard output\&.
+.PP
+\fB\-m\fR \fIMANIFESTFILE\fR
+.RS 4
+Read the rpm file names from the specified
+\fIMANIFESTFILE\fR\&. You can use
+\fB\-\fR
+to read the manifest from standard input\&.
+.RE
+.PP
+\fB\-0\fR
+.RS 4
+Use a null byte as line terminator for manifest files instead of a newline\&. This is useful if the file names can contain newlines\&. See also the
+\fB\-print0\fR
+option in
+\fBfind\fR\&.
+.RE
+.SH "SEE ALSO"
+.sp
+pacman(8)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+archpkgs2solv(1)
+================
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+archpkgs2solv - convert one or more Arch package files into a solv file
+
+Synopsis
+--------
+*archpkgs2solv* ['OPTIONS'] 'PKG1.pkg.xz' ...
+
+Description
+-----------
+The archpkgs2solv tool converts the meta data from one or more
+Arch Linux packages into the solv file written to standard output.
+
+*-m* 'MANIFESTFILE'::
+Read the rpm file names from the specified 'MANIFESTFILE'. You can
+use *-* to read the manifest from standard input.
+
+*-0*::
+Use a null byte as line terminator for manifest files instead of
+a newline. This is useful if the file names can contain newlines.
+See also the *-print0* option in *find*.
+
+See Also
+--------
+pacman(8)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: archrepo2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "ARCHREPO2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+archrepo2solv \- convert files in Arch repository format into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBarchrepo2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The archrepo2solv tool reads Arch Linux repository data (\fBcore\&.db\fR) from stdin, and writes it as solv file to standard output\&.
+.PP
+\fB\-l\fR \fIDATABASEDIR\fR
+.RS 4
+Instead of reading from standard input, scan the specified directory for package meta files\&. Set
+\fIDATABASEDIR\fR
+to
+\fB/var/lib/pacman/local\fR
+to scan the installed packages\&.
+.RE
+.SH "SEE ALSO"
+.sp
+pacman(8)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+archrepo2solv(1)
+================
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+archrepo2solv - convert files in Arch repository format into a solv file
+
+Synopsis
+--------
+*archrepo2solv* ['OPTIONS']
+
+Description
+-----------
+The archrepo2solv tool reads Arch Linux repository data (*core.db*) from stdin,
+and writes it as solv file to standard output.
+
+*-l* 'DATABASEDIR'::
+Instead of reading from standard input, scan the specified directory for
+package meta files. Set 'DATABASEDIR' to */var/lib/pacman/local* to
+scan the installed packages.
+
+See Also
+--------
+pacman(8)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: comps2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "COMPS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+comps2solv \- convert rpm\-md comps\&.xml file into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBcomps2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The comps\&.xml file is Fedora\(cqs way to implement package groups\&. The comps2solv tool reads the comps xml file from stdin and writes the parsed data as solv file to standard output\&. The parser will create \fBgroup:\fR and \fBcategory:\fR pseudo packages for each comps entry\&.
+.SH "SEE ALSO"
+.sp
+mergesolv(1), createrepo(8)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+comps2solv(1)
+=============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+comps2solv - convert rpm-md comps.xml file into a solv file
+
+Synopsis
+--------
+*comps2solv* ['OPTIONS']
+
+Description
+-----------
+The comps.xml file is Fedora's way to implement package groups.
+The comps2solv tool reads the comps xml file from stdin and
+writes the parsed data as solv file to standard output. The
+parser will create *group:* and *category:* pseudo packages
+for each comps entry.
+
+See Also
+--------
+mergesolv(1), createrepo(8)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: deb2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "DEB2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+deb2solv \- convert one or more Debian package files into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBdeb2solv\fR [\fIOPTIONS\fR] \fIPKG1\&.deb\fR \&...
+.SH "DESCRIPTION"
+.sp
+The deb2solv tool converts the meta data from one or more Debian packages into the solv file written to standard output\&.
+.PP
+\fB\-m\fR \fIMANIFESTFILE\fR
+.RS 4
+Read the rpm file names from the specified
+\fIMANIFESTFILE\fR\&. You can use
+\fB\-\fR
+to read the manifest from standard input\&.
+.RE
+.PP
+\fB\-0\fR
+.RS 4
+Use a null byte as line terminator for manifest files instead of a newline\&. This is useful if the file names can contain newlines\&. See also the
+\fB\-print0\fR
+option in
+\fBfind\fR\&.
+.RE
+.SH "SEE ALSO"
+.sp
+deb(5), dpkg\-deb(1)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+deb2solv(1)
+============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+deb2solv - convert one or more Debian package files into a solv file
+
+Synopsis
+--------
+*deb2solv* ['OPTIONS'] 'PKG1.deb' ...
+
+Description
+-----------
+The deb2solv tool converts the meta data from one or more
+Debian packages into the solv file written to standard output.
+
+*-m* 'MANIFESTFILE'::
+Read the rpm file names from the specified 'MANIFESTFILE'. You can
+use *-* to read the manifest from standard input.
+
+*-0*::
+Use a null byte as line terminator for manifest files instead of
+a newline. This is useful if the file names can contain newlines.
+See also the *-print0* option in *find*.
+
+See Also
+--------
+deb(5), dpkg-deb(1)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: deltainfoxml2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "DELTAINFOXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+deltainfoxml2solv \- convert rpm\-md\*(Aqs deltainfo format into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBdeltainfoxml2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The deltainfoxml2solv tool reads rpm\-md\(cqs \fBdeltainfo\&.xml\fR data from stdin, and writes it as solv file to standard output\&. Some distributions name the input \fBprestodelta\&.xml\fR instead\&. Each delta rpm element is converted and added as \fBrepository:deltainfo\fR element to the meta section of the solv file\&.
+.SH "SEE ALSO"
+.sp
+mergesolv(1), createrepo(8)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+deltainfoxml2solv(1)
+====================
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+deltainfoxml2solv - convert rpm-md's deltainfo format into a solv file
+
+Synopsis
+--------
+*deltainfoxml2solv* ['OPTIONS']
+
+Description
+-----------
+The deltainfoxml2solv tool reads rpm-md's *deltainfo.xml* data from stdin,
+and writes it as solv file to standard output. Some distributions name
+the input *prestodelta.xml* instead. Each delta rpm element is converted
+and added as *repository:deltainfo* element to the meta section of the
+solv file.
+
+See Also
+--------
+mergesolv(1), createrepo(8)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: dumpsolv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "DUMPSOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+dumpsolv \- print a solv file into a human readable format
+.SH "SYNOPSIS"
+.sp
+\fBdumpsolv\fR [\fIOPTIONS\fR] [\fIFILE\&.solv\fR]
+.SH "DESCRIPTION"
+.sp
+The dumpsolv tool reads a solv files and writes its contents to standard output\&. If no input file is given, it reads the solv file from standard input\&.
+.PP
+\fB\-j\fR
+.RS 4
+Write the contents in JSON format\&.
+.RE
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+dumpsolv(1)
+===========
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+dumpsolv - print a solv file into a human readable format
+
+Synopsis
+--------
+*dumpsolv* ['OPTIONS'] ['FILE.solv']
+
+Description
+-----------
+The dumpsolv tool reads a solv files and writes its contents
+to standard output. If no input file is given, it reads the
+solv file from standard input.
+
+*-j*::
+Write the contents in JSON format.
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+[blockdef-listing]
+xcode-style=template="verseblock",presubs=(),postsubs=("callouts",),filter="filters/xcode.pl {basebackend}"
+
+[paradef-xcode]
+delimiter=(?s)^(?P<text>\s+.*)
+template=verseblock
+subs=verbatim
+filter=filters/xcode.pl {basebackend}
+
+[paradef-literal]
+delimiter=(?s)^(?P<text>\s{1,7}\S.*)
+
--- /dev/null
+#!/usr/bin/perl
+
+die("I only understand docbook\n") unless @ARGV && $ARGV[0] eq 'docbook';
+
+#my $ii = '//';
+#my $io = '//';
+
+#my $si = '**';
+#my $so = '**';
+
+my $ii = '<emphasis>';
+my $io = '</emphasis>';
+
+my $si = '<emphasis role="strong">';
+my $so = '</emphasis>';
+
+while(<STDIN>) {
+ chomp;
+ my $in = '';
+ my $out = '';
+ s/^\s+//;
+ s/\s+$//;
+ if (/^(.*)(\s*\/\*.*?\*\/\s*?)$/) {
+ $out = $2;
+ $_ = $1;
+ }
+ if (/^(my\s+)(.*?)$/) {
+ $in = $1;
+ $_ = $2;
+ }
+ if (/(?<!\>);$/) {
+ $out = ";$out";
+ chop $_;
+ }
+ if (!/^[a-zA-Z0-9_]+$/) {
+ $_ = " $_";
+ $_ = "$_ ";
+ if (s/^ TCL +/ /) {
+ s/(\$[a-zA-Z_][a-zA-Z0-9_:]*)/<-S><I>$1<-I><S>/g;
+ } else {
+ s/(?<=[^a-zA-Z_\&:\.\'\";])(?!solv\W|Solv\W|Pool\W)([\$\@a-zA-Z_][a-zA-Z0-9_]*)(?=[^a-zA-Z0-9_\(;\[])(?!::)(?! [^=])/<-S><I>$1<-I><S>/g;
+ }
+ # fixup for perl bare words
+ s/{<-S><I>([a-zA-Z_][a-zA-Z0-9]*)<-I><S>}/{$1}/g;
+ # fixup for callbackfunctions
+ s/\\(&[a-zA-Z_]+)/\\<-S><I>$1<-I><S>/;
+ # fixup for stringification
+ s/\$<-S><I>/<-S><I>\$/g;
+ # fixup for %d
+ s/%<-S><I>d<-I><S>\"/%d\"/;
+ s/%<-S><I>d<-I><S>\\<-S><I>n<-I><S>/%d\\n/;
+ # iterators
+ s/^ //;
+ s/ $//;
+ s/^(for (?:my )?)(\S+) /$1<-S><I>$2<-I><S> /;
+ }
+ $_ = "<S>$_<-S>";
+ s/<S>(\s*)<-S>/$1/g;
+ s/<-S>(\s*)<S>/$1/g;
+ s/<I>(\s*)<-I>/$1/g;
+ s/<-I>(\s*)<I>/$1/g;
+ s/<S>(\s+)/$1<S>/g;
+ s/(\s+)<-S>/<-S>$1/g;
+ s/<I>(\s+)/$1<I>/g;
+ s/(\s+)<-I>/<-I>$1/g;
+ s/<S>/$si/g;
+ s/<-S>/$so/g;
+ s/<I>/$ii/g;
+ s/<-I>/$io/g;
+ print "$in$_$out\n";
+}
--- /dev/null
+'\" t
+.\" Title: helix2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 12/14/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "HELIX2SOLV" "1" "12/14/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+helix2solv \- convert legacy helixcode format into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBhelix2solv\fR
+.SH "DESCRIPTION"
+.sp
+The helix format was a metadata format used in the RedCarpet package manager\&. It\(cqs still used in libzypp testcases\&. The helix2solv tool reads data in helix format from standard input and writes it in solv file format to standard output\&.
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+helix2solv(1)
+=============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+helix2solv - convert legacy helixcode format into a solv file
+
+Synopsis
+--------
+*helix2solv*
+
+Description
+-----------
+The helix format was a metadata format used in the RedCarpet
+package manager. It's still used in libzypp testcases.
+The helix2solv tool reads data in helix format from standard
+input and writes it in solv file format to standard output.
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: installcheck
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "INSTALLCHECK" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+installcheck \- find out which packages cannot be installed
+.SH "SYNOPSIS"
+.sp
+\fBinstallcheck\fR \fIARCH\fR \fIREPO1\fR \fIREPO2\fR\&... \fB\-\-nocheck\fR \fINREPO1\fR \fINREPO2\fR\&...
+.SH "DESCRIPTION"
+.sp
+The installcheck tool checks if all packages in \fIREPO1\fR\&...\fIREPON\fR are installable\&. A package is installable if there is a set of packages from the repositories that satisfies its dependencies\&. The repositories after the \fB\-\-nocheck\fR option are only used for dependency resolving, but the tool does not check if the packages in them are installable\&.
+.sp
+A Repository can be a solv file, a rpmmd \fBprimary\&.xml\&.gz\fR file, a SUSE \fBpackages\fR or \fBpackages\&.gz\fR file, or a Debian \fBPackages\fR or \fBPackages\&.gz\fR file\&.
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+installcheck(1)
+===============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+installcheck - find out which packages cannot be installed
+
+Synopsis
+--------
+*installcheck* 'ARCH' 'REPO1' 'REPO2'... *--nocheck* 'NREPO1' 'NREPO2'...
+
+Description
+-----------
+The installcheck tool checks if all packages in 'REPO1'...'REPON' are
+installable. A package is installable if there is a set of packages
+from the repositories that satisfies its dependencies. The repositories
+after the *--nocheck* option are only used for dependency resolving,
+but the tool does not check if the packages in them are installable.
+
+A Repository can be a solv file, a rpmmd *primary.xml.gz* file, a SUSE
+*packages* or *packages.gz* file, or a Debian *Packages* or *Packages.gz*
+file.
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: Libsolv-Bindings
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 12/14/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "LIBSOLV\-BINDINGS" "3" "12/14/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+libsolv-bindings \- access libsolv from perl/python/ruby
+.SH "DESCRIPTION"
+.sp
+Libsolv\(cqs language bindings offer an abstract, object orientated interface to the library\&. The supported languages are currently perl, python, and ruby\&. All example code (except in the specifics sections, of course) lists first the \(lqC\-ish\(rq interface, then the syntax for perl, python, and ruby (in that order)\&.
+.SH "PERL SPECIFICS"
+.sp
+Libsolv\(cqs perl bindings can be loaded with the following statement:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBuse solv\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Objects are either created by calling the new() method on a class or they are returned by calling methods on other objects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+my \fI$pool\fR \fB= solv::Pool\->new()\fR;
+my \fI$repo\fR \fB=\fR \fI$pool\fR\fB\->add_repo("my_first_repo")\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Swig encapsulates all objects as tied hashes, thus the attributes can be accessed by treating the object as standard hash reference:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fI$pool\fR\fB\->{appdata} = 42\fR;
+\fBprintf "appdata is %d\en",\fR \fI$pool\fR\fB\->{appdata}\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+A special exception to this are iterator objects, they are encapsulated as tied arrays so that it is possible to iterate with a for() statement:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+my \fI$iter\fR \fB=\fR \fI$pool\fR\fB\->solvables_iter()\fR;
+\fBfor my\fR \fI$solvable\fR \fB(\fR\fI@$iter\fR\fB) { \&.\&.\&. }\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+As a downside of this approach, iterator objects cannot have attributes\&.
+.sp
+If an array needs to be passed to a method it is usually done by reference, if a method returns an array it returns it on the stack:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+my \fI@problems\fR \fB=\fR \fI$solver\fR\fB\->solve(\e\fR\fI@jobs\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Due to a bug in swig, stringification does not work for libsolv\(cqs objects\&. Instead, you have to call the object\(cqs str() method\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBprint\fR \fI$dep\fR\fB\->str() \&. "\e\fR\fIn\fR\fB"\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Swig implements all constants as numeric variables (instead of the more natural constant subs), so don\(cqt forget the leading \(lq$\(rq when accessing a constant\&. Also do not forget to prepend the namespace of the constant:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fI$pool\fR\fB\->set_flag($solv::Pool::POOL_FLAG_OBSOLETEUSESCOLORS, 1)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.SH "PYTHON SPECIFICS"
+.sp
+The python bindings can be loaded with:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBimport solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Objects are either created by calling the constructor method for a class or they are returned by calling methods on other objects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fIpool\fR \fB= solv\&.Pool()\fR
+\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.add_repo("my_first_repo")\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Attributes can be accessed as usual:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fIpool\fR\fB\&.appdata = 42\fR
+\fBprint "appdata is %d" % (\fR\fIpool\fR\fB\&.appdata)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterators also work as expected:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBfor\fR \fIsolvable\fR \fBin\fR \fIpool\fR\fB\&.solvables_iter():\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Arrays are passed and returned as list objects:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fIjobs\fR \fB= []\fR
+\fIproblems\fR \fB=\fR \fIsolver\fR\fB\&.solve(\fR\fIjobs\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The bindings define stringification for many classes, some also have a \fIrepr\fR method to ease debugging\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBprint\fR \fIdep\fR
+\fBprint repr(\fR\fIrepo\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Constants are attributes of the classes:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fIpool\fR\fB\&.set_flag(solv\&.Pool\&.POOL_FLAG_OBSOLETEUSESCOLORS, 1)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.SH "RUBY SPECIFICS"
+.sp
+The ruby bindings can be loaded with:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBrequire \*(Aqsolv\*(Aq\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Objects are either created by calling the new method on a class or they are returned by calling methods on other objects\&. Note that all classes start with an uppercase letter in ruby, so the class is called \(lqSolv\(rq\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fIpool\fR \fB= Solv::Pool\&.new\fR
+\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.add_repo("my_first_repo")\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Attributes can be accessed as usual:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fIpool\fR\fB\&.appdata = 42\fR
+\fBputs "appdata is #{\fR\fIpool\fR\fB\&.appdata}"\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterators also work as expected:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBfor\fR \fIsolvable\fR \fBin\fR \fIpool\fR\fB\&.solvables_iter() do \&.\&.\&.\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Arrays are passed and returned as array objects:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fIjobs\fR \fB= []\fR
+\fIproblems\fR \fB=\fR \fIsolver\fR\fB\&.solve(\fR\fIjobs\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Most classes define a to_s method, so objects can be easily stringified\&. Many also define an inspect() method\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBputs\fR \fIdep\fR
+\fBputs\fR \fIrepo\fR\fB\&.inspect\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Constants live in the namespace of the class they belong to:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fIpool\fR\fB\&.set_flag(Solv::Pool::POOL_FLAG_OBSOLETEUSESCOLORS, 1)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Note that boolean methods have an added trailing \(lq?\(rq, to be consistent with other ruby modules:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBputs "empty" if\fR \fIrepo\fR\fB\&.isempty?\fR
+.fi
+.if n \{\
+.RE
+.\}
+.SH "TCL SPECIFICS"
+.sp
+Libsolv\(cqs tcl bindings can be loaded with the following statement:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBpackage require solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Objects are either created by calling class name prefixed with \(lqnew_\(rq, or they are returned by calling methods on other objects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBset pool [solv::new_Pool]\fR
+\fBset repo [\fR\fI$pool\fR \fBadd_repo "my_first_repo"]\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Swig provides a \(lqcget\(rq method to read object attributes, and a \(lqconfigure\(rq method to write them:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fI$pool\fR \fBconfigure \-appdata 42\fR
+\fBputs "appdata is [\fR\fI$pool\fR \fBcget \-appdata]"\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The tcl bindings provide a little helper to work with iterators in a foreach style:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBset iter [\fR\fI$pool\fR \fBsolvables_iter]\fR
+\fBsolv::iter s\fR \fI$iter\fR \fB{ \&.\&.\&. }\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+libsolv\(cqs arrays are mapped to tcl\(cqs lists:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBset jobs [list\fR \fI$job1 $job2\fR\fB]\fR
+\fBset problems [\fR\fI$solver\fR \fBsolve\fR \fI$jobs\fR\fB]\fR
+\fBputs "We have [llength\fR \fI$problems\fR\fB] problems\&.\&.\&."\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Stringification is done by calling the object\(cqs \(lqstr\(rq method\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBputs [\fR\fI$dep\fR \fBstr]\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+There is one exception: you have to use \(lqstringify\(rq for Datamatch objects, as swig reports a clash with the \(lqstr\(rq attribute\&. Some objects also support a \(lq==\(rq method for equality tests, and a \(lq!=\(rq method\&.
+.sp
+Swig implements all constants as numeric variables, constants belonging to a libsolv class are prefixed with the class name:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fI$pool\fR \fBset_flag\fR \fI$solv::Pool_POOL_FLAG_OBSOLETEUSESCOLORS\fR \fB1\fR
+\fBputs [\fR\fI$solvable\fR \fBlookup_str\fR \fI$solv::SOLVABLE_SUMMARY\fR\fB]\fR
+.fi
+.if n \{\
+.RE
+.\}
+.SH "THE SOLV CLASS"
+.sp
+This is the main namespace of the library, you cannot create objects of this type but it contains some useful constants\&.
+.SS "CONSTANTS"
+.sp
+Relational flag constants, the first three can be or\-ed together
+.PP
+\fBREL_LT\fR
+.RS 4
+the \(lqless than\(rq bit
+.RE
+.PP
+\fBREL_EQ\fR
+.RS 4
+the \(lqequals to\(rq bit
+.RE
+.PP
+\fBREL_GT\fR
+.RS 4
+the \(lqgreater than\(rq bit
+.RE
+.PP
+\fBREL_ARCH\fR
+.RS 4
+used for relations that describe an extra architecture filter, the version part of the relation is interpreted as architecture\&.
+.RE
+.sp
+Special Solvable Ids
+.PP
+\fBSOLVID_META\fR
+.RS 4
+Access the meta section of a repository or repodata area\&. This is like an extra Solvable that has the Id SOLVID_META\&.
+.RE
+.PP
+\fBSOLVID_POS\fR
+.RS 4
+Use the data position stored inside of the pool instead of accessing some solvable by Id\&. The bindings have the Datapos objects as an abstraction mechanism, so you do not need this constant\&.
+.RE
+.sp
+Constant string Ids
+.PP
+\fBID_NULL\fR
+.RS 4
+Always zero
+.RE
+.PP
+\fBID_EMPTY\fR
+.RS 4
+Always one, describes the empty string
+.RE
+.PP
+\fBSOLVABLE_NAME\fR
+.RS 4
+The keyname Id of the name of the solvable\&.
+.RE
+.PP
+\fB\&...\fR
+.RS 4
+see the libsolv\-constantids manpage for a list of fixed Ids\&.
+.RE
+.SH "THE POOL CLASS"
+.sp
+The pool is libsolv\(cqs central resource manager\&. A pool consists of Solvables, Repositories, Dependencies, each indexed by Ids\&.
+.SS "CLASS METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *Pool()\fR
+my \fI$pool\fR \fB= solv::Pool\->new()\fR;
+\fIpool\fR \fB= solv\&.Pool()\fR
+\fIpool\fR \fB= Solv::Pool\&.new()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a new pool instance\&. In most cases you just need one pool\&. Note that the returned object "owns" the pool, i\&.e\&. if the object is freed, the pool is also freed\&. You can use the disown method to break this ownership relation\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid *appdata;\fR /* read/write */
+\fI$pool\fR\fB\->{appdata}\fR
+\fIpool\fR\fB\&.appdata\fR
+\fIpool\fR\fB\&.appdata\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Application specific data that may be used in any way by the code using the pool\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable solvables[];\fR /* read only */
+my \fI$solvable\fR \fB=\fR \fI$pool\fR\fB\->{solvables}\->[\fR\fI$solvid\fR\fB]\fR;
+\fIsolvable\fR \fB=\fR \fIpool\fR\fB\&.solvables[\fR\fIsolvid\fR\fB]\fR
+\fIsolvable\fR \fB=\fR \fIpool\fR\fB\&.solvables[\fR\fIsolvid\fR\fB]\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Look up a Solvable by its id\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepo repos[];\fR /* read only */
+my \fI$repo\fR \fB=\fR \fI$pool\fR\fB\->{repos}\->[\fR\fI$repoid\fR\fB]\fR;
+\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.repos[\fR\fIrepoid\fR\fB]\fR
+\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.repos[\fR\fIrepoid\fR\fB]\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Look up a Repository by its id\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepo *installed;\fR /* read/write */
+\fI$pool\fR\fB\->{installed} =\fR \fI$repo\fR;
+\fIpool\fR\fB\&.installed =\fR \fIrepo\fR
+\fIpool\fR\fB\&.installed =\fR \fIrepo\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Define which repository contains all the installed packages\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *errstr;\fR /* read only */
+my \fI$err\fR \fB=\fR \fI$pool\fR\fB\->{errstr}\fR;
+\fIerr\fR \fB=\fR \fIpool\fR\fB\&.errstr\fR
+\fIerr\fR \fB=\fR \fIpool\fR\fB\&.errstr\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the last error string that was stored in the pool\&.
+.SS "CONSTANTS"
+.PP
+\fBPOOL_FLAG_PROMOTEEPOCH\fR
+.RS 4
+Promote the epoch of the providing dependency to the requesting dependency if it does not contain an epoch\&. Used at some time in old rpm versions, modern systems should never need this\&.
+.RE
+.PP
+\fBPOOL_FLAG_FORBIDSELFCONFLICTS\fR
+.RS 4
+Disallow the installation of packages that conflict with themselves\&. Debian always allows self\-conflicting packages, rpm used to forbid them but switched to also allowing them recently\&.
+.RE
+.PP
+\fBPOOL_FLAG_OBSOLETEUSESPROVIDES\fR
+.RS 4
+Make obsolete type dependency match against provides instead of just the name and version of packages\&. Very old versions of rpm used the name/version, then it got switched to provides and later switched back again to just name/version\&.
+.RE
+.PP
+\fBPOOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES\fR
+.RS 4
+An implicit obsoletes is the internal mechanism to remove the old package on an update\&. The default is to remove all packages with the same name, rpm\-5 switched to also removing packages providing the same name\&.
+.RE
+.PP
+\fBPOOL_FLAG_OBSOLETEUSESCOLORS\fR
+.RS 4
+Rpm\(cqs multilib implementation (used in RedHat and Fedora) distinguishes between 32bit and 64bit packages (the terminology is that they have a different color)\&. If obsoleteusescolors is set, packages with different colors will not obsolete each other\&.
+.RE
+.PP
+\fBPOOL_FLAG_IMPLICITOBSOLETEUSESCOLORS\fR
+.RS 4
+Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if packages of the same name can be installed in parallel\&. For current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true (this is the default if FEDORA is defined when libsolv is compiled)\&.
+.RE
+.PP
+\fBPOOL_FLAG_NOINSTALLEDOBSOLETES\fR
+.RS 4
+New versions of rpm consider the obsoletes of installed packages when checking for dependency, thus you may not install a package that is obsoleted by some other installed package, unless you also erase the other package\&.
+.RE
+.PP
+\fBPOOL_FLAG_HAVEDISTEPOCH\fR
+.RS 4
+Mandriva added a new field called distepoch that gets checked in version comparison if the epoch/version/release of two packages are the same\&.
+.RE
+.PP
+\fBPOOL_FLAG_NOOBSOLETESMULTIVERSION\fR
+.RS 4
+If a package is installed in multiversionmode, rpm used to ignore both the implicit obsoletes and the obsolete dependency of a package\&. This was changed to ignoring just the implicit obsoletes, thus you may install multiple versions of the same name, but obsoleted packages still get removed\&.
+.RE
+.PP
+\fBPOOL_FLAG_ADDFILEPROVIDESFILTERED\fR
+.RS 4
+Make the addfileprovides method only add files from the standard locations (i\&.e\&. the \(lqbin\(rq and \(lqetc\(rq directories)\&. This is useful if you have only few packages that use non\-standard file dependencies, but you still want the fast speed that addfileprovides() generates\&.
+.RE
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid free()\fR
+\fI$pool\fR\fB\->free()\fR;
+\fIpool\fR\fB\&.free()\fR
+\fIpool\fR\fB\&.free()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Force a free of the pool\&. After this call, you must not access any object that still references the pool\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid disown()\fR
+\fI$pool\fR\fB\->disown()\fR;
+\fIpool\fR\fB\&.disown()\fR
+\fIpool\fR\fB\&.disown()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Break the ownership relation between the binding object and the pool\&. After this call, the pool will not get freed even if the object goes out of scope\&. This also means that you must manually call the free method to free the pool data\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid setdebuglevel(int\fR \fIlevel\fR\fB)\fR
+\fI$pool\fR\fB\->setdebuglevel(\fR\fI$level\fR\fB)\fR;
+\fIpool\fR\fB\&.setdebuglevel(\fR\fIlevel\fR\fB)\fR
+\fIpool\fR\fB\&.setdebuglevel(\fR\fIlevel\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the debug level\&. A value of zero means no debug output, the higher the value, the more output is generated\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint set_flag(int\fR \fIflag\fR\fB, int\fR \fIvalue\fR\fB)\fR
+my \fI$oldvalue\fR \fB=\fR \fI$pool\fR\fB\->set_flag(\fR\fI$flag\fR\fB,\fR \fI$value\fR\fB)\fR;
+\fIoldvalue\fR \fB=\fR \fIpool\fR\fB\&.set_flag(\fR\fIflag\fR\fB,\fR \fIvalue\fR\fB)\fR
+\fIoldvalue\fR \fB=\fR \fIpool\fR\fB\&.set_flag(\fR\fIflag\fR\fB,\fR \fIvalue\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint get_flag(int\fR \fIflag\fR\fB)\fR
+my \fI$value\fR \fB=\fR \fI$pool\fR\fB\->get_flag(\fR\fI$flag\fR\fB)\fR;
+\fIvalue\fR \fB=\fR \fIpool\fR\fB\&.get_flag(\fR\fIflag\fR\fB)\fR
+\fIvalue\fR \fB=\fR \fIpool\fR\fB\&.get_flag(\fR\fIflag\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set/get a pool specific flag\&. The flags define how the system works, e\&.g\&. how the package manager treats obsoletes\&. The default flags should be sane for most applications, but in some cases you may want to tweak a flag, for example if you want to solv package dependencies for some other system than yours\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid set_rootdir(const char *\fR\fIrootdir\fR\fB)\fR
+\fI$pool\fR\fB\->set_rootdir(\fR\fIrootdir\fR\fB)\fR;
+\fIpool\fR\fB\&.set_rootdir(\fR\fIrootdir\fR\fB)\fR
+\fIpool\fR\fB\&.set_rootdir(\fR\fIrootdir\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *get_rootdir()\fR
+my \fI$rootdir\fR \fB=\fR \fI$pool\fR\fB\->get_rootdir()\fR;
+\fIrootdir\fR \fB=\fR \fIpool\fR\fB\&.get_rootdir()\fR
+\fIrootdir\fR \fB=\fR \fIpool\fR\fB\&.get_rootdir()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set/get the rootdir to use\&. This is useful if you want package management to work only in some directory, for example if you want to setup a chroot jail\&. Note that the rootdir will only be prepended to file paths if the \fBREPO_USE_ROOTDIR\fR flag is used\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid setarch(const char *\fR\fIarch\fR \fB= 0)\fR
+\fI$pool\fR\fB\->setarch()\fR;
+\fIpool\fR\fB\&.setarch()\fR
+\fIpool\fR\fB\&.setarch()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the architecture for your system\&. The architecture is used to determine which packages are installable\&. It defaults to the result of \(lquname \-m\(rq\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepo add_repo(const char *\fR\fIname\fR\fB)\fR
+\fI$repo\fR \fB=\fR \fI$pool\fR\fB\->add_repo(\fR\fI$name\fR\fB)\fR;
+\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.add_repo(\fR\fIname\fR\fB)\fR
+\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.add_repo(\fR\fIname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a Repository with the specified name to the pool\&. The repository is empty on creation, use the repository methods to populate it with packages\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepoiterator repos_iter()\fR
+\fBfor my\fR \fI$repo\fR \fB(\fR\fI@\fR\fB{\fR\fI$pool\fR\fB\->repos_iter()})\fR
+\fBfor\fR \fIrepo\fR \fBin\fR \fIpool\fR\fB\&.repos_iter():\fR
+\fBfor\fR \fIrepo\fR \fBin\fR \fIpool\fR\fB\&.repos_iter()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterate over the existing repositories\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvableiterator solvables_iter()\fR
+\fBfor my\fR \fI$solvable\fR \fB(\fR\fI@\fR\fB{\fR\fI$pool\fR\fB\->solvables_iter()})\fR
+\fBfor\fR \fIsolvable\fR \fBin\fR \fIpool\fR\fB\&.solvables_iter():\fR
+\fBfor\fR \fIsolvable\fR \fBin\fR \fIpool\fR\fB\&.solvables_iter()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterate over the existing solvables\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep Dep(const char *\fR\fIstr\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
+my \fI$dep\fR \fB=\fR \fI$pool\fR\fB\->Dep(\fR\fI$string\fR\fB)\fR;
+\fIdep\fR \fB=\fR \fIpool\fR\fB\&.Dep(\fR\fIstring\fR\fB)\fR
+\fIdep\fR \fB=\fR \fIpool\fR\fB\&.Dep(\fR\fIstring\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create an object describing a string or dependency\&. If the string is currently not in the pool and \fIcreate\fR is false, \fBundef\fR/\fBNone\fR/\fBnil\fR is returned\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid addfileprovides()\fR
+\fI$pool\fR\fB\->addfileprovides()\fR;
+\fIpool\fR\fB\&.addfileprovides()\fR
+\fIpool\fR\fB\&.addfileprovides()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId *addfileprovides_queue()\fR
+my \fI@ids\fR \fB=\fR \fI$pool\fR\fB\->addfileprovides_queue()\fR;
+\fIids\fR \fB=\fR \fIpool\fR\fB\&.addfileprovides_queue()\fR
+\fIids\fR \fB=\fR \fIpool\fR\fB\&.addfileprovides_queue()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Some package managers like rpm allow dependencies on files contained in other packages\&. To allow libsolv to deal with those dependencies in an efficient way, you need to call the addfileprovides method after creating and reading all repositories\&. This method will scan all dependency for file names and then scan all packages for matching files\&. If a filename has been matched, it will be added to the provides list of the corresponding package\&. The addfileprovides_queue variant works the same way but returns an array containing all file dependencies\&. This information can be stored in the meta section of the repositories to speed up the next time the repository is loaded and addfileprovides is called\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid createwhatprovides()\fR
+\fI$pool\fR\fB\->createwhatprovides()\fR;
+\fIpool\fR\fB\&.createwhatprovides()\fR
+\fIpool\fR\fB\&.createwhatprovides()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create the internal \(lqwhatprovides\(rq hash over all of the provides of all packages\&. This method must be called before doing any lookups on provides\&. It\(cqs encouraged to do it right after all repos are set up, usually right after the call to addfileprovides()\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *whatprovides(DepId\fR \fIdep\fR\fB)\fR
+my \fI@solvables\fR \fB=\fR \fI$pool\fR\fB\->whatprovides(\fR\fI$dep\fR\fB)\fR;
+\fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatprovides(\fR\fIdep\fR\fB)\fR
+\fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatprovides(\fR\fIdep\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return all solvables that provide the specified dependency\&. You can use either a Dep object or a simple Id as argument\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId *matchprovidingids(const char *\fR\fImatch\fR\fB, int\fR \fIflags\fR\fB)\fR
+my \fI@ids\fR \fB=\fR \fI$pool\fR\fB\->matchprovidingids(\fR\fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIids\fR \fB=\fR \fIpool\fR\fB\&.matchprovidingids(\fR\fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIids\fR \fB=\fR \fIpool\fR\fB\&.matchprovidingids(\fR\fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Search the names of all provides and return the ones matching the specified string\&. See the Dataiterator class for the allowed flags\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId towhatprovides(Id *\fR\fIids\fR\fB)\fR
+my \fI$offset\fR \fB=\fR \fI$pool\fR\fB\->towhatprovides(\e\fR\fI@ids\fR\fB)\fR;
+\fIoffset\fR \fB=\fR \fIpool\fR\fB\&.towhatprovides(\fR\fIids\fR\fB)\fR
+\fIoffset\fR \fB=\fR \fIpool\fR\fB\&.towhatprovides(\fR\fIids\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+\(lqInternalize\(rq an array containing Ids\&. The returned value can be used to create solver jobs working on a specific set of packages\&. See the Solver class for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool isknownarch(DepId\fR \fIid\fR\fB)\fR
+my \fI$bool\fR \fB=\fR \fI$pool\fR\fB\->isknownarch(\fR\fI$id\fR\fB)\fR;
+\fIbool\fR \fB=\fR \fIpool\fR\fB\&.isknownarch(\fR\fIid\fR\fB)\fR
+\fIbool\fR \fB=\fR \fIpool\fR\fB\&.isknownarch?(\fR\fIid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return true if the specified Id describes a known architecture\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver Solver()\fR
+my \fI$solver\fR \fB=\fR \fI$pool\fR\fB\->Solver()\fR;
+\fIsolver\fR \fB=\fR \fIpool\fR\fB\&.Solver()\fR
+\fIsolver\fR \fB=\fR \fIpool\fR\fB\&.Solver()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a new solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBJob Job(int\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR
+my \fI$job\fR \fB=\fR \fI$pool\fR\fB\->Job(\fR\fI$how\fR\fB,\fR \fI$what\fR\fB)\fR;
+\fIjob\fR \fB=\fR \fIpool\fR\fB\&.Job(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
+\fIjob\fR \fB=\fR \fIpool\fR\fB\&.Job(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a new Job object\&. Kind of low level, in most cases you would use a Selection or Dep job constructor instead\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSelection Selection()\fR
+my \fI$sel\fR \fB=\fR \fI$pool\fR\fB\->Selection()\fR;
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.Selection()\fR
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.Selection()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create an empty selection\&. Useful as a starting point for merging other selections\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSelection Selection_all()\fR
+my \fI$sel\fR \fB=\fR \fI$pool\fR\fB\->Selection_all()\fR;
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.Selection_all()\fR
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.Selection_all()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a selection containing all packages\&. Useful as starting point for intersecting other selections or for update/distupgrade jobs\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSelection select(const char *\fR\fIname\fR\fB, int\fR \fIflags\fR\fB)\fR
+my \fI$sel\fR \fB=\fR \fI$pool\fR\fB\->select(\fR\fI$name\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.select(\fR\fIname\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.select(\fR\fIname\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a selection by matching packages against the specified string\&. See the Selection class for a list of flags and how to create solver jobs from a selection\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid setpooljobs(Jobs *\fR\fIjobs\fR\fB)\fR
+\fI$pool\fR\fB\->setpooljobs(\e\fR\fI@jobs\fR\fB)\fR;
+\fIpool\fR\fB\&.setpooljobs(\fR\fIjobs\fR\fB)\fR
+\fIpool\fR\fB\&.setpooljobs(\fR\fIjobs\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBJob *getpooljobs()\fR
+\fI@jobs\fR \fB=\fR \fI$pool\fR\fB\->getpooljobs()\fR;
+\fIjobs\fR \fB=\fR \fIpool\fR\fB\&.getpooljobs()\fR
+\fIjobs\fR \fB=\fR \fIpool\fR\fB\&.getpooljobs()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Get/Set fixed jobs stored in the pool\&. Those jobs are automatically appended to all solver jobs, they are meant for fixed configurations like which packages can be multiversion installed, which packages were userinstalled or must not be erased\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid set_loadcallback(Callable *\fR\fIcallback\fR\fB)\fR
+\fI$pool\fR\fB\->setloadcallback(\e\fR\fI&callbackfunction\fR\fB)\fR;
+\fIpool\fR\fB\&.setloadcallback(\fR\fIcallbackfunction\fR\fB)\fR
+\fIpool\fR\fB\&.setloadcallback { |\fR\fIrepodata\fR\fB| \&.\&.\&. }\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the callback function called when repository metadata needs to be loaded on demand\&. To make use of this feature, you need to create repodata stubs that tell the library which data is available but not loaded\&. If later on the data needs to be accessed, the callback function is called with a repodata argument\&. You can then load the data (maybe fetching it first from a remote server)\&. The callback should return true if the data has been made available\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+/* bindings only */
+\fI$pool\fR\fB\->appdata_disown()\fR
+\fIpool\fR\fB\&.appdata_disown()\fR
+\fIpool\fR\fB\&.appdata_disown()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Decrement the reference count of the appdata object\&. This can be used to break circular references (e\&.g\&. if the pool\(cqs appdata value points to some meta data structure that contains a pool handle)\&. If used incorrectly, this method can lead to application crashes, so beware\&. (This method is a no\-op for ruby and tcl\&.)
+.SS "DATA RETRIEVAL METHODS"
+.sp
+In the following functions, the \fIkeyname\fR argument describes what to retrieve\&. For the standard cases you can use the available Id constants\&. For example,
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB$solv::SOLVABLE_SUMMARY\fR
+\fBsolv\&.SOLVABLE_SUMMARY\fR
+\fBSolv::SOLVABLE_SUMMARY\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+selects the \(lqSummary\(rq entry of a solvable\&. The \fIsolvid\fR argument selects the desired solvable by Id\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *lookup_str(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI$string\fR \fB=\fR \fI$pool\fR\fB\->lookup_str(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIstring\fR \fB=\fR \fIpool\fR\fB\&.lookup_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIstring\fR \fB=\fR \fIpool\fR\fB\&.lookup_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId lookup_id(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI$id\fR \fB=\fR \fI$pool\fR\fB\->lookup_id(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIid\fR \fB=\fR \fIpool\fR\fB\&.lookup_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIid\fR \fB=\fR \fIpool\fR\fB\&.lookup_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBunsigned long long lookup_num(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInotfound\fR \fB= 0)\fR
+my \fI$num\fR \fB=\fR \fI$pool\fR\fB\->lookup_num(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fInum\fR \fB=\fR \fIpool\fR\fB\&.lookup_num(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fInum\fR \fB=\fR \fIpool\fR\fB\&.lookup_num(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool lookup_void(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI$bool\fR \fB=\fR \fI$pool\fR\fB\->lookup_void(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIbool\fR \fB=\fR \fIpool\fR\fB\&.lookup_void(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIbool\fR \fB=\fR \fIpool\fR\fB\&.lookup_void(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId *lookup_idarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI@ids\fR \fB=\fR \fI$pool\fR\fB\->lookup_idarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIids\fR \fB=\fR \fIpool\fR\fB\&.lookup_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIids\fR \fB=\fR \fIpool\fR\fB\&.lookup_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBChksum lookup_checksum(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI$chksum\fR \fB=\fR \fI$pool\fR\fB\->lookup_checksum(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIchksum\fR \fB=\fR \fIpool\fR\fB\&.lookup_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIchksum\fR \fB=\fR \fIpool\fR\fB\&.lookup_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Lookup functions\&. Return the data element stored in the specified solvable\&. You should probably use the methods of the Solvable class instead\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDataiterator Dataiterator(Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$di\fR \fB=\fR \fI$pool\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIdi\fR \fB=\fR \fIpool\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIdi\fR \fB=\fR \fIpool\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDataiterator Dataiterator_solvid(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$di\fR \fB=\fR \fI$pool\fR\fB\->Dataiterator(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIdi\fR \fB=\fR \fIpool\fR\fB\&.Dataiterator(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIdi\fR \fB=\fR \fIpool\fR\fB\&.Dataiterator(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterate over the matching data elements\&. See the Dataiterator class for more information\&. The Dataiterator method iterates over all solvables in the pool, whereas the Dataiterator_solvid only iterates over the specified solvable\&.
+.SS "ID METHODS"
+.sp
+The following methods deal with Ids, i\&.e\&. integers representing objects in the pool\&. They are considered \(lqlow level\(rq, in most cases you would not use them but instead the object orientated methods\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepo id2repo(Id\fR \fIid\fR\fB)\fR
+\fI$repo\fR \fB=\fR \fI$pool\fR\fB\->id2repo(\fR\fI$id\fR\fB)\fR;
+\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.id2repo(\fR\fIid\fR\fB)\fR
+\fIrepo\fR \fB=\fR \fIpool\fR\fB\&.id2repo(\fR\fIid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Lookup an existing Repository by id\&. You can also do this by using the \fBrepos\fR attribute\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable id2solvable(Id\fR \fIid\fR\fB)\fR
+\fI$solvable\fR \fB=\fR \fI$pool\fR\fB\->id2solvable(\fR\fI$id\fR\fB)\fR;
+\fIsolvable\fR \fB=\fR \fIpool\fR\fB\&.id2solvable(\fR\fIid\fR\fB)\fR
+\fIsolvable\fR \fB=\fR \fIpool\fR\fB\&.id2solvable(\fR\fIid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Lookup an existing Repository by id\&. You can also do this by using the \fBsolvables\fR attribute\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *solvid2str(Id\fR \fIid\fR\fB)\fR
+my \fI$str\fR \fB=\fR \fI$pool\fR\fB\->solvid2str(\fR\fI$id\fR\fB)\fR;
+\fIstr\fR \fB=\fR \fIpool\fR\fB\&.solvid2str(\fR\fIid\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIpool\fR\fB\&.solvid2str(\fR\fIid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the Solvable with the specified id\&. The string consists of the name, version, and architecture of the Solvable\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId str2id(const char *\fR\fIstr\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
+my \fI$id\fR \fB=\fR \fIpool\fR\fB\->str2id(\fR\fI$string\fR\fB)\fR;
+\fIid\fR \fB=\fR \fIpool\fR\fB\&.str2id(\fR\fIstring\fR\fB)\fR
+\fIid\fR \fB=\fR \fIpool\fR\fB\&.str2id(\fR\fIstring\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *id2str(Id\fR \fIid\fR\fB)\fR
+\fI$string\fR \fB=\fR \fIpool\fR\fB\->id2str(\fR\fI$id\fR\fB)\fR;
+\fIstring\fR \fB=\fR \fIpool\fR\fB\&.id2str(\fR\fIid\fR\fB)\fR
+\fIstring\fR \fB=\fR \fIpool\fR\fB\&.id2str(\fR\fIid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert a string into an Id and back\&. If the string is currently not in the pool and \fIcreate\fR is false, zero is returned\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId rel2id(Id\fR \fIname\fR\fB, Id\fR \fIevr\fR\fB, int\fR \fIflags\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
+my \fI$id\fR \fB=\fR \fIpool\fR\fB\->rel2id(\fR\fI$nameid\fR\fB,\fR \fI$evrid\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIid\fR \fB=\fR \fIpool\fR\fB\&.rel2id(\fR\fInameid\fR\fB,\fR \fIevrid\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIid\fR \fB=\fR \fIpool\fR\fB\&.rel2id(\fR\fInameid\fR\fB,\fR \fIevrid\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a \(lqrelational\(rq dependency\&. Such dependencies consist of a name part, the \fIflags\fR describing the relation, and a version part\&. The flags are:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB$solv::REL_EQ | $solv::REL_GT | $solv::REL_LT\fR
+\fBsolv\&.REL_EQ | solv\&.REL_GT | solv\&.REL_LT\fR
+\fBSolv::REL_EQ | Solv::REL_GT | Solv::REL_LT\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Thus, if you want a \(lq<=\(rq relation, you would use \fBREL_LT | REL_EQ\fR\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id2langid(Id\fR \fIid\fR\fB, const char *\fR\fIlang\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
+my \fI$id\fR \fB=\fR \fI$pool\fR\fB\->id2langid(\fR\fI$id\fR\fB,\fR \fI$language\fR\fB)\fR;
+\fIid\fR \fB=\fR \fIpool\fR\fB\&.id2langid(\fR\fIid\fR\fB,\fR \fIlanguage\fR\fB)\fR
+\fIid\fR \fB=\fR \fIpool\fR\fB\&.id2langid(\fR\fIid\fR\fB,\fR \fIlanguage\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a language specific Id from some other id\&. This function simply converts the id into a string, appends a dot and the specified language to the string and converts the result back into an Id\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *dep2str(Id\fR \fIid\fR\fB)\fR
+\fI$string\fR \fB=\fR \fIpool\fR\fB\->dep2str(\fR\fI$id\fR\fB)\fR;
+\fIstring\fR \fB=\fR \fIpool\fR\fB\&.dep2str(\fR\fIid\fR\fB)\fR
+\fIstring\fR \fB=\fR \fIpool\fR\fB\&.dep2str(\fR\fIid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert a dependency id into a string\&. If the id is just a string, this function has the same effect as id2str()\&. For relational dependencies, the result is the correct \(lqname relation evr\(rq string\&.
+.SH "THE DEPENDENCY CLASS"
+.sp
+The dependency class is an object orientated way to work with strings and dependencies\&. Internally, dependencies are represented as Ids, i\&.e\&. simple numbers\&. Dependency objects can be constructed by using the Pool\(cqs Dep() method\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool;\fR /* read only */
+\fI$dep\fR\fB\->{pool}\fR
+\fIdep\fR\fB\&.pool\fR
+\fIdep\fR\fB\&.pool\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back reference to the pool this dependency belongs to\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id;\fR /* read only */
+\fI$dep\fR\fB\->{id}\fR
+\fIdep\fR\fB\&.id\fR
+\fIdep\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The id of this dependency\&.
+.SH "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep Rel(int\fR \fIflags\fR\fB, DepId\fR \fIevrid\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
+my \fI$reldep\fR \fB=\fR \fI$dep\fR\fB\->Rel(\fR\fI$flags\fR\fB,\fR \fI$evrdep\fR\fB)\fR;
+\fIreldep\fR \fB=\fR \fIdep\fR\fB\&.Rel(\fR\fIflags\fR\fB,\fR \fIevrdep\fR\fB)\fR
+\fIreldep\fR \fB=\fR \fIdep\fR\fB\&.Rel(\fR\fIflags\fR\fB,\fR \fIevrdep\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a relational dependency from to string dependencies and a flags argument\&. See the pool\(cqs rel2id method for a description of the flags\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSelection Selection_name(int\fR \fIsetflags\fR \fB= 0)\fR
+my \fI$sel\fR \fB=\fR \fI$dep\fR\fB\->Selection_name()\fR;
+\fIsel\fR \fB=\fR \fIdep\fR\fB\&.Selection_name()\fR
+\fIsel\fR \fB=\fR \fIdep\fR\fB\&.Selection_name()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a Selection from a dependency\&. The selection consists of all packages that have a name equal to the dependency\&. If the dependency is of a relational type, the packages version must also fulfill the dependency\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSelection Selection_provides(int\fR \fIsetflags\fR \fB= 0)\fR
+my \fI$sel\fR \fB=\fR \fI$dep\fR\fB\->Selection_provides()\fR;
+\fIsel\fR \fB=\fR \fIdep\fR\fB\&.Selection_provides()\fR
+\fIsel\fR \fB=\fR \fIdep\fR\fB\&.Selection_provides()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a Selection from a dependency\&. The selection consists of all packages that have at least one provides matching the dependency\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *str()\fR
+my \fI$str\fR \fB=\fR \fI$dep\fR\fB\->str()\fR;
+\fIstr\fR \fB=\fR \fI$dep\fR\fB\&.str()\fR
+\fIstr\fR \fB=\fR \fI$dep\fR\fB\&.str()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the dependency\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$dep\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIdep\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIdep\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as calling the str() method\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<equality>\fR
+\fBif (\fR\fI$dep1\fR \fB==\fR \fI$dep2\fR\fB)\fR
+\fBif\fR \fIdep1\fR \fB==\fR \fIdep2\fR\fB:\fR
+\fBif\fR \fIdep1\fR \fB==\fR \fIdep2\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The dependencies are equal if they are part of the same pool and have the same ids\&.
+.SH "THE REPOSITORY CLASS"
+.sp
+A Repository describes a group of packages, normally coming from the same source\&. Repositories are created by the Pool\(cqs add_repo() method\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool;\fR /* read only */
+\fI$repo\fR\fB\->{pool}\fR
+\fIrepo\fR\fB\&.pool\fR
+\fIrepo\fR\fB\&.pool\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back reference to the pool this dependency belongs to\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id;\fR /* read only */
+\fI$repo\fR\fB\->{id}\fR
+\fIrepo\fR\fB\&.id\fR
+\fIrepo\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The id of the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *name;\fR /* read/write */
+\fI$repo\fR\fB\->{name}\fR
+\fIrepo\fR\fB\&.name\fR
+\fIrepo\fR\fB\&.name\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The repositories name\&. To libsolv, the name is just a string with no specific meaning\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint priority;\fR /* read/write */
+\fI$repo\fR\fB\->{priority}\fR
+\fIrepo\fR\fB\&.priority\fR
+\fIrepo\fR\fB\&.priority\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The priority of the repository\&. A higher number means that packages of this repository will be chosen over other repositories, even if they have a greater package version\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint subpriority;\fR /* read/write */
+\fI$repo\fR\fB\->{subpriority}\fR
+\fIrepo\fR\fB\&.subpriority\fR
+\fIrepo\fR\fB\&.subpriority\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The sub\-priority of the repository\&. This value is compared when the priorities of two repositories are the same\&. It is useful to make the library prefer on\-disk repositories to remote ones\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint nsolvables;\fR /* read only */
+\fI$repo\fR\fB\->{nsolvables}\fR
+\fIrepo\fR\fB\&.nsolvables\fR
+\fIrepo\fR\fB\&.nsolvables\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The number of solvables in this repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid *appdata;\fR /* read/write */
+\fI$repo\fR\fB\->{appdata}\fR
+\fIrepo\fR\fB\&.appdata\fR
+\fIrepo\fR\fB\&.appdata\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Application specific data that may be used in any way by the code using the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDatapos *meta;\fR /* read only */
+\fI$repo\fR\fB\->{meta}\fR
+\fIrepo\fR\fB\&.meta\fR
+\fIrepo\fR\fB\&.meta\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a Datapos object of the repodata\(cqs metadata\&. You can use the lookup methods of the Datapos class to lookup metadata attributes, like the repository timestamp\&.
+.SS "CONSTANTS"
+.PP
+\fBREPO_REUSE_REPODATA\fR
+.RS 4
+Reuse the last repository data area (\(lqrepodata\(rq) instead of creating a new one\&.
+.RE
+.PP
+\fBREPO_NO_INTERNALIZE\fR
+.RS 4
+Do not internalize the added repository data\&. This is useful if you plan to add more data because internalization is a costly operation\&.
+.RE
+.PP
+\fBREPO_LOCALPOOL\fR
+.RS 4
+Use the repodata\(cqs pool for Id storage instead of the global pool\&. Useful if you don\(cqt want to pollute the global pool with many unneeded ids, like when storing the filelist\&.
+.RE
+.PP
+\fBREPO_USE_LOADING\fR
+.RS 4
+Use the repodata that is currently being loaded instead of creating a new one\&. This only makes sense if used in a load callback\&.
+.RE
+.PP
+\fBREPO_EXTEND_SOLVABLES\fR
+.RS 4
+Do not create new solvables for the new data, but match existing solvables and add the data to them\&. Repository metadata is often split into multiple parts, with one primary file describing all packages and other parts holding information that is normally not needed, like the changelog\&.
+.RE
+.PP
+\fBREPO_USE_ROOTDIR\fR
+.RS 4
+Prepend the pool\(cqs rootdir to the path when doing file operations\&.
+.RE
+.PP
+\fBREPO_NO_LOCATION\fR
+.RS 4
+Do not add a location element to the solvables\&. Useful if the solvables are not in the final position, so you can add the correct location later in your code\&.
+.RE
+.PP
+\fBSOLV_ADD_NO_STUBS\fR
+.RS 4
+Do not create stubs for repository parts that can be downloaded on demand\&.
+.RE
+.PP
+\fBSUSETAGS_RECORD_SHARES\fR
+.RS 4
+This is specific to the add_susetags() method\&. Susetags allows one to refer to already read packages to save disk space\&. If this data sharing needs to work over multiple calls to add_susetags, you need to specify this flag so that the share information is made available to subsequent calls\&.
+.RE
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid free(bool\fR \fIreuseids\fR \fB= 0)\fR
+\fI$repo\fR\fB\->free()\fR;
+\fIrepo\fR\fB\&.free()\fR
+\fIrepo\fR\fB\&.free()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Free the repository and all solvables it contains\&. If \fIreuseids\fR is set to true, the solvable ids and the repository id may be reused by the library when added new solvables\&. Thus you should leave it false if you are not sure that somebody holds a reference\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid empty(bool\fR \fIreuseids\fR \fB= 0)\fR
+\fI$repo\fR\fB\->empty()\fR;
+\fIrepo\fR\fB\&.empty()\fR
+\fIrepo\fR\fB\&.empty()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Free all the solvables in a repository\&. The repository will be empty after this call\&. See the free() method for the meaning of \fIreuseids\fR\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool isempty()\fR
+\fI$repo\fR\fB\->isempty()\fR
+\fIrepo\fR\fB\&.empty()\fR
+\fIrepo\fR\fB\&.empty?\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return true if there are no solvables in this repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid internalize()\fR
+\fI$repo\fR\fB\->internalize()\fR;
+\fIrepo\fR\fB\&.internalize()\fR
+\fIrepo\fR\fB\&.internalize()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Internalize added data\&. Data must be internalized before it is available to the lookup and data iterator functions\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool write(FILE *\fR\fIfp\fR\fB)\fR
+\fI$repo\fR\fB\->write(\fR\fI$fp\fR\fB)\fR
+\fIrepo\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Write a repo as a \(lqsolv\(rq file\&. These files can be read very fast and thus are a good way to cache repository data\&. Returns false if there was some error writing the file\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvableiterator solvables_iter()\fR
+\fBfor my\fR \fI$solvable\fR \fB(\fR\fI@\fR\fB{\fR\fI$repo\fR\fB\->solvables_iter()})\fR
+\fBfor\fR \fIsolvable\fR \fBin\fR \fIrepo\fR\fB\&.solvables_iter():\fR
+\fBfor\fR \fIsolvable\fR \fBin\fR \fIrepo\fR\fB\&.solvables_iter()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterate over all solvables in a repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepodata add_repodata(int\fR \fIflags\fR \fB= 0)\fR
+my \fI$repodata\fR \fB=\fR \fI$repo\fR\fB\->add_repodata()\fR;
+\fIrepodata\fR \fB=\fR \fIrepo\fR\fB\&.add_repodata()\fR
+\fIrepodata\fR \fB=\fR \fIrepo\fR\fB\&.add_repodata()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a new repodata area to the repository\&. This is normally automatically done by the repo_add methods, so you need this method only in very rare circumstances\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid create_stubs()\fR
+\fI$repo\fR\fB\->create_stubs()\fR;
+\fIrepo\fR\fB\&.create_stubs()\fR
+\fIrepo\fR\fB\&.create_stubs()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Calls the create_stubs() repodata method for the last repodata of the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool iscontiguous()\fR
+\fI$repo\fR\fB\->iscontiguous()\fR
+\fIrepo\fR\fB\&.iscontiguous()\fR
+\fIrepo\fR\fB\&.iscontiguous?\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return true if the solvables of this repository are all in a single block with no holes, i\&.e\&. they have consecutive ids\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepodata first_repodata()\fR
+my \fI$repodata\fR \fB=\fR \fI$repo\fR\fB\->first_repodata()\fR;
+\fIrepodata\fR \fB=\fR \fIrepo\fR\fB\&.first_repodata()\fR
+\fIrepodata\fR \fB=\fR \fIrepo\fR\fB\&.first_repodata()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Checks if all repodatas but the first repodata are extensions, and return the first repodata if this is the case\&. Useful if you want to do a store/retrieve sequence on the repository to reduce the memory using and enable paging, as this does not work if the repository contains multiple non\-extension repodata areas\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSelection Selection(int\fR \fIsetflags\fR \fB= 0)\fR
+my \fI$sel\fR \fB=\fR \fI$repo\fR\fB\->Selection()\fR;
+\fIsel\fR \fB=\fR \fIrepo\fR\fB\&.Selection()\fR
+\fIsel\fR \fB=\fR \fIrepo\fR\fB\&.Selection()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a Selection consisting of all packages in the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDataiterator Dataiterator(Id\fR \fIkey\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$di\fR \fB=\fR \fI$repo\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIdi\fR \fB=\fR \fIrepo\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIdi\fR \fB=\fR \fIrepo\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDataiterator Dataiterator_meta(Id\fR \fIkey\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$di\fR \fB=\fR \fI$repo\fR\fB\->Dataiterator_meta(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIdi\fR \fB=\fR \fIrepo\fR\fB\&.Dataiterator_meta(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIdi\fR \fB=\fR \fIrepo\fR\fB\&.Dataiterator_meta(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterate over the matching data elements in this repository\&. See the Dataiterator class for more information\&. The Dataiterator() method iterates over all solvables in a repository, whereas the Dataiterator_meta method only iterates over the repository\(cqs meta data\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$repo\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIrepo\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIrepo\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the name of the repository, or "Repo#<id>" if no name is set\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<equality>\fR
+\fBif (\fR\fI$repo1\fR \fB==\fR \fI$repo2\fR\fB)\fR
+\fBif\fR \fIrepo1\fR \fB==\fR \fIrepo2\fR\fB:\fR
+\fBif\fR \fIrepo1\fR \fB==\fR \fIrepo2\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Two repositories are equal if they belong to the same pool and have the same id\&.
+.SS "DATA ADD METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable add_solvable()\fR
+\fI$repo\fR\fB\->add_solvable()\fR;
+\fIrepo\fR\fB\&.add_solvable()\fR
+\fIrepo\fR\fB\&.add_solvable()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a single empty solvable to the repository\&. Returns a Solvable object, see the Solvable class for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_solv(const char *\fR\fIname\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_solv(\fR\fI$name\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_solv(\fR\fIname\fR\fB)\fR
+\fIrepo\fR\fB\&.add_solv(\fR\fIname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_solv(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_solv(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Read a \(lqsolv\(rq file and add its contents to the repository\&. These files can be written with the write() method and are normally used as fast cache for repository metadata\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_rpmdb(int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_rpmdb()\fR;
+\fIrepo\fR\fB\&.add_rpmdb()\fR
+\fIrepo\fR\fB\&.add_rpmdb()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_rpmdb_reffp(FILE *\fR\fIreffp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_rpmdb_reffp(\fR\fI$reffp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_rpmdb_reffp(\fR\fIreffp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_rpmdb_reffp(\fR\fIreffp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the contents of the rpm database to the repository\&. If a solv file containing an old version of the database is available, it can be passed as reffp to speed up reading\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable add_rpm(const char *\fR\fIfilename\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$solvable\fR \fB=\fR \fI$repo\fR\fB\->add_rpm(\fR\fI$filename\fR\fB)\fR;
+\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_rpm(\fR\fIfilename\fR\fB)\fR
+\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_rpm(\fR\fIfilename\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the metadata of a single rpm package to the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_rpmdb_pubkeys(int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_rpmdb_pubkeys()\fR;
+\fIrepo\fR\fB\&.add_rpmdb_pubkeys()\fR
+\fIrepo\fR\fB\&.add_rpmdb_pubkeys()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add all pubkeys contained in the rpm database to the repository\&. Note that newer rpm versions also allow to store the pubkeys in some directory instead of the rpm database\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable add_pubkey(const char *\fR\fIkeyfile\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$solvable\fR \fB=\fR \fI$repo\fR\fB\->add_pubkey(\fR\fI$keyfile\fR\fB)\fR;
+\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_pubkey(\fR\fIkeyfile\fR\fB)\fR
+\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_pubkey(\fR\fIkeyfile\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a pubkey from a file to the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_rpmmd(FILE *\fR\fIfp\fR\fB, const char *\fR\fIlanguage\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_rpmmd(\fR\fI$fp\fR\fB,\fR \fIundef\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_rpmmd(\fR\fIfp\fR\fB,\fR \fINone\fR\fB)\fR
+\fIrepo\fR\fB\&.add_rpmmd(\fR\fIfp\fR\fB,\fR \fInil\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add metadata stored in the "rpm\-md" format (i\&.e\&. from files in the \(lqrepodata\(rq directory) to a repository\&. Supported files are "primary", "filelists", "other", "suseinfo"\&. Do not forget to specify the \fBREPO_EXTEND_SOLVABLES\fR for extension files like "filelists" and "other"\&. Use the \fIlanguage\fR parameter if you have language extension files, otherwise simply use a \fBundef\fR/\fBNone\fR/\fBnil\fR parameter\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_repomdxml(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_repomdxml(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_repomdxml(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_repomdxml(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the repomd\&.xml meta description from the "rpm\-md" format to the repository\&. This file contains information about the repository like keywords, and also a list of all database files with checksums\&. The data is added to the "meta" section of the repository, i\&.e\&. no package gets created\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_updateinfoxml(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_updateinfoxml(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_updateinfoxml(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_updateinfoxml(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the updateinfo\&.xml file containing available maintenance updates to the repository\&. All updates are created as special packages that have a "patch:" prefix in their name\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_deltainfoxml(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_deltainfoxml(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_deltainfoxml(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_deltainfoxml(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the deltainfo\&.xml file (also called prestodelta\&.xml) containing available delta\-rpms to the repository\&. The data is added to the "meta" section, i\&.e\&. no package gets created\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_debdb(int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_debdb()\fR;
+\fIrepo\fR\fB\&.add_debdb()\fR
+\fIrepo\fR\fB\&.add_debdb()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the contents of the debian installed package database to the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_debpackages(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_debpackages(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_debpackages(\fR\fI$fp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_debpackages(\fR\fI$fp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the contents of the debian repository metadata (the "packages" file) to the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable add_deb(const char *\fR\fIfilename\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$solvable\fR \fB=\fR \fI$repo\fR\fB\->add_deb(\fR\fI$filename\fR\fB)\fR;
+\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_deb(\fR\fIfilename\fR\fB)\fR
+\fIsolvable\fR \fB=\fR \fIrepo\fR\fB\&.add_deb(\fR\fIfilename\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the metadata of a single deb package to the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_mdk(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_mdk(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_mdk(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_mdk(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the contents of the mageia/mandriva repository metadata (the "synthesis\&.hdlist" file) to the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_mdk_info(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_mdk(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_mdk(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_mdk(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Extend the packages from the synthesis file with the info\&.xml and files\&.xml data\&. Do not forget to specify \fBREPO_EXTEND_SOLVABLES\fR\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_arch_repo(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_arch_repo(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_arch_repo(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_arch_repo(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the contents of the archlinux repository metadata (the "\&.db\&.tar" file) to the repository\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_arch_local(const char *\fR\fIdir\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_arch_local(\fR\fI$dir\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_arch_local(\fR\fIdir\fR\fB)\fR
+\fIrepo\fR\fB\&.add_arch_local(\fR\fIdir\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the contents of the archlinux installed package database to the repository\&. The \fIdir\fR parameter is usually set to "/var/lib/pacman/local"\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_content(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_content(\fR\fI$fp\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_content(\fR\fIfp\fR\fB)\fR
+\fIrepo\fR\fB\&.add_content(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the \(lqcontent\(rq meta description from the susetags format to the repository\&. This file contains information about the repository like keywords, and also a list of all database files with checksums\&. The data is added to the "meta" section of the repository, i\&.e\&. no package gets created\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_susetags(FILE *\fR\fIfp\fR\fB, Id\fR \fIdefvendor\fR\fB, const char *\fR\fIlanguage\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_susetags(\fR\fI$fp\fR\fB,\fR \fI$defvendor\fR\fB,\fR \fI$language\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_susetags(\fR\fIfp\fR\fB,\fR \fIdefvendor\fR\fB,\fR \fIlanguage\fR\fB)\fR
+\fIrepo\fR\fB\&.add_susetags(\fR\fIfp\fR\fB,\fR \fIdefvendor\fR\fB,\fR \fIlanguage\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add repository metadata in the susetags format to the repository\&. Like with add_rpmmd, you can specify a language if you have language extension files\&. The \fIdefvendor\fR parameter provides a default vendor for packages with missing vendors, it is usually provided in the content file\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_products(const char *\fR\fIdir\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
+\fI$repo\fR\fB\->add_products(\fR\fI$dir\fR\fB)\fR;
+\fIrepo\fR\fB\&.add_products(\fR\fIdir\fR\fB)\fR
+\fIrepo\fR\fB\&.add_products(\fR\fIdir\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the installed SUSE products database to the repository\&. The \fIdir\fR parameter is usually "/etc/products\&.d"\&.
+.SH "THE SOLVABLE CLASS"
+.sp
+A solvable describes all the information of one package\&. Each solvable belongs to one repository, it can be added and filled manually but in most cases solvables will get created by the repo_add methods\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepo *repo;\fR /* read only */
+\fI$solvable\fR\fB\->{repo}\fR
+\fIsolvable\fR\fB\&.repo\fR
+\fIsolvable\fR\fB\&.repo\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The repository this solvable belongs to\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool;\fR /* read only */
+\fI$solvable\fR\fB\->{pool}\fR
+\fIsolvable\fR\fB\&.pool\fR
+\fIsolvable\fR\fB\&.pool\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The pool this solvable belongs to, same as the pool of the repo\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id;\fR /* read only */
+\fI$solvable\fR\fB\->{id}\fR
+\fIsolvable\fR\fB\&.id\fR
+\fIsolvable\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The specific id of the solvable\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *name;\fR /* read/write */
+\fI$solvable\fR\fB\->{name}\fR
+\fIsolvable\fR\fB\&.name\fR
+\fIsolvable\fR\fB\&.name\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *evr;\fR /* read/write */
+\fI$solvable\fR\fB\->{evr}\fR
+\fIsolvable\fR\fB\&.evr\fR
+\fIsolvable\fR\fB\&.evr\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *arch;\fR /* read/write */
+\fI$solvable\fR\fB\->{arch}\fR
+\fIsolvable\fR\fB\&.arch\fR
+\fIsolvable\fR\fB\&.arch\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *vendor;\fR /* read/write */
+\fI$solvable\fR\fB\->{vendor}\fR
+\fIsolvable\fR\fB\&.vendor\fR
+\fIsolvable\fR\fB\&.vendor\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Easy access to often used attributes of solvables\&. They are internally stored as Ids\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId nameid;\fR /* read/write */
+\fI$solvable\fR\fB\->{nameid}\fR
+\fIsolvable\fR\fB\&.nameid\fR
+\fIsolvable\fR\fB\&.nameid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId evrid;\fR /* read/write */
+\fI$solvable\fR\fB\->{evrid}\fR
+\fIsolvable\fR\fB\&.evrid\fR
+\fIsolvable\fR\fB\&.evrid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId archid;\fR /* read/write */
+\fI$solvable\fR\fB\->{archid}\fR
+\fIsolvable\fR\fB\&.archid\fR
+\fIsolvable\fR\fB\&.archid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId vendorid;\fR /* read/write */
+\fI$solvable\fR\fB\->{vendorid}\fR
+\fIsolvable\fR\fB\&.vendorid\fR
+\fIsolvable\fR\fB\&.vendorid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Raw interface to the ids\&. Useful if you want to search for a specific id and want to avoid the string compare overhead\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *lookup_str(Id\fR \fIkeyname\fR\fB)\fR
+my \fI$string\fR \fB=\fR \fI$solvable\fR\fB\->lookup_str(\fR\fI$keyname\fR\fB)\fR;
+\fIstring\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_str(\fR\fIkeyname\fR\fB)\fR
+\fIstring\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_str(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId lookup_id(Id\fR \fIkeyname\fR\fB)\fR
+my \fI$id\fR \fB=\fR \fI$solvable\fR\fB\->lookup_id(\fR\fI$keyname\fR\fB)\fR;
+\fIid\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_id(\fR\fIsolvid\fR\fB)\fR
+\fIid\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_id(\fR\fIsolvid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBunsigned long long lookup_num(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInotfound\fR \fB= 0)\fR
+my \fI$num\fR \fB=\fR \fI$solvable\fR\fB\->lookup_num(\fR\fI$keyname\fR\fB)\fR;
+\fInum\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_num(\fR\fIkeyname\fR\fB)\fR
+\fInum\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_num(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool lookup_void(Id\fR \fIkeyname\fR\fB)\fR
+my \fI$bool\fR \fB=\fR \fI$solvable\fR\fB\->lookup_void(\fR\fI$keyname\fR\fB)\fR;
+\fIbool\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_void(\fR\fIkeyname\fR\fB)\fR
+\fIbool\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_void(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBChksum lookup_checksum(Id\fR \fIkeyname\fR\fB)\fR
+my \fI$chksum\fR \fB=\fR \fI$solvable\fR\fB\->lookup_checksum(\fR\fI$keyname\fR\fB)\fR;
+\fIchksum\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_checksum(\fR\fIkeyname\fR\fB)\fR
+\fIchksum\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_checksum(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId *lookup_idarray(Id\fR \fIkeyname\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
+my \fI@ids\fR \fB=\fR \fI$solvable\fR\fB\->lookup_idarray(\fR\fI$keyname\fR\fB)\fR;
+\fIids\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_idarray(\fR\fIkeyname\fR\fB)\fR
+\fIids\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_idarray(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep *lookup_deparray(Id\fR \fIkeyname\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
+my \fI@deps\fR \fB=\fR \fI$solvable\fR\fB\->lookup_deparray(\fR\fI$keyname\fR\fB)\fR;
+\fIdeps\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_deparray(\fR\fIkeyname\fR\fB)\fR
+\fIdeps\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_deparray(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Generic lookup methods\&. Retrieve data stored for the specific keyname\&. The lookup_idarray() method will return an array of Ids, use lookup_deparray if you want an array of Dependency objects instead\&. Some Id arrays contain two parts of data divided by a specific marker, for example the provides array uses the SOLVABLE_FILEMARKER id to store both the ids provided by the package and the ids added by the addfileprovides method\&. The default, \-1, translates to the correct marker for the keyname and returns the first part of the array, use 1 to select the second part or 0 to retrieve all ids including the marker\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *lookup_location(unsigned int *\fR\fIOUTPUT\fR\fB)\fR;
+my \fB(\fR\fI$location\fR\fB,\fR \fI$medianr\fR\fB) =\fR \fI$solvable\fR\fB\->lookup_location()\fR;
+\fIlocation\fR\fB,\fR \fImedianr\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_location()\fR
+\fIlocation\fR\fB,\fR \fImedianr\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_location()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a tuple containing the on\-media location and an optional media number for multi\-part repositories (e\&.g\&. repositories spawning multiple DVDs)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDataiterator Dataiterator(Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$di\fR \fB=\fR \fI$solvable\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIdi\fR \fB=\fR \fIsolvable\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIdi\fR \fB=\fR \fIsolvable\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterate over the matching data elements\&. See the Dataiterator class for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add_deparray(Id\fR \fIkeyname\fR\fB, DepId\fR \fIdep\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR;
+\fI$solvable\fR\fB\->add_deparray(\fR\fI$keyname\fR\fB,\fR \fI$dep\fR\fB)\fR;
+\fIsolvable\fR\fB\&.add_deparray(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
+\fIsolvable\fR\fB\&.add_deparray(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a new dependency to the attributes stored in keyname\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid unset(Id\fR \fIkeyname\fR\fB)\fR;
+\fI$solvable\fR\fB\->unset(\fR\fI$keyname\fR\fB)\fR;
+\fIsolvable\fR\fB\&.unset(\fR\fIkeyname\fR\fB)\fR
+\fIsolvable\fR\fB\&.unset(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Delete data stored for the specific keyname\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool installable()\fR;
+\fI$solvable\fR\fB\->installable()\fR
+\fIsolvable\fR\fB\&.installable()\fR
+\fIsolvable\fR\fB\&.installable?\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return true if the solvable is installable on the system\&. Solvables are not installable if the system does not support their architecture\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool isinstalled()\fR;
+\fI$solvable\fR\fB\->isinstalled()\fR
+\fIsolvable\fR\fB\&.isinstalled()\fR
+\fIsolvable\fR\fB\&.isinstalled?\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return true if the solvable is installed on the system\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool identical(Solvable *\fR\fIother\fR\fB)\fR
+\fI$solvable\fR\fB\->identical(\fR\fI$other\fR\fB)\fR
+\fI$solvable\fR\fB\&.identical(\fR\fIother\fR\fB)\fR
+\fI$solvable\fR\fB\&.identical?(\fR\fIother\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return true if the two solvables are identical\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint evrcmp(Solvable *\fR\fIother\fR\fB)\fR
+\fI$solvable\fR\fB\->evrcmp(\fR\fIother\fR\fB)\fR
+\fI$solvable\fR\fB\&.evrcmp(\fR\fIother\fR\fB)\fR
+\fI$solvable\fR\fB\&.evrcmp(\fR\fIother\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Returns \-1 if the epoch/version/release of the solvable is less than the one from the other solvable, 1 if it is greater, and 0 if they are equal\&. Note that "equal" does not mean that the evr is identical\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSelection Selection(int\fR \fIsetflags\fR \fB= 0)\fR
+my \fI$sel\fR \fB=\fR \fI$solvable\fR\fB\->Selection()\fR;
+\fIsel\fR \fB=\fR \fIsolvable\fR\fB\&.Selection()\fR
+\fIsel\fR \fB=\fR \fIsolvable\fR\fB\&.Selection()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a Selection containing just the single solvable\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *str()\fR
+my \fI$str\fR \fB=\fR \fI$solvable\fR\fB\->str()\fR;
+\fIstr\fR \fB=\fR \fI$solvable\fR\fB\&.str()\fR
+\fIstr\fR \fB=\fR \fI$solvable\fR\fB\&.str()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the solvable\&. The string consists of the name, version, and architecture of the Solvable\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$solvable\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIsolvable\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIsolvable\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as calling the str() method\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<equality>\fR
+\fBif (\fR\fI$solvable1\fR \fB==\fR \fI$solvable2\fR\fB)\fR
+\fBif\fR \fIsolvable1\fR \fB==\fR \fIsolvable2\fR\fB:\fR
+\fBif\fR \fIsolvable1\fR \fB==\fR \fIsolvable2\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Two solvables are equal if they are part of the same pool and have the same ids\&.
+.SH "THE DATAITERATOR CLASS"
+.sp
+Dataiterators can be used to do complex string searches or to iterate over arrays\&. They can be created via the constructors in the Pool, Repo, and Solvable classes\&. The Repo and Solvable constructors will limit the search to the repository or the specific package\&.
+.SS "CONSTANTS"
+.PP
+\fBSEARCH_STRING\fR
+.RS 4
+Return a match if the search string matches the value\&.
+.RE
+.PP
+\fBSEARCH_STRINGSTART\fR
+.RS 4
+Return a match if the value starts with the search string\&.
+.RE
+.PP
+\fBSEARCH_STRINGEND\fR
+.RS 4
+Return a match if the value ends with the search string\&.
+.RE
+.PP
+\fBSEARCH_SUBSTRING\fR
+.RS 4
+Return a match if the search string can be matched somewhere in the value\&.
+.RE
+.PP
+\fBSEARCH_GLOB\fR
+.RS 4
+Do a glob match of the search string against the value\&.
+.RE
+.PP
+\fBSEARCH_REGEX\fR
+.RS 4
+Do a regular expression match of the search string against the value\&.
+.RE
+.PP
+\fBSEARCH_NOCASE\fR
+.RS 4
+Ignore case when matching strings\&. Works for all the above match types\&.
+.RE
+.PP
+\fBSEARCH_FILES\fR
+.RS 4
+Match the complete filenames of the file list, not just the base name\&.
+.RE
+.PP
+\fBSEARCH_COMPLETE_FILELIST\fR
+.RS 4
+When matching the file list, check every file of the package not just the subset from the primary metadata\&.
+.RE
+.PP
+\fBSEARCH_CHECKSUMS\fR
+.RS 4
+Allow the matching of checksum entries\&.
+.RE
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid prepend_keyname(Id\fR \fIkeyname\fR\fB)\fR;
+\fI$di\fR\fB\->prepend_keyname(\fR\fI$keyname\fR\fB)\fR;
+\fIdi\fR\fB\&.prepend_keyname(\fR\fIkeyname\fR\fB)\fR
+\fIdi\fR\fB\&.prepend_keyname(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Do a sub\-search in the array stored in keyname\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid skip_solvable()\fR;
+\fI$di\fR\fB\->kip_solvable()\fR;
+\fIdi\fR\fB\&.skip_solvable()\fR
+\fIdi\fR\fB\&.skip_solvable()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Stop matching the current solvable and advance to the next one\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<iteration>\fR
+\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterate through the matches\&. If there is a match, the object in d will be of type Datamatch\&.
+.SH "THE DATAMATCH CLASS"
+.sp
+Objects of this type will be created for every value matched by a dataiterator\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool;\fR /* read only */
+\fI$d\fR\fB\->{pool}\fR
+\fId\fR\fB\&.pool\fR
+\fId\fR\fB\&.pool\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to pool\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepo *repo;\fR /* read only */
+\fI$d\fR\fB\->{repo}\fR
+\fId\fR\fB\&.repo\fR
+\fId\fR\fB\&.repo\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The repository containing the matched object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *solvable;\fR /* read only */
+\fI$d\fR\fB\->{solvable}\fR
+\fId\fR\fB\&.solvable\fR
+\fId\fR\fB\&.solvable\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The solvable containing the value that was matched\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId solvid;\fR /* read only */
+\fI$d\fR\fB\->{solvid}\fR
+\fId\fR\fB\&.solvid\fR
+\fId\fR\fB\&.solvid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The id of the solvable that matched\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId\fR \fIkey_id\fR;
+\fI$d\fR\fB\->{\fR\fIkey_id\fR\fB}\fR
+\fId\fR\fB\&.key_id\fR
+\fId\fR\fB\&.key_id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *\fR\fIkey_idstr\fR;
+\fI$d\fR\fB\->{\fR\fIkey_idstr\fR\fB}\fR
+\fId\fR\fB\&.key_idstr\fR
+\fId\fR\fB\&.key_idstr\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The keyname that matched, either as id or string\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId\fR \fItype_id\fR;
+\fI$d\fR\fB\->{\fR\fItype_id\fR\fB}\fR
+\fId\fR\fB\&.type_id\fR
+\fId\fR\fB\&.type_id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *\fR\fItype_idstr\fR;
+\fI$d\fR\fB\->{\fR\fItype_idstr\fR\fB}\fR;
+\fId\fR\fB\&.type_idstr\fR
+\fId\fR\fB\&.type_idstr\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The key type of the value that was matched, either as id or string\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId\fR \fIid\fR;
+\fI$d\fR\fB\->{id}\fR
+\fId\fR\fB\&.id\fR
+\fId\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId\fR \fIidstr\fR;
+\fI$d\fR\fB\->{idstr}\fR
+\fId\fR\fB\&.idstr\fR
+\fId\fR\fB\&.idstr\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The Id of the value that was matched (only valid for id types), either as id or string\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *\fR\fIstr\fR;
+\fI$d\fR\fB\->{str}\fR
+\fId\fR\fB\&.str\fR
+\fId\fR\fB\&.str\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The string value that was matched (only valid for string types)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBunsigned long long\fR \fInum\fR;
+\fI$d\fR\fB\->{num}\fR
+\fId\fR\fB\&.num\fR
+\fId\fR\fB\&.num\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The numeric value that was matched (only valid for numeric types)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBunsigned int\fR \fInum2\fR;
+\fI$d\fR\fB\->{num2}\fR
+\fId\fR\fB\&.num2\fR
+\fId\fR\fB\&.num2\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The secondary numeric value that was matched (only valid for types containing two values)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBunsigned int\fR \fIbinary\fR;
+\fI$d\fR\fB\->{binary}\fR
+\fId\fR\fB\&.binary\fR
+\fId\fR\fB\&.binary\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The value in binary form, useful for checksums and other data that cannot be represented as a string\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDatapos pos()\fR;
+my \fI$pos\fR \fB=\fR \fI$d\fR\fB\->pos()\fR;
+\fIpos\fR \fB=\fR \fId\fR\fB\&.pos()\fR
+\fIpos\fR \fB=\fR \fId\fR\fB\&.pos()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The position object of the current match\&. It can be used to do sub\-searches starting at the match (if it is of an array type)\&. See the Datapos class for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDatapos parentpos()\fR;
+my \fI$pos\fR \fB=\fR \fI$d\fR\fB\->parentpos()\fR;
+\fIpos\fR \fB=\fR \fId\fR\fB\&.parentpos()\fR
+\fIpos\fR \fB=\fR \fId\fR\fB\&.parentpos()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The position object of the array containing the current match\&. It can be used to do sub\-searches, see the Datapos class for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$d\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fId\fR\fB)\fR
+\fIstr\fR \fB=\fR \fId\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the stringification of the matched value\&. Stringification depends on the search flags, for file list entries it will return just the base name unless SEARCH_FILES is used, for checksums it will return an empty string unless SEARCH_CHECKSUMS is used\&. Numeric values are currently stringified to an empty string\&.
+.SH "THE SELECTION CLASS"
+.sp
+Selections are a way to easily deal with sets of packages\&. There are multiple constructors to create them, the most useful is probably the select() method in the Pool class\&.
+.SS "CONSTANTS"
+.PP
+\fBSELECTION_NAME\fR
+.RS 4
+Create the selection by matching package names\&.
+.RE
+.PP
+\fBSELECTION_PROVIDES\fR
+.RS 4
+Create the selection by matching package provides\&.
+.RE
+.PP
+\fBSELECTION_FILELIST\fR
+.RS 4
+Create the selection by matching package files\&.
+.RE
+.PP
+\fBSELECTION_CANON\fR
+.RS 4
+Create the selection by matching the canonical representation of the package\&. This is normally a combination of the name, the version, and the architecture of a package\&.
+.RE
+.PP
+\fBSELECTION_DOTARCH\fR
+.RS 4
+Allow an \(lq\&.<architecture>\(rq suffix when matching names or provides\&.
+.RE
+.PP
+\fBSELECTION_REL\fR
+.RS 4
+Allow the specification of a relation when matching names or provides, e\&.g\&. "name >= 1\&.2"\&.
+.RE
+.PP
+\fBSELECTION_INSTALLED_ONLY\fR
+.RS 4
+Limit the package search to installed packages\&.
+.RE
+.PP
+\fBSELECTION_SOURCE_ONLY\fR
+.RS 4
+Limit the package search to source packages only\&.
+.RE
+.PP
+\fBSELECTION_WITH_SOURCE\fR
+.RS 4
+Extend the package search to also match source packages\&. The default is only to match binary packages\&.
+.RE
+.PP
+\fBSELECTION_GLOB\fR
+.RS 4
+Allow glob matching for package names, package provides, and file names\&.
+.RE
+.PP
+\fBSELECTION_NOCASE\fR
+.RS 4
+Ignore case when matching package names, package provides, and file names\&.
+.RE
+.PP
+\fBSELECTION_FLAT\fR
+.RS 4
+Return only one selection element describing the selected packages\&. The default is to create multiple elements for all globbed packages\&. Multiple elements are useful if you want to turn the selection into an install job, in that case you want an install job for every globbed package\&.
+.RE
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool;\fR /* read only */
+\fI$d\fR\fB\->{pool}\fR
+\fId\fR\fB\&.pool\fR
+\fId\fR\fB\&.pool\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to pool\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint flags()\fR;
+my \fI$flags\fR \fB=\fR \fI$sel\fR\fB\->flags()\fR;
+\fIflags\fR \fB=\fR \fIsel\fR\fB\&.flags()\fR
+\fIflags\fR \fB=\fR \fIsel\fR\fB\&.flags()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the result flags of the selection\&. The flags are a subset of the ones used when creating the selection, they describe which method was used to get the result\&. For example, if you create the selection with \(lqSELECTION_NAME | SELECTION_PROVIDES\(rq, the resulting flags will either be SELECTION_NAME or SELECTION_PROVIDES depending if there was a package that matched the name or not\&. If there was no match at all, the flags will be zero\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool isempty()\fR;
+\fI$sel\fR\fB\->isempty()\fR
+\fIsel\fR\fB\&.isempty()\fR
+\fIsel\fR\fB\&.isempty?\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return true if the selection is empty, i\&.e\&. no package could be matched\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid filter(Selection *\fR\fIother\fR\fB)\fR
+\fI$sel\fR\fB\->filter(\fR\fI$other\fR\fB)\fR;
+\fIsel\fR\fB\&.filter(\fR\fIother\fR\fB)\fR
+\fIsel\fR\fB\&.filter(\fR\fIother\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Intersect two selections\&. Packages will only stay in the selection if there are also included in the other selecting\&. Does an in\-place modification\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add(Selection *\fR\fIother\fR\fB)\fR
+\fI$sel\fR\fB\->add(\fR\fI$other\fR\fB)\fR;
+\fIsel\fR\fB\&.add(\fR\fIother\fR\fB)\fR
+\fIsel\fR\fB\&.add(\fR\fIother\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Build the union of two selections\&. All packages of the other selection will be added to the set of packages of the selection object\&. Does an in\-place modification\&. Note that the selection flags are no longer meaningful after the add operation\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add_raw(Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR
+\fI$sel\fR\fB\->add_raw(\fR\fI$how\fR\fB,\fR \fI$what\fR\fB)\fR;
+\fIsel\fR\fB\&.add_raw(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
+\fIsel\fR\fB\&.add_raw(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a raw element to the selection\&. Check the Job class for information about the how and what parameters\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBJob *jobs(int\fR \fIaction\fR\fB)\fR
+my \fI@jobs\fR \fB=\fR \fI$sel\fR\fB\->jobs(\fR\fI$action\fR\fB)\fR;
+\fIjobs\fR \fB=\fR \fIsel\fR\fB\&.jobs(\fR\fIaction\fR\fB)\fR
+\fIjobs\fR \fB=\fR \fIsel\fR\fB\&.jobs(\fR\fIaction\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert a selection into an array of Job objects\&. The action parameter is or\-ed to the \(lqhow\(rq part of the job, it describes the type of job (e\&.g\&. install, erase)\&. See the Job class for the action and action modifier constants\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *solvables()\fR
+my \fI@solvables\fR \fB=\fR \fI$sel\fR\fB\->solvables()\fR;
+\fIsolvables\fR \fB=\fR \fIsel\fR\fB\&.solvables()\fR
+\fIsolvables\fR \fB=\fR \fIsel\fR\fB\&.solvables()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert a selection into an array of Solvable objects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$sel\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIsel\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIsel\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the selection\&.
+.SH "THE JOB CLASS"
+.sp
+Jobs are the way to specify to the dependency solver what to do\&. Most of the times jobs will get created by calling the jobs() method on a Selection object, but there is also a Job() constructor in the Pool class\&.
+.SS "CONSTANTS"
+.sp
+Selection constants:
+.PP
+\fBSOLVER_SOLVABLE\fR
+.RS 4
+The \(lqwhat\(rq part is the id of a solvable\&.
+.RE
+.PP
+\fBSOLVER_SOLVABLE_NAME\fR
+.RS 4
+The \(lqwhat\(rq part is the id of a package name\&.
+.RE
+.PP
+\fBSOLVER_SOLVABLE_PROVIDES\fR
+.RS 4
+The \(lqwhat\(rq part is the id of a package provides\&.
+.RE
+.PP
+\fBSOLVER_SOLVABLE_ONE_OF\fR
+.RS 4
+The \(lqwhat\(rq part is an offset into the \(lqwhatprovides\(rq data, created by calling the towhatprovides() pool method\&.
+.RE
+.PP
+\fBSOLVER_SOLVABLE_REPO\fR
+.RS 4
+The \(lqwhat\(rq part is the id of a repository\&.
+.RE
+.PP
+\fBSOLVER_SOLVABLE_ALL\fR
+.RS 4
+The \(lqwhat\(rq part is ignored, all packages are selected\&.
+.RE
+.PP
+\fBSOLVER_SOLVABLE_SELECTMASK\fR
+.RS 4
+A mask containing all the above selection bits\&.
+.RE
+.sp
+Action constants:
+.PP
+\fBSOLVER_NOOP\fR
+.RS 4
+Do nothing\&.
+.RE
+.PP
+\fBSOLVER_INSTALL\fR
+.RS 4
+Install a package of the specified set of packages\&. It tries to install the best matching package (i\&.e\&. the highest version of the packages from the repositories with the highest priority)\&.
+.RE
+.PP
+\fBSOLVER_ERASE\fR
+.RS 4
+Erase all of the packages from the specified set\&. If a package is not installed, erasing it will keep it from getting installed\&.
+.RE
+.PP
+\fBSOLVER_UPDATE\fR
+.RS 4
+Update the matching installed packages to their best version\&. If none of the specified packages are installed, try to update the installed packages to the specified versions\&. See the section about targeted updates about more information\&.
+.RE
+.PP
+\fBSOLVER_WEAKENDEPS\fR
+.RS 4
+Allow to break the dependencies of the matching packages\&. Handle with care\&.
+.RE
+.PP
+\fBSOLVER_MULTIVERSION\fR
+.RS 4
+Mark the matched packages for multiversion install\&. If they get to be installed because of some other job, the installation will keep the old version of the package installed (for rpm this is done by using \(lq\-i\(rq instead of \(lq\-U\(rq)\&.
+.RE
+.PP
+\fBSOLVER_LOCK\fR
+.RS 4
+Do not change the state of the matched packages, i\&.e\&. when they are installed they stay installed, if not they are not selected for installation\&.
+.RE
+.PP
+\fBSOLVER_DISTUPGRADE\fR
+.RS 4
+Update the matching installed packages to the best version included in one of the repositories\&. After this operation, all come from one of the available repositories except orphaned packages\&. Orphaned packages are packages that have no relation to the packages in the repositories, i\&.e\&. no package in the repositories have the same name or obsolete the orphaned package\&. This action brings the installed packages in sync with the ones in the repository\&. By default it also turns of arch/vendor/version locking for the affected packages to simulate a fresh installation\&. This means that distupgrade can actually downgrade packages if only lower versions of a package are available in the repositories\&. You can tweak this behavior with the SOLVER_FLAG_DUP_ solver flags\&.
+.RE
+.PP
+\fBSOLVER_DROP_ORPHANED\fR
+.RS 4
+Erase all the matching installed packages if they are orphaned\&. This only makes sense if there is a \(lqdistupgrade all packages\(rq job\&. The default is to erase orphaned packages only if they block the installation of other packages\&.
+.RE
+.PP
+\fBSOLVER_VERIFY\fR
+.RS 4
+Fix dependency problems of matching installed packages\&. The default is to ignore dependency problems for installed packages\&.
+.RE
+.PP
+\fBSOLVER_USERINSTALLED\fR
+.RS 4
+The matching installed packages are considered to be installed by a user, thus not installed to fulfill some dependency\&. This is needed input for the calculation of unneeded packages for jobs that have the SOLVER_CLEANDEPS flag set\&.
+.RE
+.PP
+\fBSOLVER_ALLOWUNINSTALL\fR
+.RS 4
+Allow the solver to deinstall the matching installed packages if they get into the way of resolving a dependency\&. This is like the SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages\&.
+.RE
+.PP
+\fBSOLVER_JOBMASK\fR
+.RS 4
+A mask containing all the above action bits\&.
+.RE
+.sp
+Action modifier constants:
+.PP
+\fBSOLVER_WEAK\fR
+.RS 4
+Makes the job a weak job\&. The solver tries to fulfill weak jobs, but does not report a problem if it is not possible to do so\&.
+.RE
+.PP
+\fBSOLVER_ESSENTIAL\fR
+.RS 4
+Makes the job an essential job\&. If there is a problem with the job, the solver will not propose to remove the job as one solution (unless all other solutions are also to remove essential jobs)\&.
+.RE
+.PP
+\fBSOLVER_CLEANDEPS\fR
+.RS 4
+The solver will try to also erase all packages dragged in through dependencies when erasing the package\&. This needs SOLVER_USERINSTALLED jobs to maximize user satisfaction\&.
+.RE
+.PP
+\fBSOLVER_FORCEBEST\fR
+.RS 4
+Insist on the best package for install, update, and distupgrade jobs\&. If this flag is not used, the solver will use the second\-best package if the best package cannot be installed for some reason\&. When this flag is used, the solver will generate a problem instead\&.
+.RE
+.PP
+\fBSOLVER_TARGETED\fR
+.RS 4
+Forces targeted operation update and distupgrade jobs\&. See the section about targeted updates about more information\&.
+.RE
+.sp
+Set constants\&.
+.PP
+\fBSOLVER_SETEV\fR
+.RS 4
+The job specified the exact epoch and version of the package set\&.
+.RE
+.PP
+\fBSOLVER_SETEVR\fR
+.RS 4
+The job specified the exact epoch, version, and release of the package set\&.
+.RE
+.PP
+\fBSOLVER_SETARCH\fR
+.RS 4
+The job specified the exact architecture of the packages from the set\&.
+.RE
+.PP
+\fBSOLVER_SETVENDOR\fR
+.RS 4
+The job specified the exact vendor of the packages from the set\&.
+.RE
+.PP
+\fBSOLVER_SETREPO\fR
+.RS 4
+The job specified the exact repository of the packages from the set\&.
+.RE
+.PP
+\fBSOLVER_SETNAME\fR
+.RS 4
+The job specified the exact name of the packages from the set\&.
+.RE
+.PP
+\fBSOLVER_NOAUTOSET\fR
+.RS 4
+Turn of automatic set flag generation for SOLVER_SOLVABLE jobs\&.
+.RE
+.PP
+\fBSOLVER_SETMASK\fR
+.RS 4
+A mask containing all the above set bits\&.
+.RE
+.sp
+See the section about set bits for more information\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool;\fR /* read only */
+\fI$job\fR\fB\->{pool}\fR
+\fId\fR\fB\&.pool\fR
+\fId\fR\fB\&.pool\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to pool\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId how;\fR /* read/write */
+\fI$job\fR\fB\->{how}\fR
+\fId\fR\fB\&.how\fR
+\fId\fR\fB\&.how\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Union of the selection, action, action modifier, and set flags\&. The selection part describes the semantics of the \(lqwhat\(rq Id\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId what;\fR /* read/write */
+\fI$job\fR\fB\->{what}\fR
+\fId\fR\fB\&.what\fR
+\fId\fR\fB\&.what\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Id describing the set of packages, the meaning depends on the selection part of the \(lqhow\(rq attribute\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *solvables()\fR
+my \fI@solvables\fR \fB=\fR \fI$job\fR\fB\->solvables()\fR;
+\fIsolvables\fR \fB=\fR \fIjob\fR\fB\&.solvables()\fR
+\fIsolvables\fR \fB=\fR \fIjob\fR\fB\&.solvables()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the set of solvables of the job as an array of Solvable objects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool isemptyupdate()\fR;
+\fI$job\fR\fB\->isemptyupdate()\fR
+\fIjob\fR\fB\&.isemptyupdate()\fR
+\fIjob\fR\fB\&.isemptyupdate?\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convenience function to find out if the job describes an update job with no matching packages, i\&.e\&. a job that does nothing\&. Some package managers like \(lqzypper\(rq like to turn those jobs into install jobs, i\&.e\&. an update of a not\-installed package will result into the installation of the package\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$job\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIjob\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIjob\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the job\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<equality>\fR
+\fBif (\fR\fI$job1\fR \fB==\fR \fI$job2\fR\fB)\fR
+\fBif\fR \fIjob1\fR \fB==\fR \fIjob2\fR\fB:\fR
+\fBif\fR \fIjob1\fR \fB==\fR \fIjob2\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Two jobs are equal if they belong to the same pool and both the \(lqhow\(rq and the \(lqwhat\(rq attributes are the same\&.
+.SS "TARGETED UPDATES"
+.sp
+Libsolv has two modes for upgrades and distupgrade: targeted and untargeted\&. Untargeted mode means that the installed packages from the specified set will be updated to the best version\&. Targeted means that packages that can be updated to a package in the specified set will be updated to the best package of the set\&.
+.sp
+Here\(cqs an example to explain the subtle difference\&. Suppose that you have package A installed in version "1\&.1", "A\-1\&.2" is available in one of the repositories and there is also package "B" that obsoletes package A\&.
+.sp
+An untargeted update of "A" will update the installed "A\-1\&.1" to package "B", because that is the newest version (B obsoletes A and is thus newer)\&.
+.sp
+A targeted update of "A" will update "A\-1\&.1" to "A\-1\&.2", as the set of packages contains both "A\-1\&.1" and "A\-1\&.2", and "A\-1\&.2" is the newer one\&.
+.sp
+An untargeted update of "B" will do nothing, as "B" is not installed\&.
+.sp
+An targeted update of "B" will update "A\-1\&.1" to "B"\&.
+.sp
+Note that the default is to do "auto\-targeting", thus if the specified set of packages does not include an installed package, the solver will assume targeted operation even if SOLVER_TARGETED is not used\&.
+.sp
+This mostly matches the intent of the user, with one exception: In the example above, an update of "A\-1\&.2" will update "A\-1\&.1" to "A\-1\&.2" (targeted mode), but a second update of "A\-1\&.2" will suddenly update to "B", as untargeted mode is chosen because "A\-1\&.2" is now installed\&.
+.sp
+If you want to have full control over when targeting mode is chosen, turn off auto\-targeting with the SOLVER_FLAG_NO_AUTOTARGET solver option\&. In that case, all updates are considered to be untargeted unless they include the SOLVER_TARGETED flag\&.
+.SS "SET BITS"
+.sp
+Set bits specify which parts of the specified packages where specified by the user\&. It is used by the solver when checking if an operation is allowed or not\&. For example, the solver will normally not allow the downgrade of an installed package\&. But it will not report a problem if the SOLVER_SETEVR flag is used, as it then assumes that the user specified the exact version and thus knows what he is doing\&.
+.sp
+So if a package "screen\-1\-1" is installed for the x86_64 architecture and version "2\-1" is only available for the i586 architecture, installing package "screen\-2\&.1" will ask the user for confirmation because of the different architecture\&. When using the Selection class to create jobs the set bits are automatically added, e\&.g\&. selecting \(lqscreen\&.i586\(rq will automatically add SOLVER_SETARCH, and thus no problem will be reported\&.
+.SH "THE SOLVER CLASS"
+.sp
+Dependency solving is what this library is about\&. A solver object is needed for solving to store the result of the solver run\&. The solver object can be used multiple times for different jobs, reusing it allows the solver to re\-use the dependency rules it already computed\&.
+.SS "CONSTANTS"
+.sp
+Flags to modify some of the solver\(cqs behavior:
+.PP
+\fBSOLVER_FLAG_ALLOW_DOWNGRADE\fR
+.RS 4
+Allow the solver to downgrade packages without asking for confirmation (i\&.e\&. reporting a problem)\&.
+.RE
+.PP
+\fBSOLVER_FLAG_ALLOW_ARCHCHANGE\fR
+.RS 4
+Allow the solver to change the architecture of an installed package without asking for confirmation\&. Note that changes to/from noarch are always considered to be allowed\&.
+.RE
+.PP
+\fBSOLVER_FLAG_ALLOW_VENDORCHANGE\fR
+.RS 4
+Allow the solver to change the vendor of an installed package without asking for confirmation\&. Each vendor is part of one or more vendor equivalence classes, normally installed packages may only change their vendor if the new vendor shares at least one equivalence class\&.
+.RE
+.PP
+\fBSOLVER_FLAG_ALLOW_NAMECHANGE\fR
+.RS 4
+Allow the solver to change the name of an installed package, i\&.e\&. install a package with a different name that obsoletes the installed package\&. This option is on by default\&.
+.RE
+.PP
+\fBSOLVER_FLAG_ALLOW_UNINSTALL\fR
+.RS 4
+Allow the solver to erase installed packages to fulfill the jobs\&. This flag also includes the above flags\&. You may want to set this flag if you only have SOLVER_ERASE jobs, as in that case it\(cqs better for the user to check the transaction overview instead of approving every single package that needs to be erased\&.
+.RE
+.PP
+\fBSOLVER_FLAG_DUP_ALLOW_DOWNGRADE\fR
+.RS 4
+Like SOLVER_FLAG_ALLOW_DOWNGRADE, but used in distupgrade mode\&.
+.RE
+.PP
+\fBSOLVER_FLAG_DUP_ALLOW_ARCHCHANGE\fR
+.RS 4
+Like SOLVER_FLAG_ALLOW_ARCHCHANGE, but used in distupgrade mode\&.
+.RE
+.PP
+\fBSOLVER_FLAG_DUP_ALLOW_VENDORCHANGE\fR
+.RS 4
+Like SOLVER_FLAG_ALLOW_VENDORCHANGE, but used in distupgrade mode\&.
+.RE
+.PP
+\fBSOLVER_FLAG_DUP_ALLOW_NAMECHANGE\fR
+.RS 4
+Like SOLVER_FLAG_ALLOW_NAMECHANGE, but used in distupgrade mode\&.
+.RE
+.PP
+\fBSOLVER_FLAG_NO_UPDATEPROVIDE\fR
+.RS 4
+If multiple packages obsolete an installed package, the solver checks the provides of every such package and ignores all packages that do not provide the installed package name\&. Thus, you can have an official update candidate that provides the old name, and other packages that also obsolete the package but are not considered for updating\&. If you cannot use this feature, you can turn it off by setting this flag\&.
+.RE
+.PP
+\fBSOLVER_FLAG_SPLITPROVIDES\fR
+.RS 4
+Make the solver aware of special provides of the form \(lq<packagename>:<path>\(rq used in SUSE systems to support package splits\&.
+.RE
+.PP
+\fBSOLVER_FLAG_IGNORE_RECOMMENDED\fR
+.RS 4
+Do not process optional (aka weak) dependencies\&.
+.RE
+.PP
+\fBSOLVER_FLAG_ADD_ALREADY_RECOMMENDED\fR
+.RS 4
+Install recommended or supplemented packages even if they have no connection to the current transaction\&. You can use this feature to implement a simple way for the user to install new recommended packages that were not available in the past\&.
+.RE
+.PP
+\fBSOLVER_FLAG_NO_INFARCHCHECK\fR
+.RS 4
+Turn off the inferior architecture checking that is normally done by the solver\&. Normally, the solver allows only the installation of packages from the "best" architecture if a package is available for multiple architectures\&.
+.RE
+.PP
+\fBSOLVER_FLAG_BEST_OBEY_POLICY\fR
+.RS 4
+Make the SOLVER_FORCEBEST job option consider only packages that meet the policies for installed packages, i\&.e\&. no downgrades, no architecture change, no vendor change (see the first flags of this section)\&. If the flag is not specified, the solver will enforce the installation of the best package ignoring the installed packages, which may conflict with the set policy\&.
+.RE
+.PP
+\fBSOLVER_FLAG_NO_AUTOTARGET\fR
+.RS 4
+Do not enable auto\-targeting up update and distupgrade jobs\&. See the section on targeted updates for more information\&.
+.RE
+.PP
+\fBSOLVER_FLAG_KEEP_ORPHANS\fR
+.RS 4
+Do not allow orphaned packages to be deinstalled if they get in the way of resolving other packages\&.
+.RE
+.PP
+\fBSOLVER_FLAG_BREAK_ORPHANS\fR
+.RS 4
+Ignore dependencies of orphaned packages that get in the way of resolving non\-orphaned ones\&. Setting the flag might result in no longer working packages in case they are orphaned\&.
+.RE
+.PP
+\fBSOLVER_FLAG_FOCUS_INSTALLED\fR
+.RS 4
+Resolve installed packages before resolving the given job\&. Setting this flag means that the solver will prefer picking a package version that fits the other installed packages over updating installed packages\&.
+.RE
+.sp
+Basic rule types:
+.PP
+\fBSOLVER_RULE_UNKNOWN\fR
+.RS 4
+A rule of an unknown class\&. You should never encounter those\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG\fR
+.RS 4
+A package dependency rule\&.
+.RE
+.PP
+\fBSOLVER_RULE_UPDATE\fR
+.RS 4
+A rule to implement the update policy of installed packages\&. Every installed package has an update rule that consists of the packages that may replace the installed package\&.
+.RE
+.PP
+\fBSOLVER_RULE_FEATURE\fR
+.RS 4
+Feature rules are fallback rules used when an update rule is disabled\&. They include all packages that may replace the installed package ignoring the update policy, i\&.e\&. they contain downgrades, arch changes and so on\&. Without them, the solver would simply erase installed packages if their update rule gets disabled\&.
+.RE
+.PP
+\fBSOLVER_RULE_JOB\fR
+.RS 4
+Job rules implement the job given to the solver\&.
+.RE
+.PP
+\fBSOLVER_RULE_DISTUPGRADE\fR
+.RS 4
+These are simple negative assertions that make sure that only packages are kept that are also available in one of the repositories\&.
+.RE
+.PP
+\fBSOLVER_RULE_INFARCH\fR
+.RS 4
+Infarch rules are also negative assertions, they disallow the installation of packages when there are packages of the same name but with a better architecture\&.
+.RE
+.PP
+\fBSOLVER_RULE_CHOICE\fR
+.RS 4
+Choice rules are used to make sure that the solver prefers updating to installing different packages when some dependency is provided by multiple packages with different names\&. The solver may always break choice rules, so you will not see them when a problem is found\&.
+.RE
+.PP
+\fBSOLVER_RULE_LEARNT\fR
+.RS 4
+These rules are generated by the solver to keep it from running into the same problem multiple times when it has to backtrack\&. They are the main reason why a sat solver is faster than other dependency solver implementations\&.
+.RE
+.sp
+Special dependency rule types:
+.PP
+\fBSOLVER_RULE_PKG_NOT_INSTALLABLE\fR
+.RS 4
+This rule was added to prevent the installation of a package of an architecture that does not work on the system\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_NOTHING_PROVIDES_DEP\fR
+.RS 4
+The package contains a required dependency which was not provided by any package\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_REQUIRES\fR
+.RS 4
+Similar to SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP, but in this case some packages provided the dependency but none of them could be installed due to other dependency issues\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_SELF_CONFLICT\fR
+.RS 4
+The package conflicts with itself\&. This is not allowed by older rpm versions\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_CONFLICTS\fR
+.RS 4
+To fulfill the dependencies two packages need to be installed, but one of the packages contains a conflict with the other one\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_SAME_NAME\fR
+.RS 4
+The dependencies can only be fulfilled by multiple versions of a package, but installing multiple versions of the same package is not allowed\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_OBSOLETES\fR
+.RS 4
+To fulfill the dependencies two packages need to be installed, but one of the packages obsoletes the other one\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_IMPLICIT_OBSOLETES\fR
+.RS 4
+To fulfill the dependencies two packages need to be installed, but one of the packages has provides a dependency that is obsoleted by the other one\&. See the POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES flag\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_INSTALLED_OBSOLETES\fR
+.RS 4
+To fulfill the dependencies a package needs to be installed that is obsoleted by an installed package\&. See the POOL_FLAG_NOINSTALLEDOBSOLETES flag\&.
+.RE
+.PP
+\fBSOLVER_RULE_JOB_NOTHING_PROVIDES_DEP\fR
+.RS 4
+The user asked for installation of a package providing a specific dependency, but no available package provides it\&.
+.RE
+.PP
+\fBSOLVER_RULE_JOB_UNKNOWN_PACKAGE\fR
+.RS 4
+The user asked for installation of a package with a specific name, but no available package has that name\&.
+.RE
+.PP
+\fBSOLVER_RULE_JOB_PROVIDED_BY_SYSTEM\fR
+.RS 4
+The user asked for the erasure of a dependency that is provided by the system (i\&.e\&. for special hardware or language dependencies), this cannot be done with a job\&.
+.RE
+.PP
+\fBSOLVER_RULE_JOB_UNSUPPORTED\fR
+.RS 4
+The user asked for something that is not yet implemented, e\&.g\&. the installation of all packages at once\&.
+.RE
+.sp
+Policy error constants
+.PP
+\fBPOLICY_ILLEGAL_DOWNGRADE\fR
+.RS 4
+The solver ask for permission before downgrading packages\&.
+.RE
+.PP
+\fBPOLICY_ILLEGAL_ARCHCHANGE\fR
+.RS 4
+The solver ask for permission before changing the architecture of installed packages\&.
+.RE
+.PP
+\fBPOLICY_ILLEGAL_VENDORCHANGE\fR
+.RS 4
+The solver ask for permission before changing the vendor of installed packages\&.
+.RE
+.PP
+\fBPOLICY_ILLEGAL_NAMECHANGE\fR
+.RS 4
+The solver ask for permission before replacing an installed packages with a package that has a different name\&.
+.RE
+.sp
+Solution element type constants
+.PP
+\fBSOLVER_SOLUTION_JOB\fR
+.RS 4
+The problem can be solved by removing the specified job\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_POOLJOB\fR
+.RS 4
+The problem can be solved by removing the specified job that is defined in the pool\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_INFARCH\fR
+.RS 4
+The problem can be solved by allowing the installation of the specified package with an inferior architecture\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_DISTUPGRADE\fR
+.RS 4
+The problem can be solved by allowing to keep the specified package installed\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_BEST\fR
+.RS 4
+The problem can be solved by allowing to install the specified package that is not the best available package\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_ERASE\fR
+.RS 4
+The problem can be solved by allowing to erase the specified package\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_REPLACE\fR
+.RS 4
+The problem can be solved by allowing to replace the package with some other package\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_REPLACE_DOWNGRADE\fR
+.RS 4
+The problem can be solved by allowing to replace the package with some other package that has a lower version\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_REPLACE_ARCHCHANGE\fR
+.RS 4
+The problem can be solved by allowing to replace the package with some other package that has a different architecture\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_REPLACE_VENDORCHANGE\fR
+.RS 4
+The problem can be solved by allowing to replace the package with some other package that has a different vendor\&.
+.RE
+.PP
+\fBSOLVER_SOLUTION_REPLACE_NAMECHANGE\fR
+.RS 4
+The problem can be solved by allowing to replace the package with some other package that has a different name\&.
+.RE
+.sp
+Reason constants
+.PP
+\fBSOLVER_REASON_UNRELATED\fR
+.RS 4
+The package status did not change as it was not related to any job\&.
+.RE
+.PP
+\fBSOLVER_REASON_UNIT_RULE\fR
+.RS 4
+The package was installed/erased/kept because of a unit rule, i\&.e\&. a rule where all literals but one were false\&.
+.RE
+.PP
+\fBSOLVER_REASON_KEEP_INSTALLED\fR
+.RS 4
+The package was chosen when trying to keep as many packages installed as possible\&.
+.RE
+.PP
+\fBSOLVER_REASON_RESOLVE_JOB\fR
+.RS 4
+The decision happened to fulfill a job rule\&.
+.RE
+.PP
+\fBSOLVER_REASON_UPDATE_INSTALLED\fR
+.RS 4
+The decision happened to fulfill a package update request\&.
+.RE
+.PP
+\fBSOLVER_REASON_CLEANDEPS_ERASE\fR
+.RS 4
+The package was erased when cleaning up dependencies from other erased packages\&.
+.RE
+.PP
+\fBSOLVER_REASON_RESOLVE\fR
+.RS 4
+The package was installed to fulfill package dependencies\&.
+.RE
+.PP
+\fBSOLVER_REASON_WEAKDEP\fR
+.RS 4
+The package was installed because of a weak dependency (Recommends or Supplements)\&.
+.RE
+.PP
+\fBSOLVER_REASON_RESOLVE_ORPHAN\fR
+.RS 4
+The decision about the package was made when deciding the fate of orphaned packages\&.
+.RE
+.PP
+\fBSOLVER_REASON_RECOMMENDED\fR
+.RS 4
+This is a special case of SOLVER_REASON_WEAKDEP\&.
+.RE
+.PP
+\fBSOLVER_REASON_SUPPLEMENTED\fR
+.RS 4
+This is a special case of SOLVER_REASON_WEAKDEP\&.
+.RE
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool;\fR /* read only */
+\fI$job\fR\fB\->{pool}\fR
+\fId\fR\fB\&.pool\fR
+\fId\fR\fB\&.pool\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to pool\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint set_flag(int\fR \fIflag\fR\fB, int\fR \fIvalue\fR\fB)\fR
+my \fI$oldvalue\fR \fB=\fR \fI$solver\fR\fB\->set_flag(\fR\fI$flag\fR\fB,\fR \fI$value\fR\fB)\fR;
+\fIoldvalue\fR \fB=\fR \fIsolver\fR\fB\&.set_flag(\fR\fIflag\fR\fB,\fR \fIvalue\fR\fB)\fR
+\fIoldvalue\fR \fB=\fR \fIsolver\fR\fB\&.set_flag(\fR\fIflag\fR\fB,\fR \fIvalue\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint get_flag(int\fR \fIflag\fR\fB)\fR
+my \fI$value\fR \fB=\fR \fI$solver\fR\fB\->get_flag(\fR\fI$flag\fR\fB)\fR;
+\fIvalue\fR \fB=\fR \fIsolver\fR\fB\&.get_flag(\fR\fIflag\fR\fB)\fR
+\fIvalue\fR \fB=\fR \fIsolver\fR\fB\&.get_flag(\fR\fIflag\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set/get a solver specific flag\&. The flags define the policies the solver has to obey\&. The flags are explained in the CONSTANTS section of this class\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBProblem *solve(Job *\fR\fIjobs\fR\fB)\fR
+my \fI@problems\fR \fB=\fR \fI$solver\fR\fB\->solve(\e\fR\fI@jobs\fR\fB)\fR;
+\fIproblems\fR \fB=\fR \fIsolver\fR\fB\&.solve(\fR\fIjobs\fR\fB)\fR
+\fIproblems\fR \fB=\fR \fIsolver\fR\fB\&.solve(\fR\fIjobs\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Solve a problem specified in the job list (plus the jobs defined in the pool)\&. Returns an array of problems that need user interaction, or an empty array if no problems were encountered\&. See the Problem class on how to deal with problems\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBTransaction transaction()\fR
+my \fI$trans\fR \fB=\fR \fI$solver\fR\fB\->transaction()\fR;
+\fItrans\fR \fB=\fR \fIsolver\fR\fB\&.transaction()\fR
+\fItrans\fR \fB=\fR \fIsolver\fR\fB\&.transaction()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the transaction to implement the calculated package changes\&. A transaction is available even if problems were found, this is useful for interactive user interfaces that show both the job result and the problems\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint\fR \fIreason\fR \fB= describe_decision(Solvable *\fR\fIs\fR\fB, Rule *\fR\fIOUTPUT\fR\fB)\fR
+my \fB(\fR\fI$reason\fR\fB,\fR \fI$rule\fR\fB) =\fR \fI$solver\fR\fB\->describe_decision(\fR\fI$solvable\fR\fB)\fR;
+\fB(\fR\fIreason\fR\fB,\fR \fIrule\fR\fB) =\fR \fIsolver\fR\fB\&.describe_decision(\fR\fIsolvable\fR\fB)\fR
+\fB(\fR\fIreason\fR\fB,\fR \fIrule\fR\fB) =\fR \fIsolver\fR\fB\&.describe_decision(\fR\fIsolvable\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the reason why a specific solvable was installed or erased\&. For most of the reasons the rule that triggered the decision is also returned\&.
+.SH "THE PROBLEM CLASS"
+.sp
+Problems are the way of the solver to interact with the user\&. You can simply list all problems and terminate your program, but a better way is to present solutions to the user and let him pick the ones he likes\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver *solv;\fR /* read only */
+\fI$problem\fR\fB\->{solv}\fR
+\fIproblem\fR\fB\&.solv\fR
+\fIproblem\fR\fB\&.solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id;\fR /* read only */
+\fI$problem\fR\fB\->{id}\fR
+\fIproblem\fR\fB\&.id\fR
+\fIproblem\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Id of the problem\&. The first problem has Id 1, they are numbered consecutively\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRule findproblemrule()\fR
+my \fI$probrule\fR \fB=\fR \fI$problem\fR\fB\->findproblemrule()\fR;
+\fIprobrule\fR \fB=\fR \fIproblem\fR\fB\&.findproblemrule()\fR
+\fIprobrule\fR \fB=\fR \fIproblem\fR\fB\&.findproblemrule()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the rule that caused the problem\&. Of course in most situations there is no single responsible rule, but many rules that interconnect with each created the problem\&. Nevertheless, the solver uses some heuristic approach to find a rule that somewhat describes the problem best to the user\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRule *findallproblemrules(bool\fR \fIunfiltered\fR \fB= 0)\fR
+my \fI@probrules\fR \fB=\fR \fI$problem\fR\fB\->findallproblemrules()\fR;
+\fIprobrules\fR \fB=\fR \fIproblem\fR\fB\&.findallproblemrule()\fR
+\fIprobrules\fR \fB=\fR \fIproblem\fR\fB\&.findallproblemrule()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return all rules responsible for the problem\&. The returned set of rules contains all the needed information why there was a problem, but it\(cqs hard to present them to the user in a sensible way\&. The default is to filter out all update and job rules (unless the returned rules only consist of those types)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolution *solutions()\fR
+my \fI@solutions\fR \fB=\fR \fI$problem\fR\fB\->solutions()\fR;
+\fIsolutions\fR \fB=\fR \fIproblem\fR\fB\&.solutions()\fR
+\fIsolutions\fR \fB=\fR \fIproblem\fR\fB\&.solutions()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return an array containing multiple possible solutions to fix the problem\&. See the solution class for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint solution_count()\fR
+my \fI$cnt\fR \fB=\fR \fI$problem\fR\fB\->solution_count()\fR;
+\fIcnt\fR \fB=\fR \fIproblem\fR\fB\&.solution_count()\fR
+\fIcnt\fR \fB=\fR \fIproblem\fR\fB\&.solution_count()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the number of solutions without creating solution objects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$problem\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIproblem\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIproblem\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the problem\&. This is a convenience function, it is a shorthand for calling findproblemrule(), then ruleinfo() on the problem rule and problemstr() on the ruleinfo object\&.
+.SH "THE RULE CLASS"
+.sp
+Rules are the basic block of sat solving\&. Each package dependency gets translated into one or multiple rules\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver *solv;\fR /* read only */
+\fI$rule\fR\fB\->{solv}\fR
+\fIrule\fR\fB\&.solv\fR
+\fIrule\fR\fB\&.solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id;\fR /* read only */
+\fI$rule\fR\fB\->{id}\fR
+\fIrule\fR\fB\&.id\fR
+\fIrule\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The id of the rule\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint type;\fR /* read only */
+\fI$rule\fR\fB\->{type}\fR
+\fIrule\fR\fB\&.type\fR
+\fIrule\fR\fB\&.type\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The basic type of the rule\&. See the constant section of the solver class for the type list\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRuleinfo info()\fR
+my \fI$ruleinfo\fR \fB=\fR \fI$rule\fR\fB\->info()\fR;
+\fIruleinfo\fR \fB=\fR \fIrule\fR\fB\&.info()\fR
+\fIruleinfo\fR \fB=\fR \fIrule\fR\fB\&.info()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a Ruleinfo object that contains information about why the rule was created\&. But see the allinfos() method below\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRuleinfo *allinfos()\fR
+my \fI@ruleinfos\fR \fB=\fR \fI$rule\fR\fB\->allinfos()\fR;
+\fIruleinfos\fR \fB=\fR \fIrule\fR\fB\&.allinfos()\fR
+\fIruleinfos\fR \fB=\fR \fIrule\fR\fB\&.allinfos()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+As the same dependency rule can get created because of multiple dependencies, one Ruleinfo is not enough to describe the reason\&. Thus the allinfos() method returns an array of all infos about a rule\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<equality>\fR
+\fBif (\fR\fI$rule1\fR \fB==\fR \fI$rule2\fR\fB)\fR
+\fBif\fR \fIrule1\fR \fB==\fR \fIrule2\fR\fB:\fR
+\fBif\fR \fIrule1\fR \fB==\fR \fIrule2\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Two rules are equal if they belong to the same solver and have the same id\&.
+.SH "THE RULEINFO CLASS"
+.sp
+A Ruleinfo describes one reason why a rule was created\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver *solv;\fR /* read only */
+\fI$ruleinfo\fR\fB\->{solv}\fR
+\fIruleinfo\fR\fB\&.solv\fR
+\fIruleinfo\fR\fB\&.solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint type;\fR /* read only */
+\fI$ruleinfo\fR\fB\->{type}\fR
+\fIruleinfo\fR\fB\&.type\fR
+\fIruleinfo\fR\fB\&.type\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The type of the ruleinfo\&. See the constant section of the solver class for the rule type list and the special type list\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep *dep;\fR /* read only */
+\fI$ruleinfo\fR\fB\->{dep}\fR
+\fIruleinfo\fR\fB\&.dep\fR
+\fIruleinfo\fR\fB\&.dep\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The dependency leading to the creation of the rule\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep *dep_id;\fR /* read only */
+\fI$ruleinfo\fR\fB\->{\*(Aqdep_id\*(Aq}\fR
+\fIruleinfo\fR\fB\&.dep_id\fR
+\fIruleinfo\fR\fB\&.dep_id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The Id of the dependency leading to the creation of the rule, or zero\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *solvable;\fR /* read only */
+\fI$ruleinfo\fR\fB\->{solvable}\fR
+\fIruleinfo\fR\fB\&.solvable\fR
+\fIruleinfo\fR\fB\&.solvable\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The involved Solvable, e\&.g\&. the one containing the dependency\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *othersolvable;\fR /* read only */
+\fI$ruleinfo\fR\fB\->{othersolvable}\fR
+\fIruleinfo\fR\fB\&.othersolvable\fR
+\fIruleinfo\fR\fB\&.othersolvable\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The other involved Solvable (if any), e\&.g\&. the one containing providing the dependency for conflicts\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *problemstr()\fR;
+my \fI$str\fR \fB=\fR \fI$ruleinfo\fR\fB\->problemstr()\fR;
+\fIstr\fR \fB=\fR \fIruleinfo\fR\fB\&.problemstr()\fR
+\fIstr\fR \fB=\fR \fIruleinfo\fR\fB\&.problemstr()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+A string describing the ruleinfo from a problem perspective\&. This probably only makes sense if the rule is part of a problem\&.
+.SH "THE SOLUTION CLASS"
+.sp
+A solution solves one specific problem\&. It consists of multiple solution elements that all need to be executed\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver *solv;\fR /* read only */
+\fI$solution\fR\fB\->{solv}\fR
+\fIsolution\fR\fB\&.solv\fR
+\fIsolution\fR\fB\&.solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId problemid;\fR /* read only */
+\fI$solution\fR\fB\->{problemid}\fR
+\fIsolution\fR\fB\&.problemid\fR
+\fIsolution\fR\fB\&.problemid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Id of the problem the solution solves\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id;\fR /* read only */
+\fI$solution\fR\fB\->{id}\fR
+\fIsolution\fR\fB\&.id\fR
+\fIsolution\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Id of the solution\&. The first solution has Id 1, they are numbered consecutively\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolutionelement *elements(bool\fR \fIexpandreplaces\fR \fB= 0)\fR
+my \fI@solutionelements\fR \fB=\fR \fI$solution\fR\fB\->elements()\fR;
+\fIsolutionelements\fR \fB=\fR \fIsolution\fR\fB\&.elements()\fR
+\fIsolutionelements\fR \fB=\fR \fIsolution\fR\fB\&.elements()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return an array containing the elements describing what needs to be done to implement the specific solution\&. If expandreplaces is true, elements of type SOLVER_SOLUTION_REPLACE will be replaced by one or more elements replace elements describing the policy mismatches\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint element_count()\fR
+my \fI$cnt\fR \fB=\fR \fI$solution\fR\fB\->solution_count()\fR;
+\fIcnt\fR \fB=\fR \fIsolution\fR\fB\&.element_count()\fR
+\fIcnt\fR \fB=\fR \fIsolution\fR\fB\&.element_count()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the number of solution elements without creating objects\&. Note that the count does not match the number of objects returned by the elements() method of expandreplaces is set to true\&.
+.SH "THE SOLUTIONELEMENT CLASS"
+.sp
+A solution element describes a single action of a solution\&. The action is always either to remove one specific job or to add a new job that installs or erases a single specific package\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver *solv;\fR /* read only */
+\fI$solutionelement\fR\fB\->{solv}\fR
+\fIsolutionelement\fR\fB\&.solv\fR
+\fIsolutionelement\fR\fB\&.solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId problemid;\fR /* read only */
+\fI$solutionelement\fR\fB\->{problemid}\fR
+\fIsolutionelement\fR\fB\&.problemid\fR
+\fIsolutionelement\fR\fB\&.problemid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Id of the problem the element (partly) solves\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId solutionid;\fR /* read only */
+\fI$solutionelement\fR\fB\->{solutionid}\fR
+\fIsolutionelement\fR\fB\&.solutionid\fR
+\fIsolutionelement\fR\fB\&.solutionid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Id of the solution the element is a part of\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id;\fR /* read only */
+\fI$solutionelement\fR\fB\->{id}\fR
+\fIsolutionelement\fR\fB\&.id\fR
+\fIsolutionelement\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Id of the solution element\&. The first element has Id 1, they are numbered consecutively\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId type;\fR /* read only */
+\fI$solutionelement\fR\fB\->{type}\fR
+\fIsolutionelement\fR\fB\&.type\fR
+\fIsolutionelement\fR\fB\&.type\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Type of the solution element\&. See the constant section of the solver class for the existing types\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *solvable;\fR /* read only */
+\fI$solutionelement\fR\fB\->{solvable}\fR
+\fIsolutionelement\fR\fB\&.solvable\fR
+\fIsolutionelement\fR\fB\&.solvable\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The installed solvable that needs to be replaced for replacement elements\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *replacement;\fR /* read only */
+\fI$solutionelement\fR\fB\->{replacement}\fR
+\fIsolutionelement\fR\fB\&.replacement\fR
+\fIsolutionelement\fR\fB\&.replacement\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The solvable that needs to be installed to fix the problem\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint jobidx;\fR /* read only */
+\fI$solutionelement\fR\fB\->{jobidx}\fR
+\fIsolutionelement\fR\fB\&.jobidx\fR
+\fIsolutionelement\fR\fB\&.jobidx\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The index of the job that needs to be removed to fix the problem, or \-1 if the element is of another type\&. Note that it\(cqs better to change the job to SOLVER_NOOP type so that the numbering of other elements does not get disturbed\&. This method works both for types SOLVER_SOLUTION_JOB and SOLVER_SOLUTION_POOLJOB\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolutionelement *replaceelements()\fR
+my \fI@solutionelements\fR \fB=\fR \fI$solutionelement\fR\fB\->replaceelements()\fR;
+\fIsolutionelements\fR \fB=\fR \fIsolutionelement\fR\fB\&.replaceelements()\fR
+\fIsolutionelements\fR \fB=\fR \fIsolutionelement\fR\fB\&.replaceelements()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+If the solution element is of type SOLVER_SOLUTION_REPLACE, return an array of elements describing the policy mismatches, otherwise return a copy of the element\&. See also the \(lqexpandreplaces\(rq option in the solution\(cqs elements() method\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint illegalreplace()\fR
+my \fI$illegal\fR \fB=\fR \fI$solutionelement\fR\fB\->illegalreplace()\fR;
+\fIillegal\fR \fB=\fR \fIsolutionelement\fR\fB\&.illegalreplace()\fR
+\fIillegal\fR \fB=\fR \fIsolutionelement\fR\fB\&.illegalreplace()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return an integer that contains the policy mismatch bits or\-ed together, or zero if there was no policy mismatch\&. See the policy error constants in the solver class\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBJob Job()\fR
+my \fI$job\fR \fB=\fR \fI$solutionelement\fR\fB\->Job()\fR;
+\fIillegal\fR \fB=\fR \fIsolutionelement\fR\fB\&.Job()\fR
+\fIillegal\fR \fB=\fR \fIsolutionelement\fR\fB\&.Job()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a job that implements the solution element\&. Add this job to the array of jobs for all elements of type different to SOLVER_SOLUTION_JOB and SOLVER_SOLUTION_POOLJOB\&. For the later two, a SOLVER_NOOB Job is created, you should replace the old job with the new one\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *str()\fR
+my \fI$str\fR \fB=\fR \fI$solutionelement\fR\fB\->str()\fR;
+\fIstr\fR \fB=\fR \fIsolutionelement\fR\fB\&.str()\fR
+\fIstr\fR \fB=\fR \fIsolutionelement\fR\fB\&.str()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+A string describing the change the solution element consists of\&.
+.SH "THE TRANSACTION CLASS"
+.sp
+Transactions describe the output of a solver run\&. A transaction contains a number of transaction elements, each either the installation of a new package or the removal of an already installed package\&. The Transaction class supports a classify() method that puts the elements into different groups so that a transaction can be presented to the user in a meaningful way\&.
+.SS "CONSTANTS"
+.sp
+Transaction element types, both active and passive
+.PP
+\fBSOLVER_TRANSACTION_IGNORE\fR
+.RS 4
+This element does nothing\&. Used to map element types that do not match the view mode\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_INSTALL\fR
+.RS 4
+This element installs a package\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_ERASE\fR
+.RS 4
+This element erases a package\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_MULTIINSTALL\fR
+.RS 4
+This element installs a package with a different version keeping the other versions installed\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_MULTIREINSTALL\fR
+.RS 4
+This element reinstalls an installed package keeping the other versions installed\&.
+.RE
+.sp
+Transaction element types, active view
+.PP
+\fBSOLVER_TRANSACTION_REINSTALL\fR
+.RS 4
+This element re\-installs a package, i\&.e\&. installs the same package again\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_CHANGE\fR
+.RS 4
+This element installs a package with same name, version, architecture but different content\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_UPGRADE\fR
+.RS 4
+This element installs a newer version of an installed package\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_DOWNGRADE\fR
+.RS 4
+This element installs an older version of an installed package\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_OBSOLETES\fR
+.RS 4
+This element installs a package that obsoletes an installed package\&.
+.RE
+.sp
+Transaction element types, passive view
+.PP
+\fBSOLVER_TRANSACTION_REINSTALLED\fR
+.RS 4
+This element re\-installs a package, i\&.e\&. installs the same package again\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_CHANGED\fR
+.RS 4
+This element replaces an installed package with one of the same name, version, architecture but different content\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_UPGRADED\fR
+.RS 4
+This element replaces an installed package with a new version\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_DOWNGRADED\fR
+.RS 4
+This element replaces an installed package with an old version\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_OBSOLETED\fR
+.RS 4
+This element replaces an installed package with a package that obsoletes it\&.
+.RE
+.sp
+Pseudo element types for showing extra information used by classify()
+.PP
+\fBSOLVER_TRANSACTION_ARCHCHANGE\fR
+.RS 4
+This element replaces an installed package with a package of a different architecture\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_VENDORCHANGE\fR
+.RS 4
+This element replaces an installed package with a package of a different vendor\&.
+.RE
+.sp
+Transaction mode flags
+.PP
+\fBSOLVER_TRANSACTION_SHOW_ACTIVE\fR
+.RS 4
+Filter for active view types\&. The default is to return passive view type, i\&.e\&. to show how the installed packages get changed\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_SHOW_OBSOLETES\fR
+.RS 4
+Do not map the obsolete view type into INSTALL/ERASE elements\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_SHOW_ALL\fR
+.RS 4
+If multiple packages replace an installed package, only the best of them is kept as OBSOLETE element, the other ones are mapped to INSTALL/ERASE elements\&. This is because most applications want to show just one package replacing the installed one\&. The SOLVER_TRANSACTION_SHOW_ALL makes the library keep all OBSOLETE elements\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_SHOW_MULTIINSTALL\fR
+.RS 4
+The library maps MULTIINSTALL elements to simple INSTALL elements\&. This flag can be used to disable the mapping\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_CHANGE_IS_REINSTALL\fR
+.RS 4
+Use this flag if you want to map CHANGE elements to the REINSTALL type\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE\fR
+.RS 4
+Use this flag if you want to map OBSOLETE elements to the UPGRADE type\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_MERGE_ARCHCHANGES\fR
+.RS 4
+Do not add extra categories for every architecture change, instead cumulate them in one category\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_MERGE_VENDORCHANGES\fR
+.RS 4
+Do not add extra categories for every vendor change, instead cumulate them in one category\&.
+.RE
+.PP
+\fBSOLVER_TRANSACTION_RPM_ONLY\fR
+.RS 4
+Special view mode that just returns IGNORE, ERASE, INSTALL, MULTIINSTALL elements\&. Useful if you want to find out what to feed to the underlying package manager\&.
+.RE
+.sp
+Transaction order flags
+.PP
+\fBSOLVER_TRANSACTION_KEEP_ORDERDATA\fR
+.RS 4
+Do not throw away the dependency graph used for ordering the transaction\&. This flag is needed if you want to do manual ordering\&.
+.RE
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool;\fR /* read only */
+\fI$trans\fR\fB\->{pool}\fR
+\fItrans\fR\fB\&.pool\fR
+\fItrans\fR\fB\&.pool\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to pool\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool isempty()\fR;
+\fI$trans\fR\fB\->isempty()\fR
+\fItrans\fR\fB\&.isempty()\fR
+\fItrans\fR\fB\&.isempty?\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Returns true if the transaction does not do anything, i\&.e\&. has no elements\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *newsolvables()\fR;
+my \fI@newsolvables\fR \fB=\fR \fI$trans\fR\fB\->newsolvables()\fR;
+\fInewsolvables\fR \fB=\fR \fItrans\fR\fB\&.newsolvables()\fR
+\fInewsolvables\fR \fB=\fR \fItrans\fR\fB\&.newsolvables()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return all packages that are to be installed by the transaction\&. These are the packages that need to be downloaded from the repositories\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *keptsolvables()\fR;
+my \fI@keptsolvables\fR \fB=\fR \fI$trans\fR\fB\->keptsolvables()\fR;
+\fIkeptsolvables\fR \fB=\fR \fItrans\fR\fB\&.keptsolvables()\fR
+\fIkeptsolvables\fR \fB=\fR \fItrans\fR\fB\&.keptsolvables()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return all installed packages that the transaction will keep installed\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *steps()\fR;
+my \fI@steps\fR \fB=\fR \fI$trans\fR\fB\->steps()\fR;
+\fIsteps\fR \fB=\fR \fItrans\fR\fB\&.steps()\fR
+\fIsteps\fR \fB=\fR \fItrans\fR\fB\&.steps()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return all solvables that need to be installed (if the returned solvable is not already installed) or erased (if the returned solvable is installed)\&. A step is also called a transaction element\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint steptype(Solvable *\fR\fIsolvable\fR\fB, int\fR \fImode\fR\fB)\fR
+my \fI$type\fR \fB=\fR \fI$trans\fR\fB\->steptype(\fR\fI$solvable\fR\fB,\fR \fI$mode\fR\fB)\fR;
+\fItype\fR \fB=\fR \fItrans\fR\fB\&.steptype(\fR\fIsolvable\fR\fB,\fR \fImode\fR\fB)\fR
+\fItype\fR \fB=\fR \fItrans\fR\fB\&.steptype(\fR\fIsolvable\fR\fB,\fR \fImode\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the transaction type of the specified solvable\&. See the CONSTANTS sections for the mode argument flags and the list of returned types\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBTransactionClass *classify(int\fR \fImode\fR \fB= 0)\fR
+my \fI@classes\fR \fB=\fR \fI$trans\fR\fB\->classify()\fR;
+\fIclasses\fR \fB=\fR \fItrans\fR\fB\&.classify()\fR
+\fIclasses\fR \fB=\fR \fItrans\fR\fB\&.classify()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Group the transaction elements into classes so that they can be displayed in a structured way\&. You can use various mapping mode flags to tweak the result to match your preferences, see the mode argument flag in the CONSTANTS section\&. See the TransactionClass class for how to deal with the returned objects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable othersolvable(Solvable *\fR\fIsolvable\fR\fB)\fR;
+my \fI$other\fR \fB=\fR \fI$trans\fR\fB\->othersolvable(\fR\fI$solvable\fR\fB)\fR;
+\fIother\fR \fB=\fR \fItrans\fR\fB\&.othersolvable(\fR\fIsolvable\fR\fB)\fR
+\fIother\fR \fB=\fR \fItrans\fR\fB\&.othersolvable(\fR\fIsolvable\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the \(lqother\(rq solvable for a given solvable\&. For installed packages the other solvable is the best package with the same name that replaces the installed package, or the best package of the obsoleting packages if the package does not get replaced by one with the same name\&.
+.sp
+For to be installed packages, the \(lqother\(rq solvable is the best installed package with the same name that will be replaced, or the best packages of all the packages that are obsoleted if the new package does not replace a package with the same name\&.
+.sp
+Thus, the \(lqother\(rq solvable is normally the package that is also shown for a given package\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *allothersolvables(Solvable *\fR\fIsolvable\fR\fB)\fR;
+my \fI@others\fR \fB=\fR \fI$trans\fR\fB\->allothersolvables(\fR\fI$solvable\fR\fB)\fR;
+\fIothers\fR \fB=\fR \fItrans\fR\fB\&.allothersolvables(\fR\fIsolvable\fR\fB)\fR
+\fIothers\fR \fB=\fR \fItrans\fR\fB\&.allothersolvables(\fR\fIsolvable\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+For installed packages, returns all of the packages that replace us\&. For to be installed packages, returns all of the packages that the new package replaces\&. The special \(lqother\(rq solvable is always the first entry of the returned array\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint calc_installsizechange()\fR;
+my \fI$change\fR \fB=\fR \fI$trans\fR\fB\->calc_installsizechange()\fR;
+\fIchange\fR \fB=\fR \fItrans\fR\fB\&.calc_installsizechange()\fR
+\fIchange\fR \fB=\fR \fItrans\fR\fB\&.calc_installsizechange()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the size change of the installed system in kilobytes (kibibytes)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid order(int\fR \fIflags\fR \fB= 0)\fR;
+\fI$trans\fR\fB\->order()\fR;
+\fItrans\fR\fB\&.order()\fR
+\fItrans\fR\fB\&.order()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Order the steps in the transactions so that dependent packages are updated before packages that depend on them\&. For rpm, you can also use rpmlib\(cqs ordering functionality, debian\(cqs dpkg does not provide a way to order a transaction\&.
+.SS "ACTIVE/PASSIVE VIEW"
+.sp
+Active view lists what new packages get installed, while passive view shows what happens to the installed packages\&. Most often there\(cqs not much difference between the two modes, but things get interesting if multiple packages get replaced by one new package\&. Say you have installed packages A\-1\-1 and B\-1\-1, and now install A\-2\-1 which has a new dependency that obsoletes B\&. The transaction elements will be
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+updated A\-1\-1 (other: A\-2\-1)
+obsoleted B\-1\-1 (other: A\-2\-1)
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+in passive mode, but
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+update A\-2\-1 (other: A\-1\-1)
+erase B
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+in active mode\&. If the mode contains SOLVER_TRANSACTION_SHOW_ALL, the passive mode list will be unchanged but the active mode list will just contain A\-2\-1\&.
+.SH "THE TRANSACTIONCLASS CLASS"
+.sp
+Objects of this type are returned by the classify() Transaction method\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBTransaction *transaction;\fR /* read only */
+\fI$class\fR\fB\->{transaction}\fR
+\fIclass\fR\fB\&.transaction\fR
+\fIclass\fR\fB\&.transaction\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to transaction object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint type;\fR /* read only */
+\fI$class\fR\fB\->{type}\fR
+\fIclass\fR\fB\&.type\fR
+\fIclass\fR\fB\&.type\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The type of the transaction elements in the class\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint count;\fR /* read only */
+\fI$class\fR\fB\->{count}\fR
+\fIclass\fR\fB\&.count\fR
+\fIclass\fR\fB\&.count\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The number of elements in the class\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *\fR\fIfromstr\fR;
+\fI$class\fR\fB\->{fromstr}\fR
+\fIclass\fR\fB\&.fromstr\fR
+\fIclass\fR\fB\&.fromstr\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The old vendor or architecture\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *\fR\fItostr\fR;
+\fI$class\fR\fB\->{tostr}\fR
+\fIclass\fR\fB\&.tostr\fR
+\fIclass\fR\fB\&.tostr\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The new vendor or architecture\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId\fR \fIfromid\fR;
+\fI$class\fR\fB\->{fromid}\fR
+\fIclass\fR\fB\&.fromid\fR
+\fIclass\fR\fB\&.fromid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The id of the old vendor or architecture\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId\fR \fItoid\fR;
+\fI$class\fR\fB\->{toid}\fR
+\fIclass\fR\fB\&.toid\fR
+\fIclass\fR\fB\&.toid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The id of the new vendor or architecture\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid solvables()\fR;
+my \fI@solvables\fR \fB=\fR \fI$class\fR\fB\->solvables()\fR;
+\fIsolvables\fR \fB=\fR \fIclass\fR\fB\&.solvables()\fR
+\fIsolvables\fR \fB=\fR \fIclass\fR\fB\&.solvables()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the solvables for all transaction elements in the class\&.
+.SH "CHECKSUMS"
+.sp
+Checksums (also called hashes) are used to make sure that downloaded data is not corrupt and also as a fingerprint mechanism to check if data has changed\&.
+.SS "CLASS METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBChksum Chksum(Id\fR \fItype\fR\fB)\fR
+my \fI$chksum\fR \fB= solv::Chksum\->new(\fR\fI$type\fR\fB)\fR;
+\fIchksum\fR \fB= solv\&.Chksum(\fR\fItype\fR\fB)\fR
+\fIchksum\fR \fB= Solv::Chksum\&.new(\fR\fItype\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a checksum object\&. Currently the following types are supported:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBREPOKEY_TYPE_MD5\fR
+\fBREPOKEY_TYPE_SHA1\fR
+\fBREPOKEY_TYPE_SHA256\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+These keys are constants in the \fBsolv\fR class\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBChksum Chksum(Id\fR \fItype\fR\fB, const char *\fR\fIhex\fR\fB)\fR
+my \fI$chksum\fR \fB= solv::Chksum\->new(\fR\fI$type\fR\fB,\fR \fI$hex\fR\fB)\fR;
+\fIchksum\fR \fB= solv\&.Chksum(\fR\fItype\fR\fB,\fR \fIhex\fR\fB)\fR
+\fIchksum\fR \fB= Solv::Chksum\&.new(\fR\fItype\fR\fB,\fR \fIhex\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create an already finalized checksum object from a hex string\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBChksum Chksum_from_bin(Id\fR \fItype\fR\fB, char *\fR\fIbin\fR\fB)\fR
+my \fI$chksum\fR \fB= solv::Chksum\->from_bin(\fR\fI$type\fR\fB,\fR \fI$bin\fR\fB)\fR;
+\fIchksum\fR \fB= solv\&.Chksum\&.from_bin(\fR\fItype\fR\fB,\fR \fIbin\fR\fB)\fR
+\fIchksum\fR \fB= Solv::Chksum\&.from_bin(\fR\fItype\fR\fB,\fR \fIbin\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create an already finalized checksum object from a binary checksum\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId type;\fR /* read only */
+\fI$chksum\fR\fB\->{type}\fR
+\fIchksum\fR\fB\&.type\fR
+\fIchksum\fR\fB\&.type\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the type of the checksum object\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add(const char *\fR\fIstr\fR\fB)\fR
+\fI$chksum\fR\fB\->add(\fR\fI$str\fR\fB)\fR;
+\fIchksum\fR\fB\&.add(\fR\fIstr\fR\fB)\fR
+\fIchksum\fR\fB\&.add(\fR\fIstr\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a (binary) string to the checksum\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add_fp(FILE *\fR\fIfp\fR\fB)\fR
+\fI$chksum\fR\fB\->add_fp(\fR\fI$file\fR\fB)\fR;
+\fIchksum\fR\fB\&.add_fp(\fR\fIfile\fR\fB)\fR
+\fIchksum\fR\fB\&.add_fp(\fR\fIfile\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the contents of a file to the checksum\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add_stat(const char *\fR\fIfilename\fR\fB)\fR
+\fI$chksum\fR\fB\->add_stat(\fR\fI$filename\fR\fB)\fR;
+\fIchksum\fR\fB\&.add_stat(\fR\fIfilename\fR\fB)\fR
+\fIchksum\fR\fB\&.add_stat(\fR\fIfilename\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Stat the file and add the dev/ino/size/mtime member to the checksum\&. If the stat fails, the members are zeroed\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add_fstat(int\fR \fIfd\fR\fB)\fR
+\fI$chksum\fR\fB\->add_fstat(\fR\fI$fd\fR\fB)\fR;
+\fIchksum\fR\fB\&.add_fstat(\fR\fIfd\fR\fB)\fR
+\fIchksum\fR\fB\&.add_fstat(\fR\fIfd\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as add_stat, but instead of the filename a file descriptor is used\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBunsigned char *raw()\fR
+my \fI$raw\fR \fB=\fR \fI$chksum\fR\fB\->raw()\fR;
+\fIraw\fR \fB=\fR \fIchksum\fR\fB\&.raw()\fR
+\fIraw\fR \fB=\fR \fIchksum\fR\fB\&.raw()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Finalize the checksum and return the result as raw bytes\&. This means that the result can contain NUL bytes or unprintable characters\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *hex()\fR
+my \fI$raw\fR \fB=\fR \fI$chksum\fR\fB\->hex()\fR;
+\fIraw\fR \fB=\fR \fIchksum\fR\fB\&.hex()\fR
+\fIraw\fR \fB=\fR \fIchksum\fR\fB\&.hex()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Finalize the checksum and return the result as hex string\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *typestr()\fR
+my \fI$typestr\fR \fB=\fR \fI$chksum\fR\fB\->typestr()\fR;
+\fItypestr\fR \fB=\fR \fIchksum\fR\fB\&.typestr\fR
+\fItypestr\fR \fB=\fR \fIchksum\fR\fB\&.typestr\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the type of the checksum as a string, e\&.g\&. "sha256"\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<equality>\fR
+\fBif (\fR\fI$chksum1\fR \fB==\fR \fI$chksum2\fR\fB)\fR
+\fBif\fR \fIchksum1\fR \fB==\fR \fIchksum2\fR\fB:\fR
+\fBif\fR \fIchksum1\fR \fB==\fR \fIchksum2\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Checksums are equal if they are of the same type and the finalized results are the same\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$chksum\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIchksum\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIchksum\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+If the checksum is finished, the checksum is returned as "<type>:<hex>" string\&. Otherwise "<type>:unfinished" is returned\&.
+.SH "FILE MANAGEMENT"
+.sp
+This functions were added because libsolv uses standard \fBFILE\fR pointers to read/write files, but languages like perl have their own implementation of files\&. The libsolv functions also support decompression and compression, the algorithm is selected by looking at the file name extension\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBFILE *xfopen(char *\fR\fIfn\fR\fB, char *\fR\fImode\fR \fB= "r")\fR
+my \fI$file\fR \fB= solv::xfopen(\fR\fI$path\fR\fB)\fR;
+\fIfile\fR \fB= solv\&.xfopen(\fR\fIpath\fR\fB)\fR
+\fIfile\fR \fB= Solv::xfopen(\fR\fIpath\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Open a file at the specified path\&. The mode argument is passed on to the stdio library\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBFILE *xfopen_fd(char *\fR\fIfn\fR\fB, int\fR \fIfileno\fR\fB)\fR
+my \fI$file\fR \fB= solv::xfopen_fd(\fR\fI$path\fR\fB,\fR \fI$fileno\fR\fB)\fR;
+\fIfile\fR \fB= solv\&.xfopen_fd(\fR\fIpath\fR\fB,\fR \fIfileno\fR\fB)\fR
+\fIfile\fR \fB= Solv::xfopen_fd(\fR\fIpath\fR\fB,\fR \fIfileno\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a file handle from the specified file descriptor\&. The path argument is only used to select the correct (de\-)compression algorithm, use an empty path if you want to make sure to read/write raw data\&. The file descriptor is dup()ed before the file handle is created\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint fileno()\fR
+my \fI$fileno\fR \fB=\fR \fI$file\fR\fB\->fileno()\fR;
+\fIfileno\fR \fB=\fR \fIfile\fR\fB\&.fileno()\fR
+\fIfileno\fR \fB=\fR \fIfile\fR\fB\&.fileno()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return file file descriptor of the file\&. If the file is not open, \-1 is returned\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid cloexec(bool\fR \fIstate\fR\fB)\fR
+\fI$file\fR\fB\->cloexec(\fR\fI$state\fR\fB)\fR
+\fIfile\fR\fB\&.cloexec(\fR\fIstate\fR\fB)\fR
+\fIfile\fR\fB\&.cloexec(\fR\fIstate\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the close\-on\-exec flag of the file descriptor\&. The xfopen function returns files with close\-on\-exec turned on, so if you want to pass a file to some other process you need to call cloexec(0) before calling exec\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint dup()\fR
+my \fI$fileno\fR \fB=\fR \fI$file\fR\fB\->dup()\fR;
+\fIfileno\fR \fB=\fR \fIfile\fR\fB\&.dup()\fR
+\fIfileno\fR \fB=\fR \fIfile\fR\fB\&.dup()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a copy of the descriptor of the file\&. If the file is not open, \-1 is returned\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool flush()\fR
+\fI$file\fR\fB\->flush()\fR;
+\fIfile\fR\fB\&.flush()\fR
+\fIfile\fR\fB\&.flush()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Flush the file\&. Returns false if there was an error\&. Flushing a closed file always returns true\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool close()\fR
+\fI$file\fR\fB\->close()\fR;
+\fIfile\fR\fB\&.close()\fR
+\fIfile\fR\fB\&.close()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Close the file\&. This is needed for languages like Ruby that do not destruct objects right after they are no longer referenced\&. In that case, it is good style to close open files so that the file descriptors are freed right away\&. Returns false if there was an error\&.
+.SH "THE REPODATA CLASS"
+.sp
+The Repodata stores attributes for packages and the repository itself, each repository can have multiple repodata areas\&. You normally only need to directly access them if you implement lazy downloading of repository data\&. Repodata areas are created by calling the repository\(cqs add_repodata() method or by using repo_add methods without the REPO_REUSE_REPODATA or REPO_USE_LOADING flag\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepo *repo;\fR /* read only */
+\fI$data\fR\fB\->{repo}\fR
+\fIdata\fR\fB\&.repo\fR
+\fIdata\fR\fB\&.repo\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to repository object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId id;\fR /* read only */
+\fI$data\fR\fB\->{id}\fR
+\fIdata\fR\fB\&.id\fR
+\fIdata\fR\fB\&.id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The id of the repodata area\&. Repodata ids of different repositories overlap\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBinternalize()\fR;
+\fI$data\fR\fB\->internalize()\fR;
+\fIdata\fR\fB\&.internalize()\fR
+\fIdata\fR\fB\&.internalize()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Internalize newly added data\&. The lookup functions will only see the new data after it has been internalized\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool write(FILE *\fR\fIfp\fR\fB)\fR;
+\fI$data\fR\fB\->write(\fR\fI$fp\fR\fB)\fR;
+\fIdata\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
+\fIdata\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Write the contents of the repodata area as solv file\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool add_solv(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR;
+\fI$data\fR\fB\->add_solv(\fR\fI$fp\fR\fB)\fR;
+\fIdata\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
+\fIdata\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Replace a stub repodata object with the data from a solv file\&. This method automatically adds the REPO_USE_LOADING flag\&. It should only be used from a load callback\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid create_stubs()\fR;
+\fI$data\fR\fB\->create_stubs()\fR
+\fIdata\fR\fB\&.create_stubs()\fR
+\fIdata\fR\fB\&.create_stubs()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create stub repodatas from the information stored in the repodata meta area\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid extend_to_repo()\fR;
+\fI$data\fR\fB\->extend_to_repo()\fR;
+\fIdata\fR\fB\&.extend_to_repo()\fR
+\fIdata\fR\fB\&.extend_to_repo()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Extend the repodata so that it has the same size as the repo it belongs to\&. This method is only needed when switching to a just written repodata extension to make the repodata match the written extension (which is always of the size of the repo)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<equality>\fR
+\fBif (\fR\fI$data1\fR \fB==\fR \fI$data2\fR\fB)\fR
+\fBif\fR \fIdata1\fR \fB==\fR \fIdata2\fR\fB:\fR
+\fBif\fR \fIdata1\fR \fB==\fR \fIdata2\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Two repodata objects are equal if they belong to the same repository and have the same id\&.
+.SS "DATA RETRIEVAL METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *lookup_str(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI$string\fR \fB=\fR \fI$data\fR\fB\->lookup_str(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIstring\fR \fB=\fR \fIdata\fR\fB\&.lookup_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIstring\fR \fB=\fR \fIdata\fR\fB\&.lookup_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId *lookup_idarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI@ids\fR \fB=\fR \fI$data\fR\fB\->lookup_idarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIids\fR \fB=\fR \fIdata\fR\fB\&.lookup_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIids\fR \fB=\fR \fIdata\fR\fB\&.lookup_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBChksum lookup_checksum(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI$chksum\fR \fB=\fR \fI$data\fR\fB\->lookup_checksum(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIchksum\fR \fB=\fR \fIdata\fR\fB\&.lookup_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIchksum\fR \fB=\fR \fIdata\fR\fB\&.lookup_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Lookup functions\&. Return the data element stored in the specified solvable\&. The methods probably only make sense to retrieve data from the special SOLVID_META solvid that stores repodata meta information\&.
+.SS "DATA STORAGE METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid set_id(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, DepId\fR \fIid\fR\fB)\fR;
+\fI$data\fR\fB\->set_id(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$id\fR\fB)\fR;
+\fIdata\fR\fB\&.set_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
+\fIdata\fR\fB\&.set_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid set_str(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fIstr\fR\fB)\fR;
+\fI$data\fR\fB\->set_str(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$str\fR\fB)\fR;
+\fIdata\fR\fB\&.set_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
+\fIdata\fR\fB\&.set_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid set_poolstr(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fIstr\fR\fB)\fR;
+\fI$data\fR\fB\->set_poolstr(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$str\fR\fB)\fR;
+\fIdata\fR\fB\&.set_poolstr(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
+\fIdata\fR\fB\&.set_poolstr(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid set_checksum(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Chksum *\fR\fIchksum\fR\fB)\fR;
+\fI$data\fR\fB\->set_checksum(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$chksum\fR\fB)\fR;
+\fIdata\fR\fB\&.set_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIchksum\fR\fB)\fR
+\fIdata\fR\fB\&.set_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIchksum\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add_idarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, DepId\fR \fIid\fR\fB)\fR;
+\fI$data\fR\fB\->add_idarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$id\fR\fB)\fR;
+\fIdata\fR\fB\&.add_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
+\fIdata\fR\fB\&.add_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId new_handle()\fR;
+my \fI$handle\fR \fB=\fR \fI$data\fR\fB\->new_handle()\fR;
+\fIhandle\fR \fB=\fR \fIdata\fR\fB\&.new_handle()\fR
+\fIhandle\fR \fB=\fR \fIdata\fR\fB\&.new_handle()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid add_flexarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fIhandle\fR\fB)\fR;
+\fI$data\fR\fB\->add_flexarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$handle\fR\fB)\fR;
+\fIdata\fR\fB\&.add_flexarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIhandle\fR\fB)\fR
+\fIdata\fR\fB\&.add_flexarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIhandle\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Data storage methods\&. Probably only useful to store data in the special SOLVID_META solvid that stores repodata meta information\&. Note that repodata areas can have their own Id pool (see the REPO_LOCALPOOL flag), so be careful if you need to store ids\&. Arrays are created by calling the add function for every element\&. A flexarray is an array of sub\-structures, call new_handle to create a new structure, use the handle as solvid to fill the structure with data and call add_flexarray to put the structure in an array\&.
+.SH "THE DATAPOS CLASS"
+.sp
+Datapos objects describe a specific position in the repository data area\&. Thus they are only valid until the repository is modified in some way\&. Datapos objects can be created by the pos() and parentpos() methods of a Datamatch object or by accessing the \(lqmeta\(rq attribute of a repository\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRepo *repo;\fR /* read only */
+\fI$data\fR\fB\->{repo}\fR
+\fIdata\fR\fB\&.repo\fR
+\fIdata\fR\fB\&.repo\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to repository object\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDataiterator(Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR\fB, int\fR \fIflags\fR\fB)\fR
+my \fI$di\fR \fB=\fR \fI$datapos\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIdi\fR \fB=\fR \fIdatapos\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIdi\fR \fB=\fR \fIdatapos\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a Dataiterator at the position of the datapos object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *lookup_deltalocation(unsigned int *\fR\fIOUTPUT\fR\fB)\fR;
+my \fB(\fR\fI$location\fR\fB,\fR \fI$medianr\fR\fB) =\fR \fI$datapos\fR\fB\->lookup_deltalocation()\fR;
+\fIlocation\fR\fB,\fR \fImedianr\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltalocation()\fR
+\fIlocation\fR\fB,\fR \fImedianr\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltalocation()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a tuple containing the on\-media location and an optional media number for a delta rpm\&. This obviously only works if the data position points to structure describing a delta rpm\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *lookup_deltaseq()\fR;
+my \fI$seq\fR \fB=\fR \fI$datapos\fR\fB\->lookup_deltaseq()\fR;
+\fIseq\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltaseq()\fR;
+\fIseq\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltaseq()\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the delta rpm sequence from the structure describing a delta rpm\&.
+.SS "DATA RETRIEVAL METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *lookup_str(Id\fR \fIkeyname\fR\fB)\fR
+my \fI$string\fR \fB=\fR \fI$datapos\fR\fB\->lookup_str(\fR\fI$keyname\fR\fB)\fR;
+\fIstring\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_str(\fR\fIkeyname\fR\fB)\fR
+\fIstring\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_str(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId lookup_id(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
+my \fI$id\fR \fB=\fR \fI$datapos\fR\fB\->lookup_id(\fR\fI$keyname\fR\fB)\fR;
+\fIid\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_id(\fR\fIkeyname\fR\fB)\fR
+\fIid\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_id(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBunsigned long long lookup_num(Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInotfound\fR \fB= 0)\fR
+my \fI$num\fR \fB=\fR \fI$datapos\fR\fB\->lookup_num(\fR\fI$keyname\fR\fB)\fR;
+\fInum\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_num(\fR\fIkeyname\fR\fB)\fR
+\fInum\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_num(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBbool lookup_void(Id\fR \fIkeyname\fR\fB)\fR
+my \fI$bool\fR \fB=\fR \fI$datapos\fR\fB\->lookup_void(\fR\fI$keyname\fR\fB)\fR;
+\fIbool\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_void(\fR\fIkeyname\fR\fB)\fR
+\fIbool\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_void(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId *lookup_idarray(Id\fR \fIkeyname\fR\fB)\fR
+my \fI@ids\fR \fB=\fR \fI$datapos\fR\fB\->lookup_idarray(\fR\fI$keyname\fR\fB)\fR;
+\fIids\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_idarray(\fR\fIkeyname\fR\fB)\fR
+\fIids\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_idarray(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBChksum lookup_checksum(Id\fR \fIkeyname\fR\fB)\fR
+my \fI$chksum\fR \fB=\fR \fI$datapos\fR\fB\->lookup_checksum(\fR\fI$keyname\fR\fB)\fR;
+\fIchksum\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_checksum(\fR\fIkeyname\fR\fB)\fR
+\fIchksum\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_checksum(\fR\fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Lookup functions\&. Note that the returned Ids are always translated into the Ids of the global pool even if the repodata area contains its own pool\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDataiterator Dataiterator(Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR \fB= 0, int\fR \fIflags\fR \fB= 0)\fR
+my \fI$di\fR \fB=\fR \fI$datapos\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \fI$match\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIdi\fR \fB=\fR \fIdatapos\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIdi\fR \fB=\fR \fIdatapos\fR\fB\&.Dataiterator(\fR\fIkeyname\fR\fB,\fR \fImatch\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBfor my\fR \fI$d\fR \fB(\fR\fI@$di\fR\fB)\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR\fB:\fR
+\fBfor\fR \fId\fR \fBin\fR \fIdi\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Iterate over the matching data elements\&. See the Dataiterator class for more information\&.
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+Libsolv-Bindings(3)
+===================
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+libsolv-bindings - access libsolv from perl/python/ruby
+
+
+Description
+-----------
+Libsolv's language bindings offer an abstract, object orientated interface
+to the library. The supported languages are currently perl, python, and ruby.
+All example code (except in the specifics sections, of course) lists first
+the ``C-ish'' interface, then the syntax for perl, python, and ruby (in that
+order).
+
+
+Perl Specifics
+--------------
+Libsolv's perl bindings can be loaded with the following statement:
+
+ use solv;
+
+Objects are either created by calling the new() method on a class or they
+are returned by calling methods on other objects.
+
+ my $pool = solv::Pool->new();
+ my $repo = $pool->add_repo("my_first_repo");
+
+Swig encapsulates all objects as tied hashes, thus the attributes can be
+accessed by treating the object as standard hash reference:
+
+ $pool->{appdata} = 42;
+ printf "appdata is %d\n", $pool->{appdata};
+
+A special exception to this are iterator objects, they are encapsulated as
+tied arrays so that it is possible to iterate with a for() statement:
+
+ my $iter = $pool->solvables_iter();
+ for my $solvable (@$iter) { ... };
+
+As a downside of this approach, iterator objects cannot have attributes.
+
+If an array needs to be passed to a method it is usually done by reference,
+if a method returns an array it returns it on the stack:
+
+ my @problems = $solver->solve(\@jobs);
+
+Due to a bug in swig, stringification does not work for libsolv's objects.
+Instead, you have to call the object's str() method.
+
+ print $dep->str() . "\n";
+
+Swig implements all constants as numeric variables (instead of the more
+natural constant subs), so don't forget the leading ``$'' when accessing a
+constant. Also do not forget to prepend the namespace of the constant:
+
+ $pool->set_flag($solv::Pool::POOL_FLAG_OBSOLETEUSESCOLORS, 1);
+
+
+Python Specifics
+----------------
+The python bindings can be loaded with:
+
+ import solv
+
+Objects are either created by calling the constructor method for a class or they
+are returned by calling methods on other objects.
+
+ pool = solv.Pool()
+ repo = pool.add_repo("my_first_repo")
+
+Attributes can be accessed as usual:
+
+ pool.appdata = 42
+ print "appdata is %d" % (pool.appdata)
+
+Iterators also work as expected:
+
+ for solvable in pool.solvables_iter():
+
+Arrays are passed and returned as list objects:
+
+ jobs = []
+ problems = solver.solve(jobs)
+
+The bindings define stringification for many classes, some also have a
+__repr__ method to ease debugging.
+
+ print dep
+ print repr(repo)
+
+Constants are attributes of the classes:
+
+ pool.set_flag(solv.Pool.POOL_FLAG_OBSOLETEUSESCOLORS, 1);
+
+
+Ruby Specifics
+--------------
+The ruby bindings can be loaded with:
+
+ require 'solv'
+
+Objects are either created by calling the new method on a class or they
+are returned by calling methods on other objects. Note that all classes start
+with an uppercase letter in ruby, so the class is called ``Solv''.
+
+ pool = Solv::Pool.new
+ repo = pool.add_repo("my_first_repo")
+
+Attributes can be accessed as usual:
+
+ pool.appdata = 42
+ puts "appdata is #{pool.appdata}"
+
+Iterators also work as expected:
+
+ for solvable in pool.solvables_iter() do ...
+
+Arrays are passed and returned as array objects:
+
+ jobs = []
+ problems = solver.solve(jobs)
+
+Most classes define a to_s method, so objects can be easily stringified.
+Many also define an inspect() method.
+
+ puts dep
+ puts repo.inspect
+
+Constants live in the namespace of the class they belong to:
+
+ pool.set_flag(Solv::Pool::POOL_FLAG_OBSOLETEUSESCOLORS, 1);
+
+Note that boolean methods have an added trailing ``?'', to be consistent with
+other ruby modules:
+
+ puts "empty" if repo.isempty?
+
+
+Tcl Specifics
+-------------
+Libsolv's tcl bindings can be loaded with the following statement:
+
+ TCL package require solv
+
+Objects are either created by calling class name prefixed with ``new_'',
+or they are returned by calling methods on other objects.
+
+ TCL set pool [solv::new_Pool]
+ TCL set repo [$pool add_repo "my_first_repo"]
+
+Swig provides a ``cget'' method to read object attributes, and a
+``configure'' method to write them:
+
+ TCL $pool configure -appdata 42
+ TCL puts "appdata is [$pool cget -appdata]"
+
+The tcl bindings provide a little helper to work with iterators in
+a foreach style:
+
+ TCL set iter [$pool solvables_iter]
+ TCL solv::iter s $iter { ... }
+
+libsolv's arrays are mapped to tcl's lists:
+
+ TCL set jobs [list $job1 $job2]
+ TCL set problems [$solver solve $jobs]
+ TCL puts "We have [llength $problems] problems..."
+
+Stringification is done by calling the object's ``str'' method.
+
+ TCL puts [$dep str]
+
+There is one exception: you have to use ``stringify'' for Datamatch
+objects, as swig reports a clash with the ``str'' attribute.
+Some objects also support a ``=='' method for equality tests, and a
+``!='' method.
+
+Swig implements all constants as numeric variables, constants belonging
+to a libsolv class are prefixed with the class name:
+
+ TCL $pool set_flag $solv::Pool_POOL_FLAG_OBSOLETEUSESCOLORS 1
+ TCL puts [$solvable lookup_str $solv::SOLVABLE_SUMMARY]
+
+
+The Solv Class
+--------------
+This is the main namespace of the library, you cannot create objects of this
+type but it contains some useful constants.
+
+=== CONSTANTS ===
+
+Relational flag constants, the first three can be or-ed together
+
+*REL_LT*::
+the ``less than'' bit
+
+*REL_EQ*::
+the ``equals to'' bit
+
+*REL_GT*::
+the ``greater than'' bit
+
+*REL_ARCH*::
+used for relations that describe an extra architecture filter, the
+version part of the relation is interpreted as architecture.
+
+Special Solvable Ids
+
+*SOLVID_META*::
+Access the meta section of a repository or repodata area. This is
+like an extra Solvable that has the Id SOLVID_META.
+
+*SOLVID_POS*::
+Use the data position stored inside of the pool instead of accessing
+some solvable by Id. The bindings have the Datapos objects as an
+abstraction mechanism, so you do not need this constant.
+
+Constant string Ids
+
+*ID_NULL*::
+Always zero
+
+*ID_EMPTY*::
+Always one, describes the empty string
+
+*SOLVABLE_NAME*::
+The keyname Id of the name of the solvable.
+
+*...*::
+see the libsolv-constantids manpage for a list of fixed Ids.
+
+
+The Pool Class
+--------------
+The pool is libsolv's central resource manager. A pool consists of Solvables,
+Repositories, Dependencies, each indexed by Ids.
+
+=== CLASS METHODS ===
+
+ Pool *Pool()
+ my $pool = solv::Pool->new();
+ pool = solv.Pool()
+ pool = Solv::Pool.new()
+
+Create a new pool instance. In most cases you just need one pool.
+Note that the returned object "owns" the pool, i.e. if the object is
+freed, the pool is also freed. You can use the disown method to
+break this ownership relation.
+
+=== ATTRIBUTES ===
+
+ void *appdata; /* read/write */
+ $pool->{appdata}
+ pool.appdata
+ pool.appdata
+
+Application specific data that may be used in any way by the code using the
+pool.
+
+ Solvable solvables[]; /* read only */
+ my $solvable = $pool->{solvables}->[$solvid];
+ solvable = pool.solvables[solvid]
+ solvable = pool.solvables[solvid]
+
+Look up a Solvable by its id.
+
+ Repo repos[]; /* read only */
+ my $repo = $pool->{repos}->[$repoid];
+ repo = pool.repos[repoid]
+ repo = pool.repos[repoid]
+
+Look up a Repository by its id.
+
+ Repo *installed; /* read/write */
+ $pool->{installed} = $repo;
+ pool.installed = repo
+ pool.installed = repo
+
+Define which repository contains all the installed packages.
+
+ const char *errstr; /* read only */
+ my $err = $pool->{errstr};
+ err = pool.errstr
+ err = pool.errstr
+
+Return the last error string that was stored in the pool.
+
+=== CONSTANTS ===
+
+*POOL_FLAG_PROMOTEEPOCH*::
+Promote the epoch of the providing dependency to the requesting
+dependency if it does not contain an epoch. Used at some time
+in old rpm versions, modern systems should never need this.
+
+*POOL_FLAG_FORBIDSELFCONFLICTS*::
+Disallow the installation of packages that conflict with themselves.
+Debian always allows self-conflicting packages, rpm used to forbid
+them but switched to also allowing them recently.
+
+*POOL_FLAG_OBSOLETEUSESPROVIDES*::
+Make obsolete type dependency match against provides instead of
+just the name and version of packages. Very old versions of rpm
+used the name/version, then it got switched to provides and later
+switched back again to just name/version.
+
+*POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES*::
+An implicit obsoletes is the internal mechanism to remove the
+old package on an update. The default is to remove all packages
+with the same name, rpm-5 switched to also removing packages
+providing the same name.
+
+*POOL_FLAG_OBSOLETEUSESCOLORS*::
+Rpm's multilib implementation (used in RedHat and Fedora)
+distinguishes between 32bit and 64bit packages (the terminology
+is that they have a different color). If obsoleteusescolors is
+set, packages with different colors will not obsolete each other.
+
+*POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS*::
+Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if
+packages of the same name can be installed in parallel. For
+current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be
+false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true
+(this is the default if FEDORA is defined when libsolv is compiled).
+
+*POOL_FLAG_NOINSTALLEDOBSOLETES*::
+New versions of rpm consider the obsoletes of installed packages
+when checking for dependency, thus you may not install a package
+that is obsoleted by some other installed package, unless you
+also erase the other package.
+
+*POOL_FLAG_HAVEDISTEPOCH*::
+Mandriva added a new field called distepoch that gets checked in
+version comparison if the epoch/version/release of two packages
+are the same.
+
+*POOL_FLAG_NOOBSOLETESMULTIVERSION*::
+If a package is installed in multiversionmode, rpm used to ignore
+both the implicit obsoletes and the obsolete dependency of a
+package. This was changed to ignoring just the implicit obsoletes,
+thus you may install multiple versions of the same name, but
+obsoleted packages still get removed.
+
+*POOL_FLAG_ADDFILEPROVIDESFILTERED*::
+Make the addfileprovides method only add files from the standard
+locations (i.e. the ``bin'' and ``etc'' directories). This is
+useful if you have only few packages that use non-standard file
+dependencies, but you still want the fast speed that addfileprovides()
+generates.
+
+=== METHODS ===
+
+ void free()
+ $pool->free();
+ pool.free()
+ pool.free()
+
+Force a free of the pool. After this call, you must not access any object
+that still references the pool.
+
+ void disown()
+ $pool->disown();
+ pool.disown()
+ pool.disown()
+
+Break the ownership relation between the binding object and the pool. After
+this call, the pool will not get freed even if the object goes out of
+scope. This also means that you must manually call the free method to free
+the pool data.
+
+ void setdebuglevel(int level)
+ $pool->setdebuglevel($level);
+ pool.setdebuglevel(level)
+ pool.setdebuglevel(level)
+
+Set the debug level. A value of zero means no debug output, the higher the
+value, the more output is generated.
+
+ int set_flag(int flag, int value)
+ my $oldvalue = $pool->set_flag($flag, $value);
+ oldvalue = pool.set_flag(flag, value)
+ oldvalue = pool.set_flag(flag, value)
+
+ int get_flag(int flag)
+ my $value = $pool->get_flag($flag);
+ value = pool.get_flag(flag)
+ value = pool.get_flag(flag)
+
+Set/get a pool specific flag. The flags define how the system works, e.g. how
+the package manager treats obsoletes. The default flags should be sane for most
+applications, but in some cases you may want to tweak a flag, for example if
+you want to solv package dependencies for some other system than yours.
+
+ void set_rootdir(const char *rootdir)
+ $pool->set_rootdir(rootdir);
+ pool.set_rootdir(rootdir)
+ pool.set_rootdir(rootdir)
+
+ const char *get_rootdir()
+ my $rootdir = $pool->get_rootdir();
+ rootdir = pool.get_rootdir()
+ rootdir = pool.get_rootdir()
+
+Set/get the rootdir to use. This is useful if you want package management
+to work only in some directory, for example if you want to setup a chroot
+jail. Note that the rootdir will only be prepended to file paths if the
+*REPO_USE_ROOTDIR* flag is used.
+
+ void setarch(const char *arch = 0)
+ $pool->setarch();
+ pool.setarch()
+ pool.setarch()
+
+Set the architecture for your system. The architecture is used to determine
+which packages are installable. It defaults to the result of ``uname -m''.
+
+ Repo add_repo(const char *name)
+ $repo = $pool->add_repo($name);
+ repo = pool.add_repo(name)
+ repo = pool.add_repo(name)
+
+Add a Repository with the specified name to the pool. The repository is empty
+on creation, use the repository methods to populate it with packages.
+
+ Repoiterator repos_iter()
+ for my $repo (@{$pool->repos_iter()})
+ for repo in pool.repos_iter():
+ for repo in pool.repos_iter()
+
+Iterate over the existing repositories.
+
+ Solvableiterator solvables_iter()
+ for my $solvable (@{$pool->solvables_iter()})
+ for solvable in pool.solvables_iter():
+ for solvable in pool.solvables_iter()
+
+Iterate over the existing solvables.
+
+ Dep Dep(const char *str, bool create = 1)
+ my $dep = $pool->Dep($string);
+ dep = pool.Dep(string)
+ dep = pool.Dep(string)
+
+Create an object describing a string or dependency. If the string is currently
+not in the pool and _create_ is false, *undef*/*None*/*nil* is returned.
+
+ void addfileprovides()
+ $pool->addfileprovides();
+ pool.addfileprovides()
+ pool.addfileprovides()
+
+ Id *addfileprovides_queue()
+ my @ids = $pool->addfileprovides_queue();
+ ids = pool.addfileprovides_queue()
+ ids = pool.addfileprovides_queue()
+
+Some package managers like rpm allow dependencies on files contained in other
+packages. To allow libsolv to deal with those dependencies in an efficient way,
+you need to call the addfileprovides method after creating and reading all
+repositories. This method will scan all dependency for file names and then scan
+all packages for matching files. If a filename has been matched, it will be
+added to the provides list of the corresponding package. The
+addfileprovides_queue variant works the same way but returns an array
+containing all file dependencies. This information can be stored in the
+meta section of the repositories to speed up the next time the
+repository is loaded and addfileprovides is called.
+
+ void createwhatprovides()
+ $pool->createwhatprovides();
+ pool.createwhatprovides()
+ pool.createwhatprovides()
+
+Create the internal ``whatprovides'' hash over all of the provides of all
+packages. This method must be called before doing any lookups on provides.
+It's encouraged to do it right after all repos are set up, usually right after
+the call to addfileprovides().
+
+ Solvable *whatprovides(DepId dep)
+ my @solvables = $pool->whatprovides($dep);
+ solvables = pool.whatprovides(dep)
+ solvables = pool.whatprovides(dep)
+
+Return all solvables that provide the specified dependency. You can use either
+a Dep object or a simple Id as argument.
+
+ Id *matchprovidingids(const char *match, int flags)
+ my @ids = $pool->matchprovidingids($match, $flags);
+ ids = pool.matchprovidingids(match, flags)
+ ids = pool.matchprovidingids(match, flags)
+
+Search the names of all provides and return the ones matching the specified
+string. See the Dataiterator class for the allowed flags.
+
+ Id towhatprovides(Id *ids)
+ my $offset = $pool->towhatprovides(\@ids);
+ offset = pool.towhatprovides(ids)
+ offset = pool.towhatprovides(ids)
+
+``Internalize'' an array containing Ids. The returned value can be used to
+create solver jobs working on a specific set of packages. See the Solver class
+for more information.
+
+ bool isknownarch(DepId id)
+ my $bool = $pool->isknownarch($id);
+ bool = pool.isknownarch(id)
+ bool = pool.isknownarch?(id)
+
+Return true if the specified Id describes a known architecture.
+
+ Solver Solver()
+ my $solver = $pool->Solver();
+ solver = pool.Solver()
+ solver = pool.Solver()
+
+Create a new solver object.
+
+ Job Job(int how, Id what)
+ my $job = $pool->Job($how, $what);
+ job = pool.Job(how, what)
+ job = pool.Job(how, what)
+
+Create a new Job object. Kind of low level, in most cases you would use a
+Selection or Dep job constructor instead.
+
+ Selection Selection()
+ my $sel = $pool->Selection();
+ sel = pool.Selection()
+ sel = pool.Selection()
+
+Create an empty selection. Useful as a starting point for merging other
+selections.
+
+ Selection Selection_all()
+ my $sel = $pool->Selection_all();
+ sel = pool.Selection_all()
+ sel = pool.Selection_all()
+
+Create a selection containing all packages. Useful as starting point for
+intersecting other selections or for update/distupgrade jobs.
+
+ Selection select(const char *name, int flags)
+ my $sel = $pool->select($name, $flags);
+ sel = pool.select(name, flags)
+ sel = pool.select(name, flags)
+
+Create a selection by matching packages against the specified string. See the
+Selection class for a list of flags and how to create solver jobs from a
+selection.
+
+ void setpooljobs(Jobs *jobs)
+ $pool->setpooljobs(\@jobs);
+ pool.setpooljobs(jobs)
+ pool.setpooljobs(jobs)
+
+ Job *getpooljobs()
+ @jobs = $pool->getpooljobs();
+ jobs = pool.getpooljobs()
+ jobs = pool.getpooljobs()
+
+Get/Set fixed jobs stored in the pool. Those jobs are automatically appended to
+all solver jobs, they are meant for fixed configurations like which packages
+can be multiversion installed, which packages were userinstalled or must not be
+erased.
+
+ void set_loadcallback(Callable *callback)
+ $pool->setloadcallback(\&callbackfunction);
+ pool.setloadcallback(callbackfunction)
+ pool.setloadcallback { |repodata| ... }
+
+Set the callback function called when repository metadata needs to be loaded on
+demand. To make use of this feature, you need to create repodata stubs that
+tell the library which data is available but not loaded. If later on the data
+needs to be accessed, the callback function is called with a repodata argument.
+You can then load the data (maybe fetching it first from a remote server).
+The callback should return true if the data has been made available.
+
+ /* bindings only */
+ $pool->appdata_disown()
+ pool.appdata_disown()
+ pool.appdata_disown()
+
+Decrement the reference count of the appdata object. This can be used to break
+circular references (e.g. if the pool's appdata value points to some meta data
+structure that contains a pool handle). If used incorrectly, this method can
+lead to application crashes, so beware. (This method is a no-op for ruby and tcl.)
+
+=== DATA RETRIEVAL METHODS ===
+
+In the following functions, the _keyname_ argument describes what to retrieve.
+For the standard cases you can use the available Id constants. For example,
+
+ $solv::SOLVABLE_SUMMARY
+ solv.SOLVABLE_SUMMARY
+ Solv::SOLVABLE_SUMMARY
+
+selects the ``Summary'' entry of a solvable. The _solvid_ argument selects the
+desired solvable by Id.
+
+ const char *lookup_str(Id solvid, Id keyname)
+ my $string = $pool->lookup_str($solvid, $keyname);
+ string = pool.lookup_str(solvid, keyname)
+ string = pool.lookup_str(solvid, keyname)
+
+ Id lookup_id(Id solvid, Id keyname)
+ my $id = $pool->lookup_id($solvid, $keyname);
+ id = pool.lookup_id(solvid, keyname)
+ id = pool.lookup_id(solvid, keyname)
+
+ unsigned long long lookup_num(Id solvid, Id keyname, unsigned long long notfound = 0)
+ my $num = $pool->lookup_num($solvid, $keyname);
+ num = pool.lookup_num(solvid, keyname)
+ num = pool.lookup_num(solvid, keyname)
+
+ bool lookup_void(Id solvid, Id keyname)
+ my $bool = $pool->lookup_void($solvid, $keyname);
+ bool = pool.lookup_void(solvid, keyname)
+ bool = pool.lookup_void(solvid, keyname)
+
+ Id *lookup_idarray(Id solvid, Id keyname)
+ my @ids = $pool->lookup_idarray($solvid, $keyname);
+ ids = pool.lookup_idarray(solvid, keyname)
+ ids = pool.lookup_idarray(solvid, keyname)
+
+ Chksum lookup_checksum(Id solvid, Id keyname)
+ my $chksum = $pool->lookup_checksum($solvid, $keyname);
+ chksum = pool.lookup_checksum(solvid, keyname)
+ chksum = pool.lookup_checksum(solvid, keyname)
+
+Lookup functions. Return the data element stored in the specified solvable.
+You should probably use the methods of the Solvable class instead.
+
+ Dataiterator Dataiterator(Id keyname, const char *match = 0, int flags = 0)
+ my $di = $pool->Dataiterator($keyname, $match, $flags);
+ di = pool.Dataiterator(keyname, match, flags)
+ di = pool.Dataiterator(keyname, match, flags)
+
+ Dataiterator Dataiterator_solvid(Id solvid, Id keyname, const char *match = 0, int flags = 0)
+ my $di = $pool->Dataiterator($solvid, $keyname, $match, $flags);
+ di = pool.Dataiterator(solvid, keyname, match, flags)
+ di = pool.Dataiterator(solvid, keyname, match, flags)
+
+ for my $d (@$di)
+ for d in di:
+ for d in di
+
+Iterate over the matching data elements. See the Dataiterator class for more
+information. The Dataiterator method iterates over all solvables in the pool,
+whereas the Dataiterator_solvid only iterates over the specified solvable.
+
+=== ID METHODS ===
+
+The following methods deal with Ids, i.e. integers representing objects in the
+pool. They are considered ``low level'', in most cases you would not use them
+but instead the object orientated methods.
+
+ Repo id2repo(Id id)
+ $repo = $pool->id2repo($id);
+ repo = pool.id2repo(id)
+ repo = pool.id2repo(id)
+
+Lookup an existing Repository by id. You can also do this by using the *repos*
+attribute.
+
+ Solvable id2solvable(Id id)
+ $solvable = $pool->id2solvable($id);
+ solvable = pool.id2solvable(id)
+ solvable = pool.id2solvable(id)
+
+Lookup an existing Repository by id. You can also do this by using the
+*solvables* attribute.
+
+ const char *solvid2str(Id id)
+ my $str = $pool->solvid2str($id);
+ str = pool.solvid2str(id)
+ str = pool.solvid2str(id)
+
+Return a string describing the Solvable with the specified id. The string
+consists of the name, version, and architecture of the Solvable.
+
+ Id str2id(const char *str, bool create = 1)
+ my $id = pool->str2id($string);
+ id = pool.str2id(string)
+ id = pool.str2id(string)
+
+ const char *id2str(Id id)
+ $string = pool->id2str($id);
+ string = pool.id2str(id)
+ string = pool.id2str(id)
+
+Convert a string into an Id and back. If the string is currently not in the
+pool and _create_ is false, zero is returned.
+
+ Id rel2id(Id name, Id evr, int flags, bool create = 1)
+ my $id = pool->rel2id($nameid, $evrid, $flags);
+ id = pool.rel2id(nameid, evrid, flags)
+ id = pool.rel2id(nameid, evrid, flags)
+
+Create a ``relational'' dependency. Such dependencies consist of a name part,
+the _flags_ describing the relation, and a version part. The flags are:
+
+ $solv::REL_EQ | $solv::REL_GT | $solv::REL_LT
+ solv.REL_EQ | solv.REL_GT | solv.REL_LT
+ Solv::REL_EQ | Solv::REL_GT | Solv::REL_LT
+
+Thus, if you want a ``\<='' relation, you would use *REL_LT | REL_EQ*.
+
+ Id id2langid(Id id, const char *lang, bool create = 1)
+ my $id = $pool->id2langid($id, $language);
+ id = pool.id2langid(id, language)
+ id = pool.id2langid(id, language)
+
+Create a language specific Id from some other id. This function simply converts
+the id into a string, appends a dot and the specified language to the string
+and converts the result back into an Id.
+
+ const char *dep2str(Id id)
+ $string = pool->dep2str($id);
+ string = pool.dep2str(id)
+ string = pool.dep2str(id)
+
+Convert a dependency id into a string. If the id is just a string, this
+function has the same effect as id2str(). For relational dependencies, the
+result is the correct ``name relation evr'' string.
+
+
+The Dependency Class
+--------------------
+The dependency class is an object orientated way to work with strings and
+dependencies. Internally, dependencies are represented as Ids, i.e. simple
+numbers. Dependency objects can be constructed by using the Pool's Dep()
+method.
+
+=== ATTRIBUTES ===
+
+ Pool *pool; /* read only */
+ $dep->{pool}
+ dep.pool
+ dep.pool
+
+Back reference to the pool this dependency belongs to.
+
+ Id id; /* read only */
+ $dep->{id}
+ dep.id
+ dep.id
+
+The id of this dependency.
+
+== Methods ==
+
+ Dep Rel(int flags, DepId evrid, bool create = 1)
+ my $reldep = $dep->Rel($flags, $evrdep);
+ reldep = dep.Rel(flags, evrdep)
+ reldep = dep.Rel(flags, evrdep)
+
+Create a relational dependency from to string dependencies and a flags
+argument. See the pool's rel2id method for a description of the flags.
+
+ Selection Selection_name(int setflags = 0)
+ my $sel = $dep->Selection_name();
+ sel = dep.Selection_name()
+ sel = dep.Selection_name()
+
+Create a Selection from a dependency. The selection consists of all packages
+that have a name equal to the dependency. If the dependency is of a relational
+type, the packages version must also fulfill the dependency.
+
+ Selection Selection_provides(int setflags = 0)
+ my $sel = $dep->Selection_provides();
+ sel = dep.Selection_provides()
+ sel = dep.Selection_provides()
+
+Create a Selection from a dependency. The selection consists of all packages
+that have at least one provides matching the dependency.
+
+ const char *str()
+ my $str = $dep->str();
+ str = $dep.str()
+ str = $dep.str()
+
+Return a string describing the dependency.
+
+ <stringification>
+ my $str = $dep->str;
+ str = str(dep)
+ str = dep.to_s
+
+Same as calling the str() method.
+
+ <equality>
+ if ($dep1 == $dep2)
+ if dep1 == dep2:
+ if dep1 == dep2
+
+The dependencies are equal if they are part of the same pool and have the same
+ids.
+
+
+The Repository Class
+--------------------
+A Repository describes a group of packages, normally coming from the same
+source. Repositories are created by the Pool's add_repo() method.
+
+=== ATTRIBUTES ===
+
+ Pool *pool; /* read only */
+ $repo->{pool}
+ repo.pool
+ repo.pool
+
+Back reference to the pool this dependency belongs to.
+
+ Id id; /* read only */
+ $repo->{id}
+ repo.id
+ repo.id
+
+The id of the repository.
+
+ const char *name; /* read/write */
+ $repo->{name}
+ repo.name
+ repo.name
+
+The repositories name. To libsolv, the name is just a string with no specific
+meaning.
+
+ int priority; /* read/write */
+ $repo->{priority}
+ repo.priority
+ repo.priority
+
+The priority of the repository. A higher number means that packages of this
+repository will be chosen over other repositories, even if they have a greater
+package version.
+
+ int subpriority; /* read/write */
+ $repo->{subpriority}
+ repo.subpriority
+ repo.subpriority
+
+The sub-priority of the repository. This value is compared when the priorities
+of two repositories are the same. It is useful to make the library prefer
+on-disk repositories to remote ones.
+
+ int nsolvables; /* read only */
+ $repo->{nsolvables}
+ repo.nsolvables
+ repo.nsolvables
+
+The number of solvables in this repository.
+
+ void *appdata; /* read/write */
+ $repo->{appdata}
+ repo.appdata
+ repo.appdata
+
+Application specific data that may be used in any way by the code using the
+repository.
+
+ Datapos *meta; /* read only */
+ $repo->{meta}
+ repo.meta
+ repo.meta
+
+Return a Datapos object of the repodata's metadata. You can use the lookup
+methods of the Datapos class to lookup metadata attributes, like the repository
+timestamp.
+
+=== CONSTANTS ===
+
+*REPO_REUSE_REPODATA*::
+Reuse the last repository data area (``repodata'') instead of creating a
+new one.
+
+*REPO_NO_INTERNALIZE*::
+Do not internalize the added repository data. This is useful if
+you plan to add more data because internalization is a costly
+operation.
+
+*REPO_LOCALPOOL*::
+Use the repodata's pool for Id storage instead of the global pool. Useful
+if you don't want to pollute the global pool with many unneeded ids, like
+when storing the filelist.
+
+*REPO_USE_LOADING*::
+Use the repodata that is currently being loaded instead of creating a new
+one. This only makes sense if used in a load callback.
+
+*REPO_EXTEND_SOLVABLES*::
+Do not create new solvables for the new data, but match existing solvables
+and add the data to them. Repository metadata is often split into multiple
+parts, with one primary file describing all packages and other parts
+holding information that is normally not needed, like the changelog.
+
+*REPO_USE_ROOTDIR*::
+Prepend the pool's rootdir to the path when doing file operations.
+
+*REPO_NO_LOCATION*::
+Do not add a location element to the solvables. Useful if the solvables
+are not in the final position, so you can add the correct location later
+in your code.
+
+*SOLV_ADD_NO_STUBS*::
+Do not create stubs for repository parts that can be downloaded on demand.
+
+*SUSETAGS_RECORD_SHARES*::
+This is specific to the add_susetags() method. Susetags allows one to refer to
+already read packages to save disk space. If this data sharing needs to
+work over multiple calls to add_susetags, you need to specify this flag so
+that the share information is made available to subsequent calls.
+
+=== METHODS ===
+
+ void free(bool reuseids = 0)
+ $repo->free();
+ repo.free()
+ repo.free()
+
+Free the repository and all solvables it contains. If _reuseids_ is set to
+true, the solvable ids and the repository id may be reused by the library when
+added new solvables. Thus you should leave it false if you are not sure that
+somebody holds a reference.
+
+ void empty(bool reuseids = 0)
+ $repo->empty();
+ repo.empty()
+ repo.empty()
+
+Free all the solvables in a repository. The repository will be empty after this
+call. See the free() method for the meaning of _reuseids_.
+
+ bool isempty()
+ $repo->isempty()
+ repo.empty()
+ repo.empty?
+
+Return true if there are no solvables in this repository.
+
+ void internalize()
+ $repo->internalize();
+ repo.internalize()
+ repo.internalize()
+
+Internalize added data. Data must be internalized before it is available to the
+lookup and data iterator functions.
+
+ bool write(FILE *fp)
+ $repo->write($fp)
+ repo.write(fp)
+ repo.write(fp)
+
+Write a repo as a ``solv'' file. These files can be read very fast and thus are
+a good way to cache repository data. Returns false if there was some error
+writing the file.
+
+ Solvableiterator solvables_iter()
+ for my $solvable (@{$repo->solvables_iter()})
+ for solvable in repo.solvables_iter():
+ for solvable in repo.solvables_iter()
+
+Iterate over all solvables in a repository.
+
+ Repodata add_repodata(int flags = 0)
+ my $repodata = $repo->add_repodata();
+ repodata = repo.add_repodata()
+ repodata = repo.add_repodata()
+
+Add a new repodata area to the repository. This is normally automatically
+done by the repo_add methods, so you need this method only in very
+rare circumstances.
+
+ void create_stubs()
+ $repo->create_stubs();
+ repo.create_stubs()
+ repo.create_stubs()
+
+Calls the create_stubs() repodata method for the last repodata of the
+repository.
+
+ bool iscontiguous()
+ $repo->iscontiguous()
+ repo.iscontiguous()
+ repo.iscontiguous?
+
+Return true if the solvables of this repository are all in a single block with
+no holes, i.e. they have consecutive ids.
+
+ Repodata first_repodata()
+ my $repodata = $repo->first_repodata();
+ repodata = repo.first_repodata()
+ repodata = repo.first_repodata()
+
+Checks if all repodatas but the first repodata are extensions, and return the
+first repodata if this is the case. Useful if you want to do a store/retrieve
+sequence on the repository to reduce the memory using and enable paging, as
+this does not work if the repository contains multiple non-extension repodata
+areas.
+
+ Selection Selection(int setflags = 0)
+ my $sel = $repo->Selection();
+ sel = repo.Selection()
+ sel = repo.Selection()
+
+Create a Selection consisting of all packages in the repository.
+
+ Dataiterator Dataiterator(Id key, const char *match = 0, int flags = 0)
+ my $di = $repo->Dataiterator($keyname, $match, $flags);
+ di = repo.Dataiterator(keyname, match, flags)
+ di = repo.Dataiterator(keyname, match, flags)
+
+ Dataiterator Dataiterator_meta(Id key, const char *match = 0, int flags = 0)
+ my $di = $repo->Dataiterator_meta($keyname, $match, $flags);
+ di = repo.Dataiterator_meta(keyname, match, flags)
+ di = repo.Dataiterator_meta(keyname, match, flags)
+
+ for my $d (@$di)
+ for d in di:
+ for d in di
+
+Iterate over the matching data elements in this repository. See the
+Dataiterator class for more information. The Dataiterator() method
+iterates over all solvables in a repository, whereas the Dataiterator_meta
+method only iterates over the repository's meta data.
+
+ <stringification>
+ my $str = $repo->str;
+ str = str(repo)
+ str = repo.to_s
+
+Return the name of the repository, or "Repo#<id>" if no name is set.
+
+ <equality>
+ if ($repo1 == $repo2)
+ if repo1 == repo2:
+ if repo1 == repo2
+
+Two repositories are equal if they belong to the same pool and have the same id.
+
+=== DATA ADD METHODS ===
+
+ Solvable add_solvable()
+ $repo->add_solvable();
+ repo.add_solvable()
+ repo.add_solvable()
+
+Add a single empty solvable to the repository. Returns a Solvable object, see
+the Solvable class for more information.
+
+ bool add_solv(const char *name, int flags = 0)
+ $repo->add_solv($name);
+ repo.add_solv(name)
+ repo.add_solv(name)
+
+ bool add_solv(FILE *fp, int flags = 0)
+ $repo->add_solv($fp);
+ repo.add_solv(fp)
+ repo.add_solv(fp)
+
+Read a ``solv'' file and add its contents to the repository. These files can be
+written with the write() method and are normally used as fast cache for
+repository metadata.
+
+ bool add_rpmdb(int flags = 0)
+ $repo->add_rpmdb();
+ repo.add_rpmdb()
+ repo.add_rpmdb()
+
+ bool add_rpmdb_reffp(FILE *reffp, int flags = 0)
+ $repo->add_rpmdb_reffp($reffp);
+ repo.add_rpmdb_reffp(reffp)
+ repo.add_rpmdb_reffp(reffp)
+
+Add the contents of the rpm database to the repository. If a solv file
+containing an old version of the database is available, it can be passed as
+reffp to speed up reading.
+
+ Solvable add_rpm(const char *filename, int flags = 0)
+ my $solvable = $repo->add_rpm($filename);
+ solvable = repo.add_rpm(filename)
+ solvable = repo.add_rpm(filename)
+
+Add the metadata of a single rpm package to the repository.
+
+ bool add_rpmdb_pubkeys(int flags = 0)
+ $repo->add_rpmdb_pubkeys();
+ repo.add_rpmdb_pubkeys()
+ repo.add_rpmdb_pubkeys()
+
+Add all pubkeys contained in the rpm database to the repository. Note that
+newer rpm versions also allow to store the pubkeys in some directory instead
+of the rpm database.
+
+ Solvable add_pubkey(const char *keyfile, int flags = 0)
+ my $solvable = $repo->add_pubkey($keyfile);
+ solvable = repo.add_pubkey(keyfile)
+ solvable = repo.add_pubkey(keyfile)
+
+Add a pubkey from a file to the repository.
+
+ bool add_rpmmd(FILE *fp, const char *language, int flags = 0)
+ $repo->add_rpmmd($fp, undef);
+ repo.add_rpmmd(fp, None)
+ repo.add_rpmmd(fp, nil)
+
+Add metadata stored in the "rpm-md" format (i.e. from files in the ``repodata''
+directory) to a repository. Supported files are "primary", "filelists",
+"other", "suseinfo". Do not forget to specify the *REPO_EXTEND_SOLVABLES* for
+extension files like "filelists" and "other". Use the _language_ parameter if
+you have language extension files, otherwise simply use a *undef*/*None*/*nil*
+parameter.
+
+ bool add_repomdxml(FILE *fp, int flags = 0)
+ $repo->add_repomdxml($fp);
+ repo.add_repomdxml(fp)
+ repo.add_repomdxml(fp)
+
+Add the repomd.xml meta description from the "rpm-md" format to the repository.
+This file contains information about the repository like keywords, and also a
+list of all database files with checksums. The data is added to the "meta"
+section of the repository, i.e. no package gets created.
+
+ bool add_updateinfoxml(FILE *fp, int flags = 0)
+ $repo->add_updateinfoxml($fp);
+ repo.add_updateinfoxml(fp)
+ repo.add_updateinfoxml(fp)
+
+Add the updateinfo.xml file containing available maintenance updates to the
+repository. All updates are created as special packages that have a "patch:"
+prefix in their name.
+
+ bool add_deltainfoxml(FILE *fp, int flags = 0)
+ $repo->add_deltainfoxml($fp);
+ repo.add_deltainfoxml(fp)
+ repo.add_deltainfoxml(fp)
+
+Add the deltainfo.xml file (also called prestodelta.xml) containing available
+delta-rpms to the repository. The data is added to the "meta" section, i.e. no
+package gets created.
+
+ bool add_debdb(int flags = 0)
+ $repo->add_debdb();
+ repo.add_debdb()
+ repo.add_debdb()
+
+Add the contents of the debian installed package database to the repository.
+
+ bool add_debpackages(FILE *fp, int flags = 0)
+ $repo->add_debpackages($fp);
+ repo.add_debpackages($fp)
+ repo.add_debpackages($fp)
+
+Add the contents of the debian repository metadata (the "packages" file)
+to the repository.
+
+ Solvable add_deb(const char *filename, int flags = 0)
+ my $solvable = $repo->add_deb($filename);
+ solvable = repo.add_deb(filename)
+ solvable = repo.add_deb(filename)
+
+Add the metadata of a single deb package to the repository.
+
+ bool add_mdk(FILE *fp, int flags = 0)
+ $repo->add_mdk($fp);
+ repo.add_mdk(fp)
+ repo.add_mdk(fp)
+
+Add the contents of the mageia/mandriva repository metadata (the
+"synthesis.hdlist" file) to the repository.
+
+ bool add_mdk_info(FILE *fp, int flags = 0)
+ $repo->add_mdk($fp);
+ repo.add_mdk(fp)
+ repo.add_mdk(fp)
+
+Extend the packages from the synthesis file with the info.xml and files.xml
+data. Do not forget to specify *REPO_EXTEND_SOLVABLES*.
+
+ bool add_arch_repo(FILE *fp, int flags = 0)
+ $repo->add_arch_repo($fp);
+ repo.add_arch_repo(fp)
+ repo.add_arch_repo(fp)
+
+Add the contents of the archlinux repository metadata (the ".db.tar" file) to
+the repository.
+
+ bool add_arch_local(const char *dir, int flags = 0)
+ $repo->add_arch_local($dir);
+ repo.add_arch_local(dir)
+ repo.add_arch_local(dir)
+
+Add the contents of the archlinux installed package database to the repository.
+The _dir_ parameter is usually set to "/var/lib/pacman/local".
+
+ bool add_content(FILE *fp, int flags = 0)
+ $repo->add_content($fp);
+ repo.add_content(fp)
+ repo.add_content(fp)
+
+Add the ``content'' meta description from the susetags format to the repository.
+This file contains information about the repository like keywords, and also
+a list of all database files with checksums. The data is added to the "meta"
+section of the repository, i.e. no package gets created.
+
+ bool add_susetags(FILE *fp, Id defvendor, const char *language, int flags = 0)
+ $repo->add_susetags($fp, $defvendor, $language);
+ repo.add_susetags(fp, defvendor, language)
+ repo.add_susetags(fp, defvendor, language)
+
+Add repository metadata in the susetags format to the repository. Like with
+add_rpmmd, you can specify a language if you have language extension files. The
+_defvendor_ parameter provides a default vendor for packages with missing
+vendors, it is usually provided in the content file.
+
+ bool add_products(const char *dir, int flags = 0)
+ $repo->add_products($dir);
+ repo.add_products(dir)
+ repo.add_products(dir)
+
+Add the installed SUSE products database to the repository. The _dir_ parameter
+is usually "/etc/products.d".
+
+
+The Solvable Class
+------------------
+A solvable describes all the information of one package. Each solvable
+belongs to one repository, it can be added and filled manually but in
+most cases solvables will get created by the repo_add methods.
+
+=== ATTRIBUTES ===
+
+ Repo *repo; /* read only */
+ $solvable->{repo}
+ solvable.repo
+ solvable.repo
+
+The repository this solvable belongs to.
+
+ Pool *pool; /* read only */
+ $solvable->{pool}
+ solvable.pool
+ solvable.pool
+
+The pool this solvable belongs to, same as the pool of the repo.
+
+ Id id; /* read only */
+ $solvable->{id}
+ solvable.id
+ solvable.id
+
+The specific id of the solvable.
+
+ char *name; /* read/write */
+ $solvable->{name}
+ solvable.name
+ solvable.name
+
+ char *evr; /* read/write */
+ $solvable->{evr}
+ solvable.evr
+ solvable.evr
+
+ char *arch; /* read/write */
+ $solvable->{arch}
+ solvable.arch
+ solvable.arch
+
+ char *vendor; /* read/write */
+ $solvable->{vendor}
+ solvable.vendor
+ solvable.vendor
+
+Easy access to often used attributes of solvables. They are
+internally stored as Ids.
+
+ Id nameid; /* read/write */
+ $solvable->{nameid}
+ solvable.nameid
+ solvable.nameid
+
+ Id evrid; /* read/write */
+ $solvable->{evrid}
+ solvable.evrid
+ solvable.evrid
+
+ Id archid; /* read/write */
+ $solvable->{archid}
+ solvable.archid
+ solvable.archid
+
+ Id vendorid; /* read/write */
+ $solvable->{vendorid}
+ solvable.vendorid
+ solvable.vendorid
+
+Raw interface to the ids. Useful if you want to search for
+a specific id and want to avoid the string compare overhead.
+
+=== METHODS ===
+
+ const char *lookup_str(Id keyname)
+ my $string = $solvable->lookup_str($keyname);
+ string = solvable.lookup_str(keyname)
+ string = solvable.lookup_str(keyname)
+
+ Id lookup_id(Id keyname)
+ my $id = $solvable->lookup_id($keyname);
+ id = solvable.lookup_id(solvid)
+ id = solvable.lookup_id(solvid)
+
+ unsigned long long lookup_num(Id solvid, Id keyname, unsigned long long notfound = 0)
+ my $num = $solvable->lookup_num($keyname);
+ num = solvable.lookup_num(keyname)
+ num = solvable.lookup_num(keyname)
+
+ bool lookup_void(Id keyname)
+ my $bool = $solvable->lookup_void($keyname);
+ bool = solvable.lookup_void(keyname)
+ bool = solvable.lookup_void(keyname)
+
+ Chksum lookup_checksum(Id keyname)
+ my $chksum = $solvable->lookup_checksum($keyname);
+ chksum = solvable.lookup_checksum(keyname)
+ chksum = solvable.lookup_checksum(keyname)
+
+ Id *lookup_idarray(Id keyname, Id marker = -1)
+ my @ids = $solvable->lookup_idarray($keyname);
+ ids = solvable.lookup_idarray(keyname)
+ ids = solvable.lookup_idarray(keyname)
+
+ Dep *lookup_deparray(Id keyname, Id marker = -1)
+ my @deps = $solvable->lookup_deparray($keyname);
+ deps = solvable.lookup_deparray(keyname)
+ deps = solvable.lookup_deparray(keyname)
+
+Generic lookup methods. Retrieve data stored for the specific keyname.
+The lookup_idarray() method will return an array of Ids, use
+lookup_deparray if you want an array of Dependency objects instead.
+Some Id arrays contain two parts of data divided by a specific marker,
+for example the provides array uses the SOLVABLE_FILEMARKER id to
+store both the ids provided by the package and the ids added by
+the addfileprovides method. The default, -1, translates to the
+correct marker for the keyname and returns the first part of the
+array, use 1 to select the second part or 0 to retrieve all ids
+including the marker.
+
+ const char *lookup_location(unsigned int *OUTPUT);
+ my ($location, $medianr) = $solvable->lookup_location();
+ location, medianr = solvable.lookup_location()
+ location, medianr = solvable.lookup_location()
+
+Return a tuple containing the on-media location and an optional
+media number for multi-part repositories (e.g. repositories
+spawning multiple DVDs).
+
+ Dataiterator Dataiterator(Id keyname, const char *match = 0, int flags = 0)
+ my $di = $solvable->Dataiterator($keyname, $match, $flags);
+ di = solvable.Dataiterator(keyname, match, flags)
+ di = solvable.Dataiterator(keyname, match, flags)
+
+ for my $d (@$di)
+ for d in di:
+ for d in di
+
+Iterate over the matching data elements. See the Dataiterator class for more
+information.
+
+ void add_deparray(Id keyname, DepId dep, Id marker = -1);
+ $solvable->add_deparray($keyname, $dep);
+ solvable.add_deparray(keyname, dep)
+ solvable.add_deparray(keyname, dep)
+
+Add a new dependency to the attributes stored in keyname.
+
+ void unset(Id keyname);
+ $solvable->unset($keyname);
+ solvable.unset(keyname)
+ solvable.unset(keyname)
+
+Delete data stored for the specific keyname.
+
+ bool installable();
+ $solvable->installable()
+ solvable.installable()
+ solvable.installable?
+
+Return true if the solvable is installable on the system. Solvables
+are not installable if the system does not support their architecture.
+
+ bool isinstalled();
+ $solvable->isinstalled()
+ solvable.isinstalled()
+ solvable.isinstalled?
+
+Return true if the solvable is installed on the system.
+
+ bool identical(Solvable *other)
+ $solvable->identical($other)
+ $solvable.identical(other)
+ $solvable.identical?(other)
+
+Return true if the two solvables are identical.
+
+ int evrcmp(Solvable *other)
+ $solvable->evrcmp(other)
+ $solvable.evrcmp(other)
+ $solvable.evrcmp(other)
+
+Returns -1 if the epoch/version/release of the solvable is less than the
+one from the other solvable, 1 if it is greater, and 0 if they are equal.
+Note that "equal" does not mean that the evr is identical.
+
+ Selection Selection(int setflags = 0)
+ my $sel = $solvable->Selection();
+ sel = solvable.Selection()
+ sel = solvable.Selection()
+
+Create a Selection containing just the single solvable.
+
+ const char *str()
+ my $str = $solvable->str();
+ str = $solvable.str()
+ str = $solvable.str()
+
+Return a string describing the solvable. The string consists of the name,
+version, and architecture of the Solvable.
+
+ <stringification>
+ my $str = $solvable->str;
+ str = str(solvable)
+ str = solvable.to_s
+
+Same as calling the str() method.
+
+ <equality>
+ if ($solvable1 == $solvable2)
+ if solvable1 == solvable2:
+ if solvable1 == solvable2
+
+Two solvables are equal if they are part of the same pool and have the same
+ids.
+
+
+The Dataiterator Class
+----------------------
+Dataiterators can be used to do complex string searches or
+to iterate over arrays. They can be created via the
+constructors in the Pool, Repo, and Solvable classes. The
+Repo and Solvable constructors will limit the search to
+the repository or the specific package.
+
+=== CONSTANTS ===
+
+*SEARCH_STRING*::
+Return a match if the search string matches the value.
+
+*SEARCH_STRINGSTART*::
+Return a match if the value starts with the search string.
+
+*SEARCH_STRINGEND*::
+Return a match if the value ends with the search string.
+
+*SEARCH_SUBSTRING*::
+Return a match if the search string can be matched somewhere in the value.
+
+*SEARCH_GLOB*::
+Do a glob match of the search string against the value.
+
+*SEARCH_REGEX*::
+Do a regular expression match of the search string against the value.
+
+*SEARCH_NOCASE*::
+Ignore case when matching strings. Works for all the above match types.
+
+*SEARCH_FILES*::
+Match the complete filenames of the file list, not just the base name.
+
+*SEARCH_COMPLETE_FILELIST*::
+When matching the file list, check every file of the package not just the
+subset from the primary metadata.
+
+*SEARCH_CHECKSUMS*::
+Allow the matching of checksum entries.
+
+=== METHODS ===
+
+ void prepend_keyname(Id keyname);
+ $di->prepend_keyname($keyname);
+ di.prepend_keyname(keyname)
+ di.prepend_keyname(keyname)
+
+Do a sub-search in the array stored in keyname.
+
+ void skip_solvable();
+ $di->kip_solvable();
+ di.skip_solvable()
+ di.skip_solvable()
+
+Stop matching the current solvable and advance to the next
+one.
+
+ <iteration>
+ for my $d (@$di)
+ for d in di:
+ for d in di
+
+Iterate through the matches. If there is a match, the object
+in d will be of type Datamatch.
+
+The Datamatch Class
+-------------------
+Objects of this type will be created for every value matched
+by a dataiterator.
+
+=== ATTRIBUTES ===
+
+ Pool *pool; /* read only */
+ $d->{pool}
+ d.pool
+ d.pool
+
+Back pointer to pool.
+
+ Repo *repo; /* read only */
+ $d->{repo}
+ d.repo
+ d.repo
+
+The repository containing the matched object.
+
+ Solvable *solvable; /* read only */
+ $d->{solvable}
+ d.solvable
+ d.solvable
+
+The solvable containing the value that was matched.
+
+ Id solvid; /* read only */
+ $d->{solvid}
+ d.solvid
+ d.solvid
+
+The id of the solvable that matched.
+
+ Id key_id;
+ $d->{key_id}
+ d.key_id
+ d.key_id
+
+ const char *key_idstr;
+ $d->{key_idstr}
+ d.key_idstr
+ d.key_idstr
+
+The keyname that matched, either as id or string.
+
+ Id type_id;
+ $d->{type_id}
+ d.type_id
+ d.type_id
+
+ const char *type_idstr;
+ $d->{type_idstr};
+ d.type_idstr
+ d.type_idstr
+
+The key type of the value that was matched, either as id or string.
+
+ Id id;
+ $d->{id}
+ d.id
+ d.id
+
+ Id idstr;
+ $d->{idstr}
+ d.idstr
+ d.idstr
+
+The Id of the value that was matched (only valid for id types),
+either as id or string.
+
+ const char *str;
+ $d->{str}
+ d.str
+ d.str
+
+The string value that was matched (only valid for string types).
+
+ unsigned long long num;
+ $d->{num}
+ d.num
+ d.num
+
+The numeric value that was matched (only valid for numeric types).
+
+ unsigned int num2;
+ $d->{num2}
+ d.num2
+ d.num2
+
+The secondary numeric value that was matched (only valid for types
+containing two values).
+
+ unsigned int binary;
+ $d->{binary}
+ d.binary
+ d.binary
+
+The value in binary form, useful for checksums and other data
+that cannot be represented as a string.
+
+=== METHODS ===
+
+ Datapos pos();
+ my $pos = $d->pos();
+ pos = d.pos()
+ pos = d.pos()
+
+The position object of the current match. It can be used to do
+sub-searches starting at the match (if it is of an array type).
+See the Datapos class for more information.
+
+ Datapos parentpos();
+ my $pos = $d->parentpos();
+ pos = d.parentpos()
+ pos = d.parentpos()
+
+The position object of the array containing the current match.
+It can be used to do sub-searches, see the Datapos class for more
+information.
+
+ <stringification>
+ my $str = $d->str;
+ str = str(d)
+ str = d.to_s
+
+Return the stringification of the matched value. Stringification
+depends on the search flags, for file list entries it will return
+just the base name unless SEARCH_FILES is used, for checksums
+it will return an empty string unless SEARCH_CHECKSUMS is used.
+Numeric values are currently stringified to an empty string.
+
+
+The Selection Class
+-------------------
+Selections are a way to easily deal with sets of packages.
+There are multiple constructors to create them, the most useful
+is probably the select() method in the Pool class.
+
+=== CONSTANTS ===
+
+*SELECTION_NAME*::
+Create the selection by matching package names.
+
+*SELECTION_PROVIDES*::
+Create the selection by matching package provides.
+
+*SELECTION_FILELIST*::
+Create the selection by matching package files.
+
+*SELECTION_CANON*::
+Create the selection by matching the canonical representation
+of the package. This is normally a combination of the name,
+the version, and the architecture of a package.
+
+*SELECTION_DOTARCH*::
+Allow an ``.<architecture>'' suffix when matching names or
+provides.
+
+*SELECTION_REL*::
+Allow the specification of a relation when matching names
+or provides, e.g. "name >= 1.2".
+
+*SELECTION_INSTALLED_ONLY*::
+Limit the package search to installed packages.
+
+*SELECTION_SOURCE_ONLY*::
+Limit the package search to source packages only.
+
+*SELECTION_WITH_SOURCE*::
+Extend the package search to also match source packages. The default is
+only to match binary packages.
+
+*SELECTION_GLOB*::
+Allow glob matching for package names, package provides, and file names.
+
+*SELECTION_NOCASE*::
+Ignore case when matching package names, package provides, and file names.
+
+*SELECTION_FLAT*::
+Return only one selection element describing the selected packages.
+The default is to create multiple elements for all globbed packages.
+Multiple elements are useful if you want to turn the selection into
+an install job, in that case you want an install job for every
+globbed package.
+
+=== ATTRIBUTES ===
+
+ Pool *pool; /* read only */
+ $d->{pool}
+ d.pool
+ d.pool
+
+Back pointer to pool.
+
+=== METHODS ===
+
+ int flags();
+ my $flags = $sel->flags();
+ flags = sel.flags()
+ flags = sel.flags()
+
+Return the result flags of the selection. The flags are a subset
+of the ones used when creating the selection, they describe which
+method was used to get the result. For example, if you create the
+selection with ``SELECTION_NAME | SELECTION_PROVIDES'', the resulting
+flags will either be SELECTION_NAME or SELECTION_PROVIDES depending
+if there was a package that matched the name or not. If there was
+no match at all, the flags will be zero.
+
+ bool isempty();
+ $sel->isempty()
+ sel.isempty()
+ sel.isempty?
+
+Return true if the selection is empty, i.e. no package could be matched.
+
+ void filter(Selection *other)
+ $sel->filter($other);
+ sel.filter(other)
+ sel.filter(other)
+
+Intersect two selections. Packages will only stay in the selection if there
+are also included in the other selecting. Does an in-place modification.
+
+ void add(Selection *other)
+ $sel->add($other);
+ sel.add(other)
+ sel.add(other)
+
+Build the union of two selections. All packages of the other selection will
+be added to the set of packages of the selection object. Does an in-place
+modification. Note that the selection flags are no longer meaningful after the
+add operation.
+
+ void add_raw(Id how, Id what)
+ $sel->add_raw($how, $what);
+ sel.add_raw(how, what)
+ sel.add_raw(how, what)
+
+Add a raw element to the selection. Check the Job class for information about
+the how and what parameters.
+
+ Job *jobs(int action)
+ my @jobs = $sel->jobs($action);
+ jobs = sel.jobs(action)
+ jobs = sel.jobs(action)
+
+Convert a selection into an array of Job objects. The action parameter is or-ed
+to the ``how'' part of the job, it describes the type of job (e.g. install,
+erase). See the Job class for the action and action modifier constants.
+
+ Solvable *solvables()
+ my @solvables = $sel->solvables();
+ solvables = sel.solvables()
+ solvables = sel.solvables()
+
+Convert a selection into an array of Solvable objects.
+
+ <stringification>
+ my $str = $sel->str;
+ str = str(sel)
+ str = sel.to_s
+
+Return a string describing the selection.
+
+The Job Class
+-------------
+Jobs are the way to specify to the dependency solver what to do.
+Most of the times jobs will get created by calling the jobs() method
+on a Selection object, but there is also a Job() constructor in the
+Pool class.
+
+=== CONSTANTS ===
+
+Selection constants:
+
+*SOLVER_SOLVABLE*::
+The ``what'' part is the id of a solvable.
+
+*SOLVER_SOLVABLE_NAME*::
+The ``what'' part is the id of a package name.
+
+*SOLVER_SOLVABLE_PROVIDES*::
+The ``what'' part is the id of a package provides.
+
+*SOLVER_SOLVABLE_ONE_OF*::
+The ``what'' part is an offset into the ``whatprovides'' data, created
+by calling the towhatprovides() pool method.
+
+*SOLVER_SOLVABLE_REPO*::
+The ``what'' part is the id of a repository.
+
+*SOLVER_SOLVABLE_ALL*::
+The ``what'' part is ignored, all packages are selected.
+
+*SOLVER_SOLVABLE_SELECTMASK*::
+A mask containing all the above selection bits.
+
+Action constants:
+
+*SOLVER_NOOP*::
+Do nothing.
+
+*SOLVER_INSTALL*::
+Install a package of the specified set of packages. It tries to install
+the best matching package (i.e. the highest version of the packages from
+the repositories with the highest priority).
+
+*SOLVER_ERASE*::
+Erase all of the packages from the specified set. If a package is not
+installed, erasing it will keep it from getting installed.
+
+*SOLVER_UPDATE*::
+Update the matching installed packages to their best version. If none
+of the specified packages are installed, try to update the installed
+packages to the specified versions. See the section about targeted
+updates about more information.
+
+*SOLVER_WEAKENDEPS*::
+Allow to break the dependencies of the matching packages. Handle with care.
+
+*SOLVER_MULTIVERSION*::
+Mark the matched packages for multiversion install. If they get to be
+installed because of some other job, the installation will keep the old
+version of the package installed (for rpm this is done by using ``-i''
+instead of ``-U'').
+
+*SOLVER_LOCK*::
+Do not change the state of the matched packages, i.e. when they are
+installed they stay installed, if not they are not selected for
+installation.
+
+*SOLVER_DISTUPGRADE*::
+Update the matching installed packages to the best version included in one
+of the repositories. After this operation, all come from one of the available
+repositories except orphaned packages. Orphaned packages are packages that
+have no relation to the packages in the repositories, i.e. no package in the
+repositories have the same name or obsolete the orphaned package.
+This action brings the installed packages in sync with the ones in the
+repository. By default it also turns of arch/vendor/version locking for the
+affected packages to simulate a fresh installation. This means that distupgrade can
+actually downgrade packages if only lower versions of a package are available
+in the repositories. You can tweak this behavior with the SOLVER_FLAG_DUP_
+solver flags.
+
+*SOLVER_DROP_ORPHANED*::
+Erase all the matching installed packages if they are orphaned. This only makes
+sense if there is a ``distupgrade all packages'' job. The default is to erase
+orphaned packages only if they block the installation of other packages.
+
+*SOLVER_VERIFY*::
+Fix dependency problems of matching installed packages. The default is to ignore
+dependency problems for installed packages.
+
+*SOLVER_USERINSTALLED*::
+The matching installed packages are considered to be installed by a user,
+thus not installed to fulfill some dependency. This is needed input for
+the calculation of unneeded packages for jobs that have the
+SOLVER_CLEANDEPS flag set.
+
+*SOLVER_ALLOWUNINSTALL*::
+Allow the solver to deinstall the matching installed packages if they get
+into the way of resolving a dependency. This is like the
+SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages.
+
+*SOLVER_JOBMASK*::
+A mask containing all the above action bits.
+
+Action modifier constants:
+
+*SOLVER_WEAK*::
+Makes the job a weak job. The solver tries to fulfill weak jobs, but does
+not report a problem if it is not possible to do so.
+
+*SOLVER_ESSENTIAL*::
+Makes the job an essential job. If there is a problem with the job, the
+solver will not propose to remove the job as one solution (unless all
+other solutions are also to remove essential jobs).
+
+*SOLVER_CLEANDEPS*::
+The solver will try to also erase all packages dragged in through
+dependencies when erasing the package. This needs SOLVER_USERINSTALLED
+jobs to maximize user satisfaction.
+
+*SOLVER_FORCEBEST*::
+Insist on the best package for install, update, and distupgrade jobs. If
+this flag is not used, the solver will use the second-best package if the
+best package cannot be installed for some reason. When this flag is used,
+the solver will generate a problem instead.
+
+*SOLVER_TARGETED*::
+Forces targeted operation update and distupgrade jobs. See the section
+about targeted updates about more information.
+
+Set constants.
+
+*SOLVER_SETEV*::
+The job specified the exact epoch and version of the package set.
+
+*SOLVER_SETEVR*::
+The job specified the exact epoch, version, and release of the package set.
+
+*SOLVER_SETARCH*::
+The job specified the exact architecture of the packages from the set.
+
+*SOLVER_SETVENDOR*::
+The job specified the exact vendor of the packages from the set.
+
+*SOLVER_SETREPO*::
+The job specified the exact repository of the packages from the set.
+
+*SOLVER_SETNAME*::
+The job specified the exact name of the packages from the set.
+
+*SOLVER_NOAUTOSET*::
+Turn of automatic set flag generation for SOLVER_SOLVABLE jobs.
+
+*SOLVER_SETMASK*::
+A mask containing all the above set bits.
+
+See the section about set bits for more information.
+
+=== ATTRIBUTES ===
+
+ Pool *pool; /* read only */
+ $job->{pool}
+ d.pool
+ d.pool
+
+Back pointer to pool.
+
+ Id how; /* read/write */
+ $job->{how}
+ d.how
+ d.how
+
+Union of the selection, action, action modifier, and set flags.
+The selection part describes the semantics of the ``what'' Id.
+
+ Id what; /* read/write */
+ $job->{what}
+ d.what
+ d.what
+
+Id describing the set of packages, the meaning depends on the
+selection part of the ``how'' attribute.
+
+=== METHODS ===
+
+ Solvable *solvables()
+ my @solvables = $job->solvables();
+ solvables = job.solvables()
+ solvables = job.solvables()
+
+Return the set of solvables of the job as an array of Solvable
+objects.
+
+ bool isemptyupdate();
+ $job->isemptyupdate()
+ job.isemptyupdate()
+ job.isemptyupdate?
+
+Convenience function to find out if the job describes an update
+job with no matching packages, i.e. a job that does nothing.
+Some package managers like ``zypper'' like to turn those jobs
+into install jobs, i.e. an update of a not-installed package
+will result into the installation of the package.
+
+ <stringification>
+ my $str = $job->str;
+ str = str(job)
+ str = job.to_s
+
+Return a string describing the job.
+
+ <equality>
+ if ($job1 == $job2)
+ if job1 == job2:
+ if job1 == job2
+
+Two jobs are equal if they belong to the same pool and both the
+``how'' and the ``what'' attributes are the same.
+
+=== TARGETED UPDATES ===
+Libsolv has two modes for upgrades and distupgrade: targeted and
+untargeted. Untargeted mode means that the installed packages from
+the specified set will be updated to the best version. Targeted means
+that packages that can be updated to a package in the specified set
+will be updated to the best package of the set.
+
+Here's an example to explain the subtle difference. Suppose that
+you have package A installed in version "1.1", "A-1.2" is available
+in one of the repositories and there is also package "B" that
+obsoletes package A.
+
+An untargeted update of "A" will update the installed "A-1.1" to
+package "B", because that is the newest version (B obsoletes A and
+is thus newer).
+
+A targeted update of "A" will update "A-1.1" to "A-1.2", as the
+set of packages contains both "A-1.1" and "A-1.2", and "A-1.2" is
+the newer one.
+
+An untargeted update of "B" will do nothing, as "B" is not installed.
+
+An targeted update of "B" will update "A-1.1" to "B".
+
+Note that the default is to do "auto-targeting", thus if the specified
+set of packages does not include an installed package, the solver
+will assume targeted operation even if SOLVER_TARGETED is not used.
+
+This mostly matches the intent of the user, with one exception: In
+the example above, an update of "A-1.2" will update "A-1.1" to
+"A-1.2" (targeted mode), but a second update of "A-1.2" will suddenly
+update to "B", as untargeted mode is chosen because "A-1.2" is now
+installed.
+
+If you want to have full control over when targeting mode is chosen,
+turn off auto-targeting with the SOLVER_FLAG_NO_AUTOTARGET solver option.
+In that case, all updates are considered to be untargeted unless they
+include the SOLVER_TARGETED flag.
+
+=== SET BITS ===
+Set bits specify which parts of the specified packages where specified
+by the user. It is used by the solver when checking if an operation is
+allowed or not. For example, the solver will normally not allow the
+downgrade of an installed package. But it will not report a problem if
+the SOLVER_SETEVR flag is used, as it then assumes that the user specified
+the exact version and thus knows what he is doing.
+
+So if a package "screen-1-1" is installed for the x86_64 architecture and
+version "2-1" is only available for the i586 architecture, installing
+package "screen-2.1" will ask the user for confirmation because of the
+different architecture. When using the Selection class to create jobs
+the set bits are automatically added, e.g. selecting ``screen.i586'' will
+automatically add SOLVER_SETARCH, and thus no problem will be reported.
+
+The Solver Class
+----------------
+Dependency solving is what this library is about. A solver object is needed
+for solving to store the result of the solver run. The solver object can be
+used multiple times for different jobs, reusing it allows the solver to
+re-use the dependency rules it already computed.
+
+=== CONSTANTS ===
+
+Flags to modify some of the solver's behavior:
+
+*SOLVER_FLAG_ALLOW_DOWNGRADE*::
+Allow the solver to downgrade packages without asking for confirmation
+(i.e. reporting a problem).
+
+*SOLVER_FLAG_ALLOW_ARCHCHANGE*::
+Allow the solver to change the architecture of an installed package
+without asking for confirmation. Note that changes to/from noarch
+are always considered to be allowed.
+
+*SOLVER_FLAG_ALLOW_VENDORCHANGE*::
+Allow the solver to change the vendor of an installed package
+without asking for confirmation. Each vendor is part of one or more
+vendor equivalence classes, normally installed packages may only
+change their vendor if the new vendor shares at least one equivalence
+class.
+
+*SOLVER_FLAG_ALLOW_NAMECHANGE*::
+Allow the solver to change the name of an installed package, i.e.
+install a package with a different name that obsoletes the installed
+package. This option is on by default.
+
+*SOLVER_FLAG_ALLOW_UNINSTALL*::
+Allow the solver to erase installed packages to fulfill the jobs.
+This flag also includes the above flags. You may want to set this
+flag if you only have SOLVER_ERASE jobs, as in that case it's
+better for the user to check the transaction overview instead of
+approving every single package that needs to be erased.
+
+*SOLVER_FLAG_DUP_ALLOW_DOWNGRADE*::
+Like SOLVER_FLAG_ALLOW_DOWNGRADE, but used in distupgrade mode.
+
+*SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE*::
+Like SOLVER_FLAG_ALLOW_ARCHCHANGE, but used in distupgrade mode.
+
+*SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE*::
+Like SOLVER_FLAG_ALLOW_VENDORCHANGE, but used in distupgrade mode.
+
+*SOLVER_FLAG_DUP_ALLOW_NAMECHANGE*::
+Like SOLVER_FLAG_ALLOW_NAMECHANGE, but used in distupgrade mode.
+
+*SOLVER_FLAG_NO_UPDATEPROVIDE*::
+If multiple packages obsolete an installed package, the solver checks
+the provides of every such package and ignores all packages that
+do not provide the installed package name. Thus, you can have an
+official update candidate that provides the old name, and other
+packages that also obsolete the package but are not considered for
+updating. If you cannot use this feature, you can turn it off
+by setting this flag.
+
+*SOLVER_FLAG_SPLITPROVIDES*::
+Make the solver aware of special provides of the form
+``<packagename>:<path>'' used in SUSE systems to support package
+splits.
+
+*SOLVER_FLAG_IGNORE_RECOMMENDED*::
+Do not process optional (aka weak) dependencies.
+
+*SOLVER_FLAG_ADD_ALREADY_RECOMMENDED*::
+Install recommended or supplemented packages even if they have no
+connection to the current transaction. You can use this feature
+to implement a simple way for the user to install new recommended
+packages that were not available in the past.
+
+*SOLVER_FLAG_NO_INFARCHCHECK*::
+Turn off the inferior architecture checking that is normally done
+by the solver. Normally, the solver allows only the installation
+of packages from the "best" architecture if a package is available
+for multiple architectures.
+
+*SOLVER_FLAG_BEST_OBEY_POLICY*::
+Make the SOLVER_FORCEBEST job option consider only packages that
+meet the policies for installed packages, i.e. no downgrades,
+no architecture change, no vendor change (see the first flags
+of this section). If the flag is not specified, the solver will
+enforce the installation of the best package ignoring the
+installed packages, which may conflict with the set policy.
+
+*SOLVER_FLAG_NO_AUTOTARGET*::
+Do not enable auto-targeting up update and distupgrade jobs. See
+the section on targeted updates for more information.
+
+*SOLVER_FLAG_KEEP_ORPHANS*::
+Do not allow orphaned packages to be deinstalled if they get
+in the way of resolving other packages.
+
+*SOLVER_FLAG_BREAK_ORPHANS*::
+Ignore dependencies of orphaned packages that get in the way
+of resolving non-orphaned ones. Setting the flag might result
+in no longer working packages in case they are orphaned.
+
+*SOLVER_FLAG_FOCUS_INSTALLED*::
+Resolve installed packages before resolving the given job.
+Setting this flag means that the solver will prefer picking
+a package version that fits the other installed packages
+over updating installed packages.
+
+Basic rule types:
+
+*SOLVER_RULE_UNKNOWN*::
+A rule of an unknown class. You should never encounter those.
+
+*SOLVER_RULE_PKG*::
+A package dependency rule.
+
+*SOLVER_RULE_UPDATE*::
+A rule to implement the update policy of installed packages. Every
+installed package has an update rule that consists of the packages
+that may replace the installed package.
+
+*SOLVER_RULE_FEATURE*::
+Feature rules are fallback rules used when an update rule is disabled. They
+include all packages that may replace the installed package ignoring the
+update policy, i.e. they contain downgrades, arch changes and so on.
+Without them, the solver would simply erase installed packages if their
+update rule gets disabled.
+
+*SOLVER_RULE_JOB*::
+Job rules implement the job given to the solver.
+
+*SOLVER_RULE_DISTUPGRADE*::
+These are simple negative assertions that make sure that only packages
+are kept that are also available in one of the repositories.
+
+*SOLVER_RULE_INFARCH*::
+Infarch rules are also negative assertions, they disallow the installation
+of packages when there are packages of the same name but with a better
+architecture.
+
+*SOLVER_RULE_CHOICE*::
+Choice rules are used to make sure that the solver prefers updating to
+installing different packages when some dependency is provided by
+multiple packages with different names. The solver may always break
+choice rules, so you will not see them when a problem is found.
+
+*SOLVER_RULE_LEARNT*::
+These rules are generated by the solver to keep it from running into
+the same problem multiple times when it has to backtrack. They are
+the main reason why a sat solver is faster than other dependency solver
+implementations.
+
+Special dependency rule types:
+
+*SOLVER_RULE_PKG_NOT_INSTALLABLE*::
+This rule was added to prevent the installation of a package of an
+architecture that does not work on the system.
+
+*SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP*::
+The package contains a required dependency which was not provided by
+any package.
+
+*SOLVER_RULE_PKG_REQUIRES*::
+Similar to SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP, but in this case
+some packages provided the dependency but none of them could be
+installed due to other dependency issues.
+
+*SOLVER_RULE_PKG_SELF_CONFLICT*::
+The package conflicts with itself. This is not allowed by older rpm
+versions.
+
+*SOLVER_RULE_PKG_CONFLICTS*::
+To fulfill the dependencies two packages need to be installed, but
+one of the packages contains a conflict with the other one.
+
+*SOLVER_RULE_PKG_SAME_NAME*::
+The dependencies can only be fulfilled by multiple versions of
+a package, but installing multiple versions of the same package
+is not allowed.
+
+*SOLVER_RULE_PKG_OBSOLETES*::
+To fulfill the dependencies two packages need to be installed, but
+one of the packages obsoletes the other one.
+
+*SOLVER_RULE_PKG_IMPLICIT_OBSOLETES*::
+To fulfill the dependencies two packages need to be installed, but
+one of the packages has provides a dependency that is obsoleted
+by the other one. See the POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES
+flag.
+
+*SOLVER_RULE_PKG_INSTALLED_OBSOLETES*::
+To fulfill the dependencies a package needs to be installed that is
+obsoleted by an installed package. See the POOL_FLAG_NOINSTALLEDOBSOLETES
+flag.
+
+*SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP*::
+The user asked for installation of a package providing a specific
+dependency, but no available package provides it.
+
+*SOLVER_RULE_JOB_UNKNOWN_PACKAGE*::
+The user asked for installation of a package with a specific name,
+but no available package has that name.
+
+*SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM*::
+The user asked for the erasure of a dependency that is provided by the
+system (i.e. for special hardware or language dependencies), this
+cannot be done with a job.
+
+*SOLVER_RULE_JOB_UNSUPPORTED*::
+The user asked for something that is not yet implemented, e.g. the
+installation of all packages at once.
+
+Policy error constants
+
+*POLICY_ILLEGAL_DOWNGRADE*::
+The solver ask for permission before downgrading packages.
+
+*POLICY_ILLEGAL_ARCHCHANGE*::
+The solver ask for permission before changing the architecture of installed
+packages.
+
+*POLICY_ILLEGAL_VENDORCHANGE*::
+The solver ask for permission before changing the vendor of installed
+packages.
+
+*POLICY_ILLEGAL_NAMECHANGE*::
+The solver ask for permission before replacing an installed packages with
+a package that has a different name.
+
+Solution element type constants
+
+*SOLVER_SOLUTION_JOB*::
+The problem can be solved by removing the specified job.
+
+*SOLVER_SOLUTION_POOLJOB*::
+The problem can be solved by removing the specified job that is defined
+in the pool.
+
+*SOLVER_SOLUTION_INFARCH*::
+The problem can be solved by allowing the installation of the specified
+package with an inferior architecture.
+
+*SOLVER_SOLUTION_DISTUPGRADE*::
+The problem can be solved by allowing to keep the specified package
+installed.
+
+*SOLVER_SOLUTION_BEST*::
+The problem can be solved by allowing to install the specified package
+that is not the best available package.
+
+*SOLVER_SOLUTION_ERASE*::
+The problem can be solved by allowing to erase the specified package.
+
+*SOLVER_SOLUTION_REPLACE*::
+The problem can be solved by allowing to replace the package with some
+other package.
+
+*SOLVER_SOLUTION_REPLACE_DOWNGRADE*::
+The problem can be solved by allowing to replace the package with some
+other package that has a lower version.
+
+*SOLVER_SOLUTION_REPLACE_ARCHCHANGE*::
+The problem can be solved by allowing to replace the package with some
+other package that has a different architecture.
+
+*SOLVER_SOLUTION_REPLACE_VENDORCHANGE*::
+The problem can be solved by allowing to replace the package with some
+other package that has a different vendor.
+
+*SOLVER_SOLUTION_REPLACE_NAMECHANGE*::
+The problem can be solved by allowing to replace the package with some
+other package that has a different name.
+
+
+Reason constants
+
+*SOLVER_REASON_UNRELATED*::
+The package status did not change as it was not related to any job.
+
+*SOLVER_REASON_UNIT_RULE*::
+The package was installed/erased/kept because of a unit rule, i.e. a rule
+where all literals but one were false.
+
+*SOLVER_REASON_KEEP_INSTALLED*::
+The package was chosen when trying to keep as many packages installed as
+possible.
+
+*SOLVER_REASON_RESOLVE_JOB*::
+The decision happened to fulfill a job rule.
+
+*SOLVER_REASON_UPDATE_INSTALLED*::
+The decision happened to fulfill a package update request.
+
+*SOLVER_REASON_CLEANDEPS_ERASE*::
+The package was erased when cleaning up dependencies from other erased
+packages.
+
+*SOLVER_REASON_RESOLVE*::
+The package was installed to fulfill package dependencies.
+
+*SOLVER_REASON_WEAKDEP*::
+The package was installed because of a weak dependency (Recommends or
+Supplements).
+
+*SOLVER_REASON_RESOLVE_ORPHAN*::
+The decision about the package was made when deciding the fate of orphaned
+packages.
+
+*SOLVER_REASON_RECOMMENDED*::
+This is a special case of SOLVER_REASON_WEAKDEP.
+
+*SOLVER_REASON_SUPPLEMENTED*::
+This is a special case of SOLVER_REASON_WEAKDEP.
+
+
+=== ATTRIBUTES ===
+
+ Pool *pool; /* read only */
+ $job->{pool}
+ d.pool
+ d.pool
+
+Back pointer to pool.
+
+=== METHODS ===
+
+ int set_flag(int flag, int value)
+ my $oldvalue = $solver->set_flag($flag, $value);
+ oldvalue = solver.set_flag(flag, value)
+ oldvalue = solver.set_flag(flag, value)
+
+ int get_flag(int flag)
+ my $value = $solver->get_flag($flag);
+ value = solver.get_flag(flag)
+ value = solver.get_flag(flag)
+
+Set/get a solver specific flag. The flags define the policies the solver has
+to obey. The flags are explained in the CONSTANTS section of this class.
+
+ Problem *solve(Job *jobs)
+ my @problems = $solver->solve(\@jobs);
+ problems = solver.solve(jobs)
+ problems = solver.solve(jobs)
+
+Solve a problem specified in the job list (plus the jobs defined in the pool).
+Returns an array of problems that need user interaction, or an empty array
+if no problems were encountered. See the Problem class on how to deal with
+problems.
+
+ Transaction transaction()
+ my $trans = $solver->transaction();
+ trans = solver.transaction()
+ trans = solver.transaction()
+
+Return the transaction to implement the calculated package changes. A transaction
+is available even if problems were found, this is useful for interactive user
+interfaces that show both the job result and the problems.
+
+ int reason = describe_decision(Solvable *s, Rule *OUTPUT)
+ my ($reason, $rule) = $solver->describe_decision($solvable);
+ (reason, rule) = solver.describe_decision(solvable)
+ (reason, rule) = solver.describe_decision(solvable)
+
+Return the reason why a specific solvable was installed or erased. For most of
+the reasons the rule that triggered the decision is also returned.
+
+The Problem Class
+-----------------
+Problems are the way of the solver to interact with the user. You can simply list
+all problems and terminate your program, but a better way is to present solutions to
+the user and let him pick the ones he likes.
+
+=== ATTRIBUTES ===
+
+ Solver *solv; /* read only */
+ $problem->{solv}
+ problem.solv
+ problem.solv
+
+Back pointer to solver object.
+
+ Id id; /* read only */
+ $problem->{id}
+ problem.id
+ problem.id
+
+Id of the problem. The first problem has Id 1, they are numbered consecutively.
+
+=== METHODS ===
+
+ Rule findproblemrule()
+ my $probrule = $problem->findproblemrule();
+ probrule = problem.findproblemrule()
+ probrule = problem.findproblemrule()
+
+Return the rule that caused the problem. Of course in most situations there is no
+single responsible rule, but many rules that interconnect with each created the
+problem. Nevertheless, the solver uses some heuristic approach to find a rule
+that somewhat describes the problem best to the user.
+
+ Rule *findallproblemrules(bool unfiltered = 0)
+ my @probrules = $problem->findallproblemrules();
+ probrules = problem.findallproblemrule()
+ probrules = problem.findallproblemrule()
+
+Return all rules responsible for the problem. The returned set of rules contains
+all the needed information why there was a problem, but it's hard to present
+them to the user in a sensible way. The default is to filter out all update and
+job rules (unless the returned rules only consist of those types).
+
+ Solution *solutions()
+ my @solutions = $problem->solutions();
+ solutions = problem.solutions()
+ solutions = problem.solutions()
+
+Return an array containing multiple possible solutions to fix the problem. See
+the solution class for more information.
+
+ int solution_count()
+ my $cnt = $problem->solution_count();
+ cnt = problem.solution_count()
+ cnt = problem.solution_count()
+
+Return the number of solutions without creating solution objects.
+
+ <stringification>
+ my $str = $problem->str;
+ str = str(problem)
+ str = problem.to_s
+
+Return a string describing the problem. This is a convenience function, it is
+a shorthand for calling findproblemrule(), then ruleinfo() on the problem
+rule and problemstr() on the ruleinfo object.
+
+The Rule Class
+--------------
+Rules are the basic block of sat solving. Each package dependency gets translated
+into one or multiple rules.
+
+=== ATTRIBUTES ===
+
+ Solver *solv; /* read only */
+ $rule->{solv}
+ rule.solv
+ rule.solv
+
+Back pointer to solver object.
+
+ Id id; /* read only */
+ $rule->{id}
+ rule.id
+ rule.id
+
+The id of the rule.
+
+ int type; /* read only */
+ $rule->{type}
+ rule.type
+ rule.type
+
+The basic type of the rule. See the constant section of the solver class for the type list.
+
+=== METHODS ===
+
+ Ruleinfo info()
+ my $ruleinfo = $rule->info();
+ ruleinfo = rule.info()
+ ruleinfo = rule.info()
+
+Return a Ruleinfo object that contains information about why the rule was created. But
+see the allinfos() method below.
+
+ Ruleinfo *allinfos()
+ my @ruleinfos = $rule->allinfos();
+ ruleinfos = rule.allinfos()
+ ruleinfos = rule.allinfos()
+
+As the same dependency rule can get created because of multiple dependencies, one
+Ruleinfo is not enough to describe the reason. Thus the allinfos() method returns
+an array of all infos about a rule.
+
+ <equality>
+ if ($rule1 == $rule2)
+ if rule1 == rule2:
+ if rule1 == rule2
+
+Two rules are equal if they belong to the same solver and have the same id.
+
+The Ruleinfo Class
+------------------
+A Ruleinfo describes one reason why a rule was created.
+
+=== ATTRIBUTES ===
+
+ Solver *solv; /* read only */
+ $ruleinfo->{solv}
+ ruleinfo.solv
+ ruleinfo.solv
+
+Back pointer to solver object.
+
+ int type; /* read only */
+ $ruleinfo->{type}
+ ruleinfo.type
+ ruleinfo.type
+
+The type of the ruleinfo. See the constant section of the solver class for the
+rule type list and the special type list.
+
+ Dep *dep; /* read only */
+ $ruleinfo->{dep}
+ ruleinfo.dep
+ ruleinfo.dep
+
+The dependency leading to the creation of the rule.
+
+ Dep *dep_id; /* read only */
+ $ruleinfo->{'dep_id'}
+ ruleinfo.dep_id
+ ruleinfo.dep_id
+
+The Id of the dependency leading to the creation of the rule, or zero.
+
+ Solvable *solvable; /* read only */
+ $ruleinfo->{solvable}
+ ruleinfo.solvable
+ ruleinfo.solvable
+
+The involved Solvable, e.g. the one containing the dependency.
+
+ Solvable *othersolvable; /* read only */
+ $ruleinfo->{othersolvable}
+ ruleinfo.othersolvable
+ ruleinfo.othersolvable
+
+The other involved Solvable (if any), e.g. the one containing providing
+the dependency for conflicts.
+
+ const char *problemstr();
+ my $str = $ruleinfo->problemstr();
+ str = ruleinfo.problemstr()
+ str = ruleinfo.problemstr()
+
+A string describing the ruleinfo from a problem perspective. This probably
+only makes sense if the rule is part of a problem.
+
+The Solution Class
+------------------
+A solution solves one specific problem. It consists of multiple solution elements
+that all need to be executed.
+
+=== ATTRIBUTES ===
+
+ Solver *solv; /* read only */
+ $solution->{solv}
+ solution.solv
+ solution.solv
+
+Back pointer to solver object.
+
+ Id problemid; /* read only */
+ $solution->{problemid}
+ solution.problemid
+ solution.problemid
+
+Id of the problem the solution solves.
+
+ Id id; /* read only */
+ $solution->{id}
+ solution.id
+ solution.id
+
+Id of the solution. The first solution has Id 1, they are numbered consecutively.
+
+=== METHODS ===
+
+ Solutionelement *elements(bool expandreplaces = 0)
+ my @solutionelements = $solution->elements();
+ solutionelements = solution.elements()
+ solutionelements = solution.elements()
+
+Return an array containing the elements describing what needs to be done to
+implement the specific solution. If expandreplaces is true, elements of type
+SOLVER_SOLUTION_REPLACE will be replaced by one or more elements replace
+elements describing the policy mismatches.
+
+ int element_count()
+ my $cnt = $solution->solution_count();
+ cnt = solution.element_count()
+ cnt = solution.element_count()
+
+Return the number of solution elements without creating objects. Note that the
+count does not match the number of objects returned by the elements() method
+of expandreplaces is set to true.
+
+
+The Solutionelement Class
+-------------------------
+A solution element describes a single action of a solution. The action is always
+either to remove one specific job or to add a new job that installs or erases
+a single specific package.
+
+=== ATTRIBUTES ===
+
+ Solver *solv; /* read only */
+ $solutionelement->{solv}
+ solutionelement.solv
+ solutionelement.solv
+
+Back pointer to solver object.
+
+ Id problemid; /* read only */
+ $solutionelement->{problemid}
+ solutionelement.problemid
+ solutionelement.problemid
+
+Id of the problem the element (partly) solves.
+
+ Id solutionid; /* read only */
+ $solutionelement->{solutionid}
+ solutionelement.solutionid
+ solutionelement.solutionid
+
+Id of the solution the element is a part of.
+
+ Id id; /* read only */
+ $solutionelement->{id}
+ solutionelement.id
+ solutionelement.id
+
+Id of the solution element. The first element has Id 1, they are numbered consecutively.
+
+ Id type; /* read only */
+ $solutionelement->{type}
+ solutionelement.type
+ solutionelement.type
+
+Type of the solution element. See the constant section of the solver class for the
+existing types.
+
+ Solvable *solvable; /* read only */
+ $solutionelement->{solvable}
+ solutionelement.solvable
+ solutionelement.solvable
+
+The installed solvable that needs to be replaced for replacement elements.
+
+ Solvable *replacement; /* read only */
+ $solutionelement->{replacement}
+ solutionelement.replacement
+ solutionelement.replacement
+
+The solvable that needs to be installed to fix the problem.
+
+ int jobidx; /* read only */
+ $solutionelement->{jobidx}
+ solutionelement.jobidx
+ solutionelement.jobidx
+
+The index of the job that needs to be removed to fix the problem, or -1 if the
+element is of another type. Note that it's better to change the job to SOLVER_NOOP
+type so that the numbering of other elements does not get disturbed. This
+method works both for types SOLVER_SOLUTION_JOB and SOLVER_SOLUTION_POOLJOB.
+
+=== METHODS ===
+
+ Solutionelement *replaceelements()
+ my @solutionelements = $solutionelement->replaceelements();
+ solutionelements = solutionelement.replaceelements()
+ solutionelements = solutionelement.replaceelements()
+
+If the solution element is of type SOLVER_SOLUTION_REPLACE, return an array of
+elements describing the policy mismatches, otherwise return a copy of the
+element. See also the ``expandreplaces'' option in the solution's elements()
+method.
+
+ int illegalreplace()
+ my $illegal = $solutionelement->illegalreplace();
+ illegal = solutionelement.illegalreplace()
+ illegal = solutionelement.illegalreplace()
+
+Return an integer that contains the policy mismatch bits or-ed together, or
+zero if there was no policy mismatch. See the policy error constants in
+the solver class.
+
+ Job Job()
+ my $job = $solutionelement->Job();
+ illegal = solutionelement.Job()
+ illegal = solutionelement.Job()
+
+Create a job that implements the solution element. Add this job to the array
+of jobs for all elements of type different to SOLVER_SOLUTION_JOB and
+SOLVER_SOLUTION_POOLJOB. For the later two, a SOLVER_NOOB Job is created,
+you should replace the old job with the new one.
+
+ const char *str()
+ my $str = $solutionelement->str();
+ str = solutionelement.str()
+ str = solutionelement.str()
+
+A string describing the change the solution element consists of.
+
+The Transaction Class
+---------------------
+Transactions describe the output of a solver run. A transaction contains
+a number of transaction elements, each either the installation of a new
+package or the removal of an already installed package. The Transaction
+class supports a classify() method that puts the elements into different
+groups so that a transaction can be presented to the user in a meaningful
+way.
+
+=== CONSTANTS ===
+
+Transaction element types, both active and passive
+
+*SOLVER_TRANSACTION_IGNORE*::
+This element does nothing. Used to map element types that do not match
+the view mode.
+
+*SOLVER_TRANSACTION_INSTALL*::
+This element installs a package.
+
+*SOLVER_TRANSACTION_ERASE*::
+This element erases a package.
+
+*SOLVER_TRANSACTION_MULTIINSTALL*::
+This element installs a package with a different version keeping the other
+versions installed.
+
+*SOLVER_TRANSACTION_MULTIREINSTALL*::
+This element reinstalls an installed package keeping the other versions
+installed.
+
+Transaction element types, active view
+
+*SOLVER_TRANSACTION_REINSTALL*::
+This element re-installs a package, i.e. installs the same package again.
+
+*SOLVER_TRANSACTION_CHANGE*::
+This element installs a package with same name, version, architecture but
+different content.
+
+*SOLVER_TRANSACTION_UPGRADE*::
+This element installs a newer version of an installed package.
+
+*SOLVER_TRANSACTION_DOWNGRADE*::
+This element installs an older version of an installed package.
+
+*SOLVER_TRANSACTION_OBSOLETES*::
+This element installs a package that obsoletes an installed package.
+
+Transaction element types, passive view
+
+*SOLVER_TRANSACTION_REINSTALLED*::
+This element re-installs a package, i.e. installs the same package again.
+
+*SOLVER_TRANSACTION_CHANGED*::
+This element replaces an installed package with one of the same name,
+version, architecture but different content.
+
+*SOLVER_TRANSACTION_UPGRADED*::
+This element replaces an installed package with a new version.
+
+*SOLVER_TRANSACTION_DOWNGRADED*::
+This element replaces an installed package with an old version.
+
+*SOLVER_TRANSACTION_OBSOLETED*::
+This element replaces an installed package with a package that obsoletes
+it.
+
+Pseudo element types for showing extra information used by classify()
+
+*SOLVER_TRANSACTION_ARCHCHANGE*::
+This element replaces an installed package with a package of a different
+architecture.
+
+*SOLVER_TRANSACTION_VENDORCHANGE*::
+This element replaces an installed package with a package of a different
+vendor.
+
+Transaction mode flags
+
+*SOLVER_TRANSACTION_SHOW_ACTIVE*::
+Filter for active view types. The default is to return passive view type,
+i.e. to show how the installed packages get changed.
+
+*SOLVER_TRANSACTION_SHOW_OBSOLETES*::
+Do not map the obsolete view type into INSTALL/ERASE elements.
+
+*SOLVER_TRANSACTION_SHOW_ALL*::
+If multiple packages replace an installed package, only the best of them
+is kept as OBSOLETE element, the other ones are mapped to INSTALL/ERASE
+elements. This is because most applications want to show just one package
+replacing the installed one. The SOLVER_TRANSACTION_SHOW_ALL makes the
+library keep all OBSOLETE elements.
+
+*SOLVER_TRANSACTION_SHOW_MULTIINSTALL*::
+The library maps MULTIINSTALL elements to simple INSTALL elements. This
+flag can be used to disable the mapping.
+
+*SOLVER_TRANSACTION_CHANGE_IS_REINSTALL*::
+Use this flag if you want to map CHANGE elements to the REINSTALL type.
+
+*SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE*::
+Use this flag if you want to map OBSOLETE elements to the UPGRADE type.
+
+*SOLVER_TRANSACTION_MERGE_ARCHCHANGES*::
+Do not add extra categories for every architecture change, instead cumulate
+them in one category.
+
+*SOLVER_TRANSACTION_MERGE_VENDORCHANGES*::
+Do not add extra categories for every vendor change, instead cumulate
+them in one category.
+
+*SOLVER_TRANSACTION_RPM_ONLY*::
+Special view mode that just returns IGNORE, ERASE, INSTALL, MULTIINSTALL
+elements. Useful if you want to find out what to feed to the underlying
+package manager.
+
+Transaction order flags
+
+*SOLVER_TRANSACTION_KEEP_ORDERDATA*::
+Do not throw away the dependency graph used for ordering the transaction.
+This flag is needed if you want to do manual ordering.
+
+=== ATTRIBUTES ===
+
+ Pool *pool; /* read only */
+ $trans->{pool}
+ trans.pool
+ trans.pool
+
+Back pointer to pool.
+
+=== METHODS ===
+
+ bool isempty();
+ $trans->isempty()
+ trans.isempty()
+ trans.isempty?
+
+Returns true if the transaction does not do anything, i.e. has no elements.
+
+ Solvable *newsolvables();
+ my @newsolvables = $trans->newsolvables();
+ newsolvables = trans.newsolvables()
+ newsolvables = trans.newsolvables()
+
+Return all packages that are to be installed by the transaction. These are
+the packages that need to be downloaded from the repositories.
+
+ Solvable *keptsolvables();
+ my @keptsolvables = $trans->keptsolvables();
+ keptsolvables = trans.keptsolvables()
+ keptsolvables = trans.keptsolvables()
+
+Return all installed packages that the transaction will keep installed.
+
+ Solvable *steps();
+ my @steps = $trans->steps();
+ steps = trans.steps()
+ steps = trans.steps()
+
+Return all solvables that need to be installed (if the returned solvable
+is not already installed) or erased (if the returned solvable is installed).
+A step is also called a transaction element.
+
+ int steptype(Solvable *solvable, int mode)
+ my $type = $trans->steptype($solvable, $mode);
+ type = trans.steptype(solvable, mode)
+ type = trans.steptype(solvable, mode)
+
+Return the transaction type of the specified solvable. See the CONSTANTS
+sections for the mode argument flags and the list of returned types.
+
+ TransactionClass *classify(int mode = 0)
+ my @classes = $trans->classify();
+ classes = trans.classify()
+ classes = trans.classify()
+
+Group the transaction elements into classes so that they can be displayed
+in a structured way. You can use various mapping mode flags to tweak
+the result to match your preferences, see the mode argument flag in
+the CONSTANTS section. See the TransactionClass class for how to deal
+with the returned objects.
+
+ Solvable othersolvable(Solvable *solvable);
+ my $other = $trans->othersolvable($solvable);
+ other = trans.othersolvable(solvable)
+ other = trans.othersolvable(solvable)
+
+Return the ``other'' solvable for a given solvable. For installed packages
+the other solvable is the best package with the same name that replaces
+the installed package, or the best package of the obsoleting packages if
+the package does not get replaced by one with the same name.
+
+For to be installed packages, the ``other'' solvable is the best installed
+package with the same name that will be replaced, or the best packages
+of all the packages that are obsoleted if the new package does not replace
+a package with the same name.
+
+Thus, the ``other'' solvable is normally the package that is also shown
+for a given package.
+
+ Solvable *allothersolvables(Solvable *solvable);
+ my @others = $trans->allothersolvables($solvable);
+ others = trans.allothersolvables(solvable)
+ others = trans.allothersolvables(solvable)
+
+For installed packages, returns all of the packages that replace us. For to
+be installed packages, returns all of the packages that the new package
+replaces. The special ``other'' solvable is always the first entry of the
+returned array.
+
+ int calc_installsizechange();
+ my $change = $trans->calc_installsizechange();
+ change = trans.calc_installsizechange()
+ change = trans.calc_installsizechange()
+
+Return the size change of the installed system in kilobytes (kibibytes).
+
+ void order(int flags = 0);
+ $trans->order();
+ trans.order()
+ trans.order()
+
+Order the steps in the transactions so that dependent packages are updated
+before packages that depend on them. For rpm, you can also use rpmlib's
+ordering functionality, debian's dpkg does not provide a way to order a
+transaction.
+
+=== ACTIVE/PASSIVE VIEW ===
+
+Active view lists what new packages get installed, while passive view shows
+what happens to the installed packages. Most often there's not much
+difference between the two modes, but things get interesting if multiple
+packages get replaced by one new package. Say you have installed packages
+A-1-1 and B-1-1, and now install A-2-1 which has a new dependency that
+obsoletes B. The transaction elements will be
+
+ updated A-1-1 (other: A-2-1)
+ obsoleted B-1-1 (other: A-2-1)
+
+in passive mode, but
+
+ update A-2-1 (other: A-1-1)
+ erase B
+
+in active mode. If the mode contains SOLVER_TRANSACTION_SHOW_ALL, the
+passive mode list will be unchanged but the active mode list will just
+contain A-2-1.
+
+The Transactionclass Class
+--------------------------
+Objects of this type are returned by the classify() Transaction method.
+
+=== ATTRIBUTES ===
+
+ Transaction *transaction; /* read only */
+ $class->{transaction}
+ class.transaction
+ class.transaction
+
+Back pointer to transaction object.
+
+ int type; /* read only */
+ $class->{type}
+ class.type
+ class.type
+
+The type of the transaction elements in the class.
+
+ int count; /* read only */
+ $class->{count}
+ class.count
+ class.count
+
+The number of elements in the class.
+
+ const char *fromstr;
+ $class->{fromstr}
+ class.fromstr
+ class.fromstr
+
+The old vendor or architecture.
+
+ const char *tostr;
+ $class->{tostr}
+ class.tostr
+ class.tostr
+
+The new vendor or architecture.
+
+ Id fromid;
+ $class->{fromid}
+ class.fromid
+ class.fromid
+
+The id of the old vendor or architecture.
+
+ Id toid;
+ $class->{toid}
+ class.toid
+ class.toid
+
+The id of the new vendor or architecture.
+
+=== METHODS ===
+
+ void solvables();
+ my @solvables = $class->solvables();
+ solvables = class.solvables()
+ solvables = class.solvables()
+
+Return the solvables for all transaction elements in the class.
+
+Checksums
+---------
+Checksums (also called hashes) are used to make sure that downloaded data is
+not corrupt and also as a fingerprint mechanism to check if data has changed.
+
+=== CLASS METHODS ===
+
+ Chksum Chksum(Id type)
+ my $chksum = solv::Chksum->new($type);
+ chksum = solv.Chksum(type)
+ chksum = Solv::Chksum.new(type)
+
+Create a checksum object. Currently the following types are supported:
+
+ REPOKEY_TYPE_MD5
+ REPOKEY_TYPE_SHA1
+ REPOKEY_TYPE_SHA256
+
+These keys are constants in the *solv* class.
+
+ Chksum Chksum(Id type, const char *hex)
+ my $chksum = solv::Chksum->new($type, $hex);
+ chksum = solv.Chksum(type, hex)
+ chksum = Solv::Chksum.new(type, hex)
+
+Create an already finalized checksum object from a hex string.
+
+ Chksum Chksum_from_bin(Id type, char *bin)
+ my $chksum = solv::Chksum->from_bin($type, $bin);
+ chksum = solv.Chksum.from_bin(type, bin)
+ chksum = Solv::Chksum.from_bin(type, bin)
+
+Create an already finalized checksum object from a binary checksum.
+
+=== ATTRIBUTES ===
+
+ Id type; /* read only */
+ $chksum->{type}
+ chksum.type
+ chksum.type
+
+Return the type of the checksum object.
+
+=== METHODS ===
+
+ void add(const char *str)
+ $chksum->add($str);
+ chksum.add(str)
+ chksum.add(str)
+
+Add a (binary) string to the checksum.
+
+ void add_fp(FILE *fp)
+ $chksum->add_fp($file);
+ chksum.add_fp(file)
+ chksum.add_fp(file)
+
+Add the contents of a file to the checksum.
+
+ void add_stat(const char *filename)
+ $chksum->add_stat($filename);
+ chksum.add_stat(filename)
+ chksum.add_stat(filename)
+
+Stat the file and add the dev/ino/size/mtime member to the checksum. If the
+stat fails, the members are zeroed.
+
+ void add_fstat(int fd)
+ $chksum->add_fstat($fd);
+ chksum.add_fstat(fd)
+ chksum.add_fstat(fd)
+
+Same as add_stat, but instead of the filename a file descriptor is used.
+
+ unsigned char *raw()
+ my $raw = $chksum->raw();
+ raw = chksum.raw()
+ raw = chksum.raw()
+
+Finalize the checksum and return the result as raw bytes. This means that the
+result can contain NUL bytes or unprintable characters.
+
+ const char *hex()
+ my $raw = $chksum->hex();
+ raw = chksum.hex()
+ raw = chksum.hex()
+
+Finalize the checksum and return the result as hex string.
+
+ const char *typestr()
+ my $typestr = $chksum->typestr();
+ typestr = chksum.typestr
+ typestr = chksum.typestr
+
+Return the type of the checksum as a string, e.g. "sha256".
+
+ <equality>
+ if ($chksum1 == $chksum2)
+ if chksum1 == chksum2:
+ if chksum1 == chksum2
+
+Checksums are equal if they are of the same type and the finalized results are
+the same.
+
+ <stringification>
+ my $str = $chksum->str;
+ str = str(chksum)
+ str = chksum.to_s
+
+If the checksum is finished, the checksum is returned as "<type>:<hex>" string.
+Otherwise "<type>:unfinished" is returned.
+
+
+File Management
+---------------
+This functions were added because libsolv uses standard *FILE* pointers to
+read/write files, but languages like perl have their own implementation of
+files. The libsolv functions also support decompression and compression, the
+algorithm is selected by looking at the file name extension.
+
+ FILE *xfopen(char *fn, char *mode = "r")
+ my $file = solv::xfopen($path);
+ file = solv.xfopen(path)
+ file = Solv::xfopen(path)
+
+Open a file at the specified path. The `mode` argument is passed on to the
+stdio library.
+
+ FILE *xfopen_fd(char *fn, int fileno)
+ my $file = solv::xfopen_fd($path, $fileno);
+ file = solv.xfopen_fd(path, fileno)
+ file = Solv::xfopen_fd(path, fileno)
+
+Create a file handle from the specified file descriptor. The path argument is
+only used to select the correct (de-)compression algorithm, use an empty path
+if you want to make sure to read/write raw data. The file descriptor is dup()ed
+before the file handle is created.
+
+=== METHODS ===
+
+ int fileno()
+ my $fileno = $file->fileno();
+ fileno = file.fileno()
+ fileno = file.fileno()
+
+Return file file descriptor of the file. If the file is not open, `-1` is
+returned.
+
+ void cloexec(bool state)
+ $file->cloexec($state)
+ file.cloexec(state)
+ file.cloexec(state)
+
+Set the close-on-exec flag of the file descriptor. The xfopen function
+returns files with close-on-exec turned on, so if you want to pass
+a file to some other process you need to call cloexec(0) before calling
+exec.
+
+ int dup()
+ my $fileno = $file->dup();
+ fileno = file.dup()
+ fileno = file.dup()
+
+Return a copy of the descriptor of the file. If the file is not open, `-1` is
+returned.
+
+ bool flush()
+ $file->flush();
+ file.flush()
+ file.flush()
+
+Flush the file. Returns false if there was an error. Flushing a closed file
+always returns true.
+
+ bool close()
+ $file->close();
+ file.close()
+ file.close()
+
+Close the file. This is needed for languages like Ruby that do not destruct
+objects right after they are no longer referenced. In that case, it is good
+style to close open files so that the file descriptors are freed right away.
+Returns false if there was an error.
+
+
+The Repodata Class
+------------------
+The Repodata stores attributes for packages and the repository itself, each
+repository can have multiple repodata areas. You normally only need to
+directly access them if you implement lazy downloading of repository data.
+Repodata areas are created by calling the repository's add_repodata() method
+or by using repo_add methods without the REPO_REUSE_REPODATA or REPO_USE_LOADING
+flag.
+
+=== ATTRIBUTES ===
+
+ Repo *repo; /* read only */
+ $data->{repo}
+ data.repo
+ data.repo
+
+Back pointer to repository object.
+
+ Id id; /* read only */
+ $data->{id}
+ data.id
+ data.id
+
+The id of the repodata area. Repodata ids of different repositories overlap.
+
+=== METHODS ===
+
+ internalize();
+ $data->internalize();
+ data.internalize()
+ data.internalize()
+
+Internalize newly added data. The lookup functions will only see the new data
+after it has been internalized.
+
+ bool write(FILE *fp);
+ $data->write($fp);
+ data.write(fp)
+ data.write(fp)
+
+Write the contents of the repodata area as solv file.
+
+ bool add_solv(FILE *fp, int flags = 0);
+ $data->add_solv($fp);
+ data.add_solv(fp)
+ data.add_solv(fp)
+
+Replace a stub repodata object with the data from a solv file. This method
+automatically adds the REPO_USE_LOADING flag. It should only be used from
+a load callback.
+
+ void create_stubs();
+ $data->create_stubs()
+ data.create_stubs()
+ data.create_stubs()
+
+Create stub repodatas from the information stored in the repodata meta
+area.
+
+ void extend_to_repo();
+ $data->extend_to_repo();
+ data.extend_to_repo()
+ data.extend_to_repo()
+
+Extend the repodata so that it has the same size as the repo it belongs to.
+This method is only needed when switching to a just written repodata extension
+to make the repodata match the written extension (which is always of the
+size of the repo).
+
+ <equality>
+ if ($data1 == $data2)
+ if data1 == data2:
+ if data1 == data2
+
+Two repodata objects are equal if they belong to the same repository and have
+the same id.
+
+=== DATA RETRIEVAL METHODS ===
+
+ const char *lookup_str(Id solvid, Id keyname)
+ my $string = $data->lookup_str($solvid, $keyname);
+ string = data.lookup_str(solvid, keyname)
+ string = data.lookup_str(solvid, keyname)
+
+ Id *lookup_idarray(Id solvid, Id keyname)
+ my @ids = $data->lookup_idarray($solvid, $keyname);
+ ids = data.lookup_idarray(solvid, keyname)
+ ids = data.lookup_idarray(solvid, keyname)
+
+ Chksum lookup_checksum(Id solvid, Id keyname)
+ my $chksum = $data->lookup_checksum($solvid, $keyname);
+ chksum = data.lookup_checksum(solvid, keyname)
+ chksum = data.lookup_checksum(solvid, keyname)
+
+Lookup functions. Return the data element stored in the specified solvable.
+The methods probably only make sense to retrieve data from the special
+SOLVID_META solvid that stores repodata meta information.
+
+=== DATA STORAGE METHODS ===
+
+ void set_id(Id solvid, Id keyname, DepId id);
+ $data->set_id($solvid, $keyname, $id);
+ data.set_id(solvid, keyname, id)
+ data.set_id(solvid, keyname, id)
+
+ void set_str(Id solvid, Id keyname, const char *str);
+ $data->set_str($solvid, $keyname, $str);
+ data.set_str(solvid, keyname, str)
+ data.set_str(solvid, keyname, str)
+
+ void set_poolstr(Id solvid, Id keyname, const char *str);
+ $data->set_poolstr($solvid, $keyname, $str);
+ data.set_poolstr(solvid, keyname, str)
+ data.set_poolstr(solvid, keyname, str)
+
+ void set_checksum(Id solvid, Id keyname, Chksum *chksum);
+ $data->set_checksum($solvid, $keyname, $chksum);
+ data.set_checksum(solvid, keyname, chksum)
+ data.set_checksum(solvid, keyname, chksum)
+
+ void add_idarray(Id solvid, Id keyname, DepId id);
+ $data->add_idarray($solvid, $keyname, $id);
+ data.add_idarray(solvid, keyname, id)
+ data.add_idarray(solvid, keyname, id)
+
+ Id new_handle();
+ my $handle = $data->new_handle();
+ handle = data.new_handle()
+ handle = data.new_handle()
+
+ void add_flexarray(Id solvid, Id keyname, Id handle);
+ $data->add_flexarray($solvid, $keyname, $handle);
+ data.add_flexarray(solvid, keyname, handle)
+ data.add_flexarray(solvid, keyname, handle)
+
+Data storage methods. Probably only useful to store data in the special
+SOLVID_META solvid that stores repodata meta information. Note that
+repodata areas can have their own Id pool (see the REPO_LOCALPOOL flag),
+so be careful if you need to store ids. Arrays are created by calling
+the add function for every element. A flexarray is an array of
+sub-structures, call new_handle to create a new structure, use the
+handle as solvid to fill the structure with data and call add_flexarray
+to put the structure in an array.
+
+
+The Datapos Class
+-----------------
+Datapos objects describe a specific position in the repository data area.
+Thus they are only valid until the repository is modified in some way.
+Datapos objects can be created by the pos() and parentpos() methods of
+a Datamatch object or by accessing the ``meta'' attribute of a repository.
+
+=== ATTRIBUTES ===
+
+ Repo *repo; /* read only */
+ $data->{repo}
+ data.repo
+ data.repo
+
+Back pointer to repository object.
+
+=== METHODS ===
+
+ Dataiterator(Id keyname, const char *match, int flags)
+ my $di = $datapos->Dataiterator($keyname, $match, $flags);
+ di = datapos.Dataiterator(keyname, match, flags)
+ di = datapos.Dataiterator(keyname, match, flags)
+
+Create a Dataiterator at the position of the datapos object.
+
+ const char *lookup_deltalocation(unsigned int *OUTPUT);
+ my ($location, $medianr) = $datapos->lookup_deltalocation();
+ location, medianr = datapos.lookup_deltalocation()
+ location, medianr = datapos.lookup_deltalocation()
+
+Return a tuple containing the on-media location and an optional media number
+for a delta rpm. This obviously only works if the data position points to
+structure describing a delta rpm.
+
+ const char *lookup_deltaseq();
+ my $seq = $datapos->lookup_deltaseq();
+ seq = datapos.lookup_deltaseq();
+ seq = datapos.lookup_deltaseq();
+
+Return the delta rpm sequence from the structure describing a delta rpm.
+
+=== DATA RETRIEVAL METHODS ===
+
+ const char *lookup_str(Id keyname)
+ my $string = $datapos->lookup_str($keyname);
+ string = datapos.lookup_str(keyname)
+ string = datapos.lookup_str(keyname)
+
+ Id lookup_id(Id solvid, Id keyname)
+ my $id = $datapos->lookup_id($keyname);
+ id = datapos.lookup_id(keyname)
+ id = datapos.lookup_id(keyname)
+
+ unsigned long long lookup_num(Id keyname, unsigned long long notfound = 0)
+ my $num = $datapos->lookup_num($keyname);
+ num = datapos.lookup_num(keyname)
+ num = datapos.lookup_num(keyname)
+
+ bool lookup_void(Id keyname)
+ my $bool = $datapos->lookup_void($keyname);
+ bool = datapos.lookup_void(keyname)
+ bool = datapos.lookup_void(keyname)
+
+ Id *lookup_idarray(Id keyname)
+ my @ids = $datapos->lookup_idarray($keyname);
+ ids = datapos.lookup_idarray(keyname)
+ ids = datapos.lookup_idarray(keyname)
+
+ Chksum lookup_checksum(Id keyname)
+ my $chksum = $datapos->lookup_checksum($keyname);
+ chksum = datapos.lookup_checksum(keyname)
+ chksum = datapos.lookup_checksum(keyname)
+
+Lookup functions. Note that the returned Ids are always translated into
+the Ids of the global pool even if the repodata area contains its own pool.
+
+ Dataiterator Dataiterator(Id keyname, const char *match = 0, int flags = 0)
+ my $di = $datapos->Dataiterator($keyname, $match, $flags);
+ di = datapos.Dataiterator(keyname, match, flags)
+ di = datapos.Dataiterator(keyname, match, flags)
+
+ for my $d (@$di)
+ for d in di:
+ for d in di
+
+Iterate over the matching data elements. See the Dataiterator class for more
+information.
+
+Author
+------
+Michael Schroeder <mls@suse.de>
+
--- /dev/null
+'\" t
+.\" Title: Libsolv-Constantids
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 12/14/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "LIBSOLV\-CONSTANTIDS" "3" "12/14/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+libsolv-constantids \- fixed Ids for often used strings
+.SH "DESCRIPTION"
+.sp
+Constant Ids are Ids of strings that are often needed\&. They are defined to ease programming and reduce the number of pool_str2id calls\&. The constant Ids are part of the binary ABI of libsolv, a minor version update will only add new constants and not change existing Ids to maintain compatible\&. The on\-disk solv format works does not use the fixed Ids, but instead references the strings, so solv files can still be read when the ABI is broken\&.
+.SH "SPECIAL STRINGS"
+.PP
+\fBID_EMPTY ""\fR
+.RS 4
+The empty string\&. It will always have Id 1\&.
+.RE
+.PP
+\fBSYSTEM_SYSTEM "system:system"\fR
+.RS 4
+The name of the always installed "system" solvable\&.
+.RE
+.SH "SOLVABLE ATTRIBUTES"
+.sp
+These are Ids for keyname of attributes\&. They can be used in the lookup and storage functions to select the correct attribute in the solvable\&. The descriptions below describe the intended semantics of the values stored in the attribute with the keyname\&.
+.PP
+\fBSOLVABLE_NAME "solvable:name"\fR
+.RS 4
+The name of the package\&.
+.RE
+.PP
+\fBSOLVABLE_ARCH "solvable:arch"\fR
+.RS 4
+The architecture of the package\&. See the Solvable Architecture section for predefined architecture Id values\&.
+.RE
+.PP
+\fBSOLVABLE_EVR "solvable:evr"\fR
+.RS 4
+The version of the package\&. It usually consists of some combination of the Epoch, the Version, and the Release of the solvable\&.
+.RE
+.PP
+\fBSOLVABLE_VENDOR "solvable:vendor"\fR
+.RS 4
+A vendor string\&. Usually the company or group that created the binary package\&.
+.RE
+.PP
+\fBSOLVABLE_PROVIDES "solvable:provides"\fR
+.RS 4
+Stores an array of dependency Ids that describe the capabilities that the package provides\&.
+.RE
+.PP
+\fBSOLVABLE_OBSOLETES "solvable:obsoletes"\fR
+.RS 4
+Stores an array of dependency Ids that describe the packages that this package replaces\&.
+.RE
+.PP
+\fBSOLVABLE_CONFLICTS "solvable:conflicts"\fR
+.RS 4
+Stores an array of dependency Ids that describe the capabilities that this package conflicts with, i\&.e\&. that can\(cqt be installed together with this package\&.
+.RE
+.PP
+\fBSOLVABLE_REQUIRES "solvable:requires"\fR
+.RS 4
+Stores an array of dependency Ids that describe the capabilities that also must be installed when this package is installed\&.
+.RE
+.PP
+\fBSOLVABLE_RECOMMENDS "solvable:recommends"\fR
+.RS 4
+Stores an array of dependency Ids that describe the capabilities that also should be installed when this package is installed\&. It\(cqs not an error if not all capabilities can be met\&.
+.RE
+.PP
+\fBSOLVABLE_SUGGESTS "solvable:suggests"\fR
+.RS 4
+Stores an array of dependency Ids that describe the capabilities that also useful to have installed when this package is installed\&. This is intended to provide a hint to the user about other packages\&.
+.RE
+.PP
+\fBSOLVABLE_SUPPLEMENTS "solvable:supplements"\fR
+.RS 4
+Stores an array of dependency Ids that define that this package should be installed if one of the capabilities is met\&. This is like the recommends attribute, but works in the reverse way\&.
+.RE
+.PP
+\fBSOLVABLE_ENHANCES "solvable:enhances"\fR
+.RS 4
+Stores an array of dependency Ids that define that this package is useful to have installed if one of the capabilities is met\&. This is like the suggests attribute, but works in the reverse way\&.
+.RE
+.PP
+\fBSOLVABLE_SUMMARY "solvable:summary"\fR
+.RS 4
+The summary should be a short string without any newlines that describes what a package does\&.
+.RE
+.PP
+\fBSOLVABLE_DESCRIPTION "solvable:description"\fR
+.RS 4
+The description should be a more verbose description about what a package does\&. It may consist of multiple lines\&.
+.RE
+.PP
+\fBSOLVABLE_DISTRIBUTION "solvable:distribution"\fR
+.RS 4
+The distribution is a short string that describes the OS and OS version this package is built for\&.
+.RE
+.PP
+\fBSOLVABLE_AUTHORS "solvable:authors"\fR
+.RS 4
+A list of authors of this package\&. This attribute was used in SUSE packages\&.
+.RE
+.PP
+\fBSOLVABLE_PACKAGER "solvable:packager"\fR
+.RS 4
+The person who created the binary package, see also the vendor attribute\&.
+.RE
+.PP
+\fBSOLVABLE_GROUP "solvable:group"\fR
+.RS 4
+The package group that this package belongs to\&. See also the keywords attribute\&.
+.RE
+.PP
+\fBSOLVABLE_URL "solvable:url"\fR
+.RS 4
+An URL that points to more information about the package\&.
+.RE
+.PP
+\fBSOLVABLE_KEYWORDS "solvable:keywords"\fR
+.RS 4
+list of keyword string IDs used for tagging this package\&.
+.RE
+.PP
+\fBSOLVABLE_LICENSE "solvable:license"\fR
+.RS 4
+The license(s) of this package\&.
+.RE
+.PP
+\fBSOLVABLE_BUILDTIME "solvable:buildtime"\fR
+.RS 4
+The seconds since the unix epoch when the binary package was created\&.
+.RE
+.PP
+\fBSOLVABLE_BUILDHOST "solvable:buildhost"\fR
+.RS 4
+The name of the host on which the binary package was created\&.
+.RE
+.PP
+\fBSOLVABLE_EULA "solvable:eula"\fR
+.RS 4
+If this attribute is present the user should be asked to accept the end user license agreement before the package gets installed\&.
+.RE
+.PP
+\fBSOLVABLE_CPEID "solvable:cpeid"\fR
+.RS 4
+A Common Platform Enumeration string describes the platform this package is intended for\&. See also the distribution attribute\&.
+.RE
+.PP
+\fBSOLVABLE_MESSAGEINS "solvable:messageins"\fR
+.RS 4
+A message that should be displayed to the user when the package gets installed\&.
+.RE
+.PP
+\fBSOLVABLE_MESSAGEDEL "solvable:messagedel"\fR
+.RS 4
+A message that should be displayed to the user when the package gets erased\&.
+.RE
+.PP
+\fBSOLVABLE_INSTALLSIZE "solvable:installsize"\fR
+.RS 4
+The disk space in bytes needed when installing the package\&.
+.RE
+.PP
+\fBSOLVABLE_DISKUSAGE "solvable:diskusage"\fR
+.RS 4
+A SUSE extension that stores for each directory the needed amount of disk space in kilobytes and inodes\&.
+.RE
+.PP
+\fBSOLVABLE_FILELIST "solvable:filelist"\fR
+.RS 4
+A list of files that the package contains\&.
+.RE
+.PP
+\fBSOLVABLE_INSTALLTIME "solvable:installtime"\fR
+.RS 4
+The seconds since the unix epoch when the binary package was installed on the system\&.
+.RE
+.PP
+\fBSOLVABLE_MEDIADIR "solvable:mediadir"\fR
+.RS 4
+The directory on the repository that contains the package\&. If this attribute is set to void, the package architecture is used as directory\&.
+.RE
+.PP
+\fBSOLVABLE_MEDIAFILE "solvable:mediafile"\fR
+.RS 4
+The filename on the repository that contains the package\&. If this attribute is set to void, the canonical file name of the package is used (i\&.e\&. a combination of the name, version, architecture)\&.
+.RE
+.PP
+\fBSOLVABLE_MEDIANR "solvable:medianr"\fR
+.RS 4
+The media number\&. This is an integer describing on which of a multi\-part media set this package is on\&.
+.RE
+.PP
+\fBSOLVABLE_MEDIABASE "solvable:mediabase"\fR
+.RS 4
+This attribute can be used to overwrite the repositories base url\&.
+.RE
+.PP
+\fBSOLVABLE_DOWNLOADSIZE "solvable:downloadsize"\fR
+.RS 4
+The size of the binary package in bytes\&.
+.RE
+.PP
+\fBSOLVABLE_SOURCEARCH "solvable:sourcearch"\fR
+.RS 4
+The architecture of the source package that this package belongs to\&.
+.RE
+.PP
+\fBSOLVABLE_SOURCENAME "solvable:sourcename"\fR
+.RS 4
+The name of the source package that this package belongs to\&. If set to void, the package name attribute is used instead\&.
+.RE
+.PP
+\fBSOLVABLE_SOURCEEVR "solvable:sourceevr"\fR
+.RS 4
+The version of the source package that this package belongs to\&. If set to void, the package version attribute is used instead\&.
+.RE
+.PP
+\fBSOLVABLE_TRIGGERS "solvable:triggers"\fR
+.RS 4
+A list of package triggers for this package\&. Used in the transaction ordering code\&.
+.RE
+.PP
+\fBSOLVABLE_CHECKSUM "solvable:checksum"\fR
+.RS 4
+The checksum of the binary package\&. See the Data Types section for a list of supported algorithms\&.
+.RE
+.PP
+\fBSOLVABLE_PKGID "solvable:pkgid"\fR
+.RS 4
+A string identifying a package\&. For rpm packages, this is the md5sum over the package header and the payload\&.
+.RE
+.PP
+\fBSOLVABLE_HDRID "solvable:hdrid"\fR
+.RS 4
+A string identifying a package\&. For rpm packages, this is the sha1sum over just the package header\&.
+.RE
+.PP
+\fBSOLVABLE_LEADSIGID "solvable:leadsigid"\fR
+.RS 4
+A string identifying the signature part of a package\&. For rpm packages, this is the md5sum from the start of the file up to the package header (i\&.e\&. it includes the lead, the signature header, and the padding)\&.
+.RE
+.PP
+\fBSOLVABLE_HEADEREND "solvable:headerend"\fR
+.RS 4
+The offset of the payload in rpm binary packages\&. You can use this information to download just the header if you want to display information not included in the repository metadata\&.
+.RE
+.PP
+\fBSOLVABLE_CHANGELOG "solvable:changelog"\fR
+.RS 4
+The array containing all the changelog structures\&.
+.RE
+.PP
+\fBSOLVABLE_CHANGELOG_AUTHOR "solvable:changelog:author"\fR
+.RS 4
+The author of a changelog entry\&.
+.RE
+.PP
+\fBSOLVABLE_CHANGELOG_TIME "solvable:changelog:time"\fR
+.RS 4
+The seconds since the unix epoch when the changelog entry was written\&.
+.RE
+.PP
+\fBSOLVABLE_CHANGELOG_TEXT "solvable:changelog:text"\fR
+.RS 4
+The text of a changelog entry\&.
+.RE
+.SH "SPECIAL SOLVABLE ATTRIBUTES"
+.PP
+\fBRPM_RPMDBID "rpm:dbid"\fR
+.RS 4
+The rpm database id of this (installed) package\&. Usually a small integer number\&.
+.RE
+.PP
+\fBSOLVABLE_PATCHCATEGORY "solvable:patchcategory"\fR
+.RS 4
+The category field for patch solvables\&. Should be named \(lqupdate:category\(rq instead\&.
+.RE
+.PP
+\fBUPDATE_REBOOT "update:reboot"\fR
+.RS 4
+If this attribute is present the system should be rebooted after the update is installed\&.
+.RE
+.PP
+\fBUPDATE_RESTART "update:restart"\fR
+.RS 4
+If this attribute is present the software manager should be run again after the update is installed\&.
+.RE
+.PP
+\fBUPDATE_RELOGIN "update:relogin"\fR
+.RS 4
+If this attribute is present the user should log off and on again after the update is installed\&.
+.RE
+.PP
+\fBUPDATE_MESSAGE "update:message"\fR
+.RS 4
+A message that should be shown to the user to warn him about anything non\-standard\&.
+.RE
+.PP
+\fBUPDATE_SEVERITY "update:severity"\fR
+.RS 4
+The severity of the update\&.
+.RE
+.PP
+\fBUPDATE_RIGHTS "update:rights"\fR
+.RS 4
+Any legal or other rights of the update\&.
+.RE
+.PP
+\fBUPDATE_COLLECTION "update:collection"\fR
+.RS 4
+The array containing the package list of the update\&.
+.RE
+.PP
+\fBUPDATE_COLLECTION_NAME "update:collection:name"\fR
+.RS 4
+The name of the updated package\&.
+.RE
+.PP
+\fBUPDATE_COLLECTION_EVR "update:collection:evr"\fR
+.RS 4
+The version of the updated package\&.
+.RE
+.PP
+\fBUPDATE_COLLECTION_ARCH "update:collection:arch"\fR
+.RS 4
+The architecture of the updated package\&.
+.RE
+.PP
+\fBUPDATE_COLLECTION_FILENAME "update:collection:filename"\fR
+.RS 4
+The file name of the updated package\&.
+.RE
+.PP
+\fBUPDATE_REFERENCE "update:reference"\fR
+.RS 4
+The array containing the reference list of the update\&.
+.RE
+.PP
+\fBUPDATE_REFERENCE_TYPE "update:reference:type"\fR
+.RS 4
+The type of the reference, e\&.g\&. bugzilla\&.
+.RE
+.PP
+\fBUPDATE_REFERENCE_HREF "update:reference:href"\fR
+.RS 4
+The URL of the reference\&.
+.RE
+.PP
+\fBUPDATE_REFERENCE_ID "update:reference:id"\fR
+.RS 4
+The identification string of the reference, e\&.g\&. the bug number\&.
+.RE
+.PP
+\fBUPDATE_REFERENCE_TITLE "update:reference:title"\fR
+.RS 4
+The title of the reference, e\&.g\&. the bug summary\&.
+.RE
+.PP
+\fBPRODUCT_REFERENCEFILE "product:referencefile"\fR
+.RS 4
+The basename of the product file in the package\&.
+.RE
+.PP
+\fBPRODUCT_SHORTLABEL "product:shortlabel"\fR
+.RS 4
+An identification string of the product\&.
+.RE
+.PP
+\fBPRODUCT_DISTPRODUCT "product:distproduct"\fR
+.RS 4
+Obsolete, do not use\&. Was a SUSE Code\-10 product name\&.
+.RE
+.PP
+\fBPRODUCT_DISTVERSION "product:distversion"\fR
+.RS 4
+Obsolete, do not use\&. Was a SUSE Code\-10 product version\&.
+.RE
+.PP
+\fBPRODUCT_TYPE "product:type"\fR
+.RS 4
+The type of the product, e\&.g\&. \(lqbase\(rq\&.
+.RE
+.PP
+\fBPRODUCT_URL "product:url"\fR
+.RS 4
+An array of product URLs\&.
+.RE
+.PP
+\fBPRODUCT_URL_TYPE "product:url:type"\fR
+.RS 4
+An array of product URL types\&.
+.RE
+.PP
+\fBPRODUCT_FLAGS "product:flags"\fR
+.RS 4
+An array of product flags\&.
+.RE
+.PP
+\fBPRODUCT_PRODUCTLINE "product:productline"\fR
+.RS 4
+A product line string used for product registering\&.
+.RE
+.PP
+\fBPRODUCT_REGISTER_TARGET "product:regtarget"\fR
+.RS 4
+A target for product registering\&.
+.RE
+.PP
+\fBPRODUCT_REGISTER_RELEASE "product:regrelease"\fR
+.RS 4
+A release string for product registering\&.
+.RE
+.PP
+\fBPUBKEY_KEYID "pubkey:keyid"\fR
+.RS 4
+The keyid of a pubkey, consisting of 8 bytes in hex\&.
+.RE
+.PP
+\fBPUBKEY_FINGERPRINT "pubkey:fingerprint"\fR
+.RS 4
+The fingerprint of a pubkey, usually a sha1sum in hex\&. Old V3 RSA keys use a md5sum instead\&.
+.RE
+.PP
+\fBPUBKEY_EXPIRES "pubkey:expires"\fR
+.RS 4
+The seconds since the unix epoch when the pubkey expires\&.
+.RE
+.PP
+\fBPUBKEY_SUBKEYOF "pubkey:subkeyof"\fR
+.RS 4
+The keyid of the master pubkey for subkeys\&.
+.RE
+.PP
+\fBPUBKEY_DATA "pubkey:data"\fR
+.RS 4
+The MPI data of the pubkey\&.
+.RE
+.PP
+\fBSOLVABLE_ISVISIBLE "solvable:isvisible"\fR
+.RS 4
+An attribute describing if the package should be listed to the user or not\&. Used for SUSE patterns\&.
+.RE
+.PP
+\fBSOLVABLE_CATEGORY "solvable:category"\fR
+.RS 4
+The category of a pattern\&.
+.RE
+.PP
+\fBSOLVABLE_INCLUDES "solvable:includes"\fR
+.RS 4
+A list of other patterns that this pattern includes\&.
+.RE
+.PP
+\fBSOLVABLE_EXTENDS "solvable:extends"\fR
+.RS 4
+A list of other patterns that this pattern extends\&.
+.RE
+.PP
+\fBSOLVABLE_ICON "solvable:icon"\fR
+.RS 4
+The icon name of a pattern\&.
+.RE
+.PP
+\fBSOLVABLE_ORDER "solvable:order"\fR
+.RS 4
+An ordering clue of a pattern\&.
+.RE
+.PP
+\fBSUSETAGS_SHARE_NAME "susetags:share:name"\fR
+.RS 4
+Internal attribute to implement susetags shared records\&. Holds the name of the solvable used for sharing attributes\&.
+.RE
+.PP
+\fBSUSETAGS_SHARE_EVR "susetags:share:evr"\fR
+.RS 4
+Internal attribute to implement susetags shared records\&. Holds the version of the solvable used for sharing attributes\&.
+.RE
+.PP
+\fBSUSETAGS_SHARE_ARCH "susetags:share:arch"\fR
+.RS 4
+Internal attribute to implement susetags shared records\&. Holds the architecture of the solvable used for sharing attributes\&.
+.RE
+.SH "SOLVABLE ARCHITECTURES"
+.sp
+Predefined architecture values for commonly used architectures\&.
+.PP
+\fBARCH_SRC "src"\fR
+.RS 4
+Used for binary packages that contain the package sources\&.
+.RE
+.PP
+\fBARCH_NOSRC "nosrc"\fR
+.RS 4
+Used for binary packages that contain some of the package sources, but not all files (because of restrictions)\&.
+.RE
+.PP
+\fBARCH_NOARCH "noarch"\fR
+.RS 4
+This package can be installed on any architecture\&. Used for rpm\&.
+.RE
+.PP
+\fBARCH_ALL "all"\fR
+.RS 4
+This package can be installed on any architecture\&. Used for Debian\&.
+.RE
+.PP
+\fBARCH_ANY "any"\fR
+.RS 4
+This package can be installed on any architecture\&. Used for Archlinux and Haiku\&.
+.RE
+.SH "DEPENDENCY IDS"
+.sp
+Namespaces are special modifiers that change the meaning of a dependency\&. Namespace dependencies are created with the REL_NAMESPACE flag\&. To make custom namespaces work you have to implement a namespace callback function\&.
+.sp
+The dependency markers partition the dependency array in two parts with different semantics\&.
+.PP
+\fBNAMESPACE_MODALIAS "namespace:modalias"\fR
+.RS 4
+The dependency is a special modalias dependency that matches installed hardware\&.
+.RE
+.PP
+\fBNAMESPACE_SPLITPROVIDES "namespace:splitprovides"\fR
+.RS 4
+The dependency is a special splitprovides dependency used to implement updates that include a package split\&. A splitprovides dependency contains a filename and a package name, it is matched if a package with the provided package name is installed that contains the filename\&. This namespace is implemented in libsolv, so you do not need a callback\&.
+.RE
+.PP
+\fBNAMESPACE_LANGUAGE "namespace:language"\fR
+.RS 4
+The dependency describes a language\&. The callback should return true if the language was selected by the user\&.
+.RE
+.PP
+\fBNAMESPACE_FILESYSTEM "namespace:filesystem"\fR
+.RS 4
+The dependency describes a filesystem\&. The callback should return true if the filesystem is needed\&.
+.RE
+.PP
+\fBNAMESPACE_OTHERPROVIDERS "namespace:otherproviders"\fR
+.RS 4
+This is a hack to allow self\-conflicting packages\&. It is not needed with current rpm version, so do not use this namespace\&.
+.RE
+.PP
+\fBSOLVABLE_PREREQMARKER "solvable:prereqmarker"\fR
+.RS 4
+This marker partitions the normal require dependencies from the prerequires\&. It is not needed for dependency solving, but it is used by the transaction ordering algorithm when a dependency cycle needs to be broken (non\-prereq deps get broken first)\&.
+.RE
+.PP
+\fBSOLVABLE_FILEMARKER "solvable:filemarker"\fR
+.RS 4
+This marker partitions the package provides dependencies from the synthetic file provides dependencies added by pool_addfileprovides()\&.
+.RE
+.SH "DATA TYPES"
+.sp
+Each attribute data is stored with a type, so that the lookup functions know how to interpret the data\&. The following types are available:
+.PP
+\fBREPOKEY_TYPE_VOID "repokey:type:void"\fR
+.RS 4
+No data is stored with this attribute\&. Thus you can only test if the attribute exists or not\&. Useful to store boolean values\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_CONSTANT "repokey:type:constant"\fR
+.RS 4
+The data is a constant 32bit number\&. The number is stored in the key area, so using it does not cost extra storage space (but you need the extra key space)\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_CONSTANTID "repokey:type:constantid"\fR
+.RS 4
+The data is a constant Id\&. The Id is stored in the key area, so using it does not cost extra storage space (but you need the extra key space)\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_ID "repokey:type:id"\fR
+.RS 4
+The data is an Id\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_NUM "repokey:type:num"\fR
+.RS 4
+The data is an unsigned 64bit number\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_U32 "repokey:type:num32"\fR
+.RS 4
+The data is an unsigned 32bit number\&. Obsolete, do not use\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_DIR "repokey:type:dir"\fR
+.RS 4
+The data is an Id of a directory\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_STR "repokey:type:str"\fR
+.RS 4
+The data is a regular string\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_BINARY "repokey:type:binary"\fR
+.RS 4
+The data is a binary blob\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_IDARRAY "repokey:type:idarray"\fR
+.RS 4
+The data is an array of non\-zero Ids\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_REL_IDARRAY "repokey:type:relidarray"\fR
+.RS 4
+The data is an array of non\-zero Ids ordered so that it needs less space\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_DIRSTRARRAY "repokey:type:dirstrarray"\fR
+.RS 4
+The data is a tuple consisting of a directory Id and a basename\&. Used to store file names\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_DIRNUMNUMARRAY "repokey:type:dirnumnumarray"\fR
+.RS 4
+The data is a triple consisting of a directory Id and two 32bit unsigned integers\&. Used to store disk usage information\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_MD5 "repokey:type:md5"\fR
+.RS 4
+The data is a binary md5sum\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_SHA1 "repokey:type:sha1"\fR
+.RS 4
+The data is a binary sha1sum\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_SHA256 "repokey:type:sha256"\fR
+.RS 4
+The data is a binary sha256sum\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_FIXARRAY "repokey:type:fixarray"\fR
+.RS 4
+The data is an array of structures that have all the same layout (i\&.e\&. the same keynames and keytypes in the same order)\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_FLEXARRAY "repokey:type:flexarray"\fR
+.RS 4
+The data is an array of structures that have a different layout\&.
+.RE
+.PP
+\fBREPOKEY_TYPE_DELETED "repokey:type:deleted"\fR
+.RS 4
+The data does not exist\&. Used to mark an attribute that was deleted\&.
+.RE
+.SH "REPOSITORY METADATA"
+.sp
+This attributes contain meta information about the repository\&.
+.PP
+\fBREPOSITORY_SOLVABLES "repository:solvables"\fR
+.RS 4
+This attribute holds the array including all of the solvables\&. It is only used in the on\-disk solv files, internally the solvables are stored in the pool\(cqs solvable array for fast access\&.
+.RE
+.PP
+\fBREPOSITORY_DELTAINFO "repository:deltainfo"\fR
+.RS 4
+This attribute holds the array including all of the delta packages\&.
+.RE
+.PP
+\fBREPOSITORY_EXTERNAL "repository:external"\fR
+.RS 4
+This attribute holds the array including all of the data to construct stub repodata areas to support on\-demand loading of metadata\&.
+.RE
+.PP
+\fBREPOSITORY_KEYS "repository:keys"\fR
+.RS 4
+This should really be named "repository:external:keys", it contains an array if Ids that consists of (keyname, keytype) pairs that describe the keys of the stub\&.
+.RE
+.PP
+\fBREPOSITORY_LOCATION "repository:location"\fR
+.RS 4
+This is used to provide a file name in the stub\&.
+.RE
+.PP
+\fBREPOSITORY_ADDEDFILEPROVIDES "repository:addedfileprovides"\fR
+.RS 4
+This attribute holds an array of filename Ids, that tell the library, that all of the Ids were already added to the solvable provides\&.
+.RE
+.PP
+\fBREPOSITORY_RPMDBCOOKIE "repository:rpmdbcookie"\fR
+.RS 4
+An attribute that stores a sha256sum over the file stats of the Packages database\&. It\(cqs used to detect rebuilds of the database, as in that case the database Ids of every package are newly distributed\&.
+.RE
+.PP
+\fBREPOSITORY_TIMESTAMP "repository:timestamp"\fR
+.RS 4
+The seconds since the unix epoch when the repository was created\&.
+.RE
+.PP
+\fBREPOSITORY_EXPIRE "repository:expire"\fR
+.RS 4
+The seconds after the timestamp when the repository will expire\&.
+.RE
+.PP
+\fBREPOSITORY_UPDATES "repository:updates"\fR
+.RS 4
+An array of structures describing what this repository updates\&.
+.RE
+.PP
+\fBREPOSITORY_DISTROS "repository:distros"\fR
+.RS 4
+Also an array of structures describing what this repository updates\&. Seems to be the newer name of REPOSITORY_UPDATES\&.
+.RE
+.PP
+\fBREPOSITORY_PRODUCT_LABEL "repository:product:label"\fR
+.RS 4
+Should really be called "repository:updates:label"\&. What distribution is updated with this repository\&.
+.RE
+.PP
+\fBREPOSITORY_PRODUCT_CPEID "repository:product:cpeid"\fR
+.RS 4
+The cpeid of the platform updated by this repository\&. Is both used in REPOSITORY_UPDATES and REPOSITORY_DISTROS to maximize confusion\&.
+.RE
+.PP
+\fBREPOSITORY_REPOID "repository:repoid"\fR
+.RS 4
+An array of Id strings describing keywords/tags about the repository itself\&.
+.RE
+.PP
+\fBREPOSITORY_KEYWORDS "repository:keywords"\fR
+.RS 4
+An array of Id strings describing keywords/tags about the content of the repository\&.
+.RE
+.PP
+\fBREPOSITORY_REVISION "repository:revision"\fR
+.RS 4
+An arbitrary string describing the revision of the repository\&.
+.RE
+.PP
+\fBREPOSITORY_TOOLVERSION "repository:toolversion"\fR
+.RS 4
+Some string describing somewhat the version of libsolv used to create the solv file\&.
+.RE
+.SH "REPOSITORY METADATA FOR SUSETAGS REPOS"
+.sp
+Attributes describing repository files in a susetags repository\&. \fBSUSETAGS_DATADIR "susetags:datadir"\fR:: The directory that contains the packages\&.
+.PP
+\fBSUSETAGS_DESCRDIR "susetags:descrdir"\fR
+.RS 4
+The directory that contains the repository file resources\&.
+.RE
+.PP
+\fBSUSETAGS_DEFAULTVENDOR "susetags:defaultvendor"\fR
+.RS 4
+The default vendor used when a package does not specify a vendor\&.
+.RE
+.PP
+\fBSUSETAGS_FILE "susetags:file"\fR
+.RS 4
+An array of file resources of the repository\&.
+.RE
+.PP
+\fBSUSETAGS_FILE_NAME "susetags:file:name"\fR
+.RS 4
+The filename of the resource\&.
+.RE
+.PP
+\fBSUSETAGS_FILE_TYPE "susetags:file:type"\fR
+.RS 4
+The type of the resource, e\&.g\&. \(lqMETA\(rq\&.
+.RE
+.PP
+\fBSUSETAGS_FILE_CHECKSUM "susetags:file:checksum"\fR
+.RS 4
+The file checksum of the resource\&.
+.RE
+.SH "REPOSITORY METADATA FOR RPMMD REPOS"
+.PP
+\fBREPOSITORY_REPOMD "repository:repomd"\fR
+.RS 4
+An array of file resources of the repository\&.
+.RE
+.PP
+\fBREPOSITORY_REPOMD_TYPE "repository:repomd:type"\fR
+.RS 4
+The type of the resource, e\&.g\&. \(lqprimary\(rq\&.
+.RE
+.PP
+\fBREPOSITORY_REPOMD_LOCATION "repository:repomd:location"\fR
+.RS 4
+The location (aka filename) of the resource
+.RE
+.PP
+\fBREPOSITORY_REPOMD_TIMESTAMP "repository:repomd:timestamp"\fR
+.RS 4
+The seconds since the unix epoch when the resource was created\&.
+.RE
+.PP
+\fBREPOSITORY_REPOMD_CHECKSUM "repository:repomd:checksum"\fR
+.RS 4
+The file checksum of the resource\&.
+.RE
+.PP
+\fBREPOSITORY_REPOMD_OPENCHECKSUM "repository:repomd:openchecksum"\fR
+.RS 4
+The checksum over the uncompressed contents of the resource\&.
+.RE
+.PP
+\fBREPOSITORY_REPOMD_SIZE "repository:repomd:size"\fR
+.RS 4
+The size of the resource file\&.
+.RE
+.SH "DELTA PACKAGE ATTRIBUTES"
+.PP
+\fBDELTA_PACKAGE_NAME "delta:pkgname"\fR
+.RS 4
+The target package name for the delta package\&. Applying the delta will recreate the target package\&.
+.RE
+.PP
+\fBDELTA_PACKAGE_EVR "delta:pkgevr"\fR
+.RS 4
+The version of the target package\&.
+.RE
+.PP
+\fBDELTA_PACKAGE_ARCH "delta:pkgarch"\fR
+.RS 4
+The architecture of the target package\&.
+.RE
+.PP
+\fBDELTA_LOCATION_DIR "delta:locdir"\fR
+.RS 4
+The directory in the repository that contains the delta package\&.
+.RE
+.PP
+\fBDELTA_LOCATION_NAME "delta:locname"\fR
+.RS 4
+The first part of the file name of the delta package\&.
+.RE
+.PP
+\fBDELTA_LOCATION_EVR "delta:locevr"\fR
+.RS 4
+The version part of the file name of the delta package\&.
+.RE
+.PP
+\fBDELTA_LOCATION_SUFFIX "delta:locsuffix"\fR
+.RS 4
+The suffix part of the file name of the delta package\&.
+.RE
+.PP
+\fBDELTA_LOCATION_BASE "delta:locbase"\fR
+.RS 4
+This attribute can be used to overwrite the repositories base url for the delta\&.
+.RE
+.PP
+\fBDELTA_DOWNLOADSIZE "delta:downloadsize"\fR
+.RS 4
+The size of the delta rpm file\&.
+.RE
+.PP
+\fBDELTA_CHECKSUM "delta:checksum"\fR
+.RS 4
+The checksum of the delta rpm file\&.
+.RE
+.PP
+\fBDELTA_BASE_EVR "delta:baseevr"\fR
+.RS 4
+The version of the package the delta was built against\&.
+.RE
+.PP
+\fBDELTA_SEQ_NAME "delta:seqname"\fR
+.RS 4
+The first part of the delta sequence, the base package name\&.
+.RE
+.PP
+\fBDELTA_SEQ_EVR "delta:seqevr"\fR
+.RS 4
+The evr part of the delta sequence, the base package evr\&. Identical to the DELTA_BASE_EVR attribute\&.
+.RE
+.PP
+\fBDELTA_SEQ_NUM "delta:seqnum"\fR
+.RS 4
+The last part of the delta sequence, the content selection string\&.
+.RE
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+Libsolv-Constantids(3)
+======================
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+libsolv-constantids - fixed Ids for often used strings
+
+
+Description
+-----------
+Constant Ids are Ids of strings that are often needed. They are defined
+to ease programming and reduce the number of pool_str2id calls. The
+constant Ids are part of the binary ABI of libsolv, a minor version
+update will only add new constants and not change existing Ids to
+maintain compatible. The on-disk solv format works does not use the
+fixed Ids, but instead references the strings, so solv files can still
+be read when the ABI is broken.
+
+
+Special Strings
+---------------
+*ID_EMPTY ""*::
+ The empty string. It will always have Id 1.
+
+*SYSTEM_SYSTEM "system:system"*::
+ The name of the always installed "system" solvable.
+
+
+Solvable Attributes
+-------------------
+These are Ids for keyname of attributes. They can be used in the
+lookup and storage functions to select the correct attribute in the
+solvable. The descriptions below describe the intended semantics
+of the values stored in the attribute with the keyname.
+
+*SOLVABLE_NAME "solvable:name"*::
+ The name of the package.
+
+*SOLVABLE_ARCH "solvable:arch"*::
+ The architecture of the package. See the Solvable Architecture section
+ for predefined architecture Id values.
+
+*SOLVABLE_EVR "solvable:evr"*::
+ The version of the package. It usually consists of some combination of
+ the Epoch, the Version, and the Release of the solvable.
+
+*SOLVABLE_VENDOR "solvable:vendor"*::
+ A vendor string. Usually the company or group that created the binary
+ package.
+
+*SOLVABLE_PROVIDES "solvable:provides"*::
+ Stores an array of dependency Ids that describe the capabilities
+ that the package provides.
+
+*SOLVABLE_OBSOLETES "solvable:obsoletes"*::
+ Stores an array of dependency Ids that describe the packages that this
+ package replaces.
+
+*SOLVABLE_CONFLICTS "solvable:conflicts"*::
+ Stores an array of dependency Ids that describe the capabilities that
+ this package conflicts with, i.e. that can't be installed together with
+ this package.
+
+*SOLVABLE_REQUIRES "solvable:requires"*::
+ Stores an array of dependency Ids that describe the capabilities that
+ also must be installed when this package is installed.
+
+*SOLVABLE_RECOMMENDS "solvable:recommends"*::
+ Stores an array of dependency Ids that describe the capabilities that
+ also should be installed when this package is installed. It's not an
+ error if not all capabilities can be met.
+
+*SOLVABLE_SUGGESTS "solvable:suggests"*::
+ Stores an array of dependency Ids that describe the capabilities that
+ also useful to have installed when this package is installed. This is
+ intended to provide a hint to the user about other packages.
+
+*SOLVABLE_SUPPLEMENTS "solvable:supplements"*::
+ Stores an array of dependency Ids that define that this package should
+ be installed if one of the capabilities is met. This is like the
+ recommends attribute, but works in the reverse way.
+
+*SOLVABLE_ENHANCES "solvable:enhances"*::
+ Stores an array of dependency Ids that define that this package is
+ useful to have installed if one of the capabilities is met. This is like
+ the suggests attribute, but works in the reverse way.
+
+*SOLVABLE_SUMMARY "solvable:summary"*::
+ The summary should be a short string without any newlines that describes
+ what a package does.
+
+*SOLVABLE_DESCRIPTION "solvable:description"*::
+ The description should be a more verbose description about what a
+ package does. It may consist of multiple lines.
+
+*SOLVABLE_DISTRIBUTION "solvable:distribution"*::
+ The distribution is a short string that describes the OS and OS version
+ this package is built for.
+
+*SOLVABLE_AUTHORS "solvable:authors"*::
+ A list of authors of this package. This attribute was used in SUSE
+ packages.
+
+*SOLVABLE_PACKAGER "solvable:packager"*::
+ The person who created the binary package, see also the vendor attribute.
+
+*SOLVABLE_GROUP "solvable:group"*::
+ The package group that this package belongs to. See also the keywords
+ attribute.
+
+*SOLVABLE_URL "solvable:url"*::
+ An URL that points to more information about the package.
+
+*SOLVABLE_KEYWORDS "solvable:keywords"*::
+ list of keyword string IDs used for tagging this package.
+
+*SOLVABLE_LICENSE "solvable:license"*::
+ The license(s) of this package.
+
+*SOLVABLE_BUILDTIME "solvable:buildtime"*::
+ The seconds since the unix epoch when the binary package was created.
+
+*SOLVABLE_BUILDHOST "solvable:buildhost"*::
+ The name of the host on which the binary package was created.
+
+*SOLVABLE_EULA "solvable:eula"*::
+ If this attribute is present the user should be asked to accept the end
+ user license agreement before the package gets installed.
+
+*SOLVABLE_CPEID "solvable:cpeid"*::
+ A Common Platform Enumeration string describes the platform this package
+ is intended for. See also the distribution attribute.
+
+*SOLVABLE_MESSAGEINS "solvable:messageins"*::
+ A message that should be displayed to the user when the package gets
+ installed.
+
+*SOLVABLE_MESSAGEDEL "solvable:messagedel"*::
+ A message that should be displayed to the user when the package gets
+ erased.
+
+*SOLVABLE_INSTALLSIZE "solvable:installsize"*::
+ The disk space in bytes needed when installing the package.
+
+*SOLVABLE_DISKUSAGE "solvable:diskusage"*::
+ A SUSE extension that stores for each directory the needed amount of
+ disk space in kilobytes and inodes.
+
+*SOLVABLE_FILELIST "solvable:filelist"*::
+ A list of files that the package contains.
+
+*SOLVABLE_INSTALLTIME "solvable:installtime"*::
+ The seconds since the unix epoch when the binary package was installed
+ on the system.
+
+*SOLVABLE_MEDIADIR "solvable:mediadir"*::
+ The directory on the repository that contains the package. If this
+ attribute is set to void, the package architecture is used as
+ directory.
+
+*SOLVABLE_MEDIAFILE "solvable:mediafile"*::
+ The filename on the repository that contains the package. If this
+ attribute is set to void, the canonical file name of the package is
+ used (i.e. a combination of the name, version, architecture).
+
+*SOLVABLE_MEDIANR "solvable:medianr"*::
+ The media number. This is an integer describing on which of a multi-part
+ media set this package is on.
+
+*SOLVABLE_MEDIABASE "solvable:mediabase"*::
+ This attribute can be used to overwrite the repositories base url.
+
+*SOLVABLE_DOWNLOADSIZE "solvable:downloadsize"*::
+ The size of the binary package in bytes.
+
+*SOLVABLE_SOURCEARCH "solvable:sourcearch"*::
+ The architecture of the source package that this package belongs to.
+
+*SOLVABLE_SOURCENAME "solvable:sourcename"*::
+ The name of the source package that this package belongs to. If set
+ to void, the package name attribute is used instead.
+
+*SOLVABLE_SOURCEEVR "solvable:sourceevr"*::
+ The version of the source package that this package belongs to. If set
+ to void, the package version attribute is used instead.
+
+*SOLVABLE_TRIGGERS "solvable:triggers"*::
+ A list of package triggers for this package. Used in the transaction
+ ordering code.
+
+*SOLVABLE_CHECKSUM "solvable:checksum"*::
+ The checksum of the binary package. See the Data Types section for
+ a list of supported algorithms.
+
+*SOLVABLE_PKGID "solvable:pkgid"*::
+ A string identifying a package. For rpm packages, this is the md5sum
+ over the package header and the payload.
+
+*SOLVABLE_HDRID "solvable:hdrid"*::
+ A string identifying a package. For rpm packages, this is the sha1sum
+ over just the package header.
+
+*SOLVABLE_LEADSIGID "solvable:leadsigid"*::
+ A string identifying the signature part of a package. For rpm packages,
+ this is the md5sum from the start of the file up to the package header
+ (i.e. it includes the lead, the signature header, and the padding).
+
+*SOLVABLE_HEADEREND "solvable:headerend"*::
+ The offset of the payload in rpm binary packages. You can use this
+ information to download just the header if you want to display
+ information not included in the repository metadata.
+
+*SOLVABLE_CHANGELOG "solvable:changelog"*::
+ The array containing all the changelog structures.
+
+*SOLVABLE_CHANGELOG_AUTHOR "solvable:changelog:author"*::
+ The author of a changelog entry.
+
+*SOLVABLE_CHANGELOG_TIME "solvable:changelog:time"*::
+ The seconds since the unix epoch when the changelog entry was written.
+
+*SOLVABLE_CHANGELOG_TEXT "solvable:changelog:text"*::
+ The text of a changelog entry.
+
+
+Special Solvable Attributes
+---------------------------
+*RPM_RPMDBID "rpm:dbid"*::
+ The rpm database id of this (installed) package. Usually a small
+ integer number.
+
+*SOLVABLE_PATCHCATEGORY "solvable:patchcategory"*::
+ The category field for patch solvables. Should be named
+ ``update:category'' instead.
+
+*UPDATE_REBOOT "update:reboot"*::
+ If this attribute is present the system should be rebooted after
+ the update is installed.
+
+*UPDATE_RESTART "update:restart"*::
+ If this attribute is present the software manager should be run
+ again after the update is installed.
+
+*UPDATE_RELOGIN "update:relogin"*::
+ If this attribute is present the user should log off and on again
+ after the update is installed.
+
+*UPDATE_MESSAGE "update:message"*::
+ A message that should be shown to the user to warn him about anything
+ non-standard.
+
+*UPDATE_SEVERITY "update:severity"*::
+ The severity of the update.
+
+*UPDATE_RIGHTS "update:rights"*::
+ Any legal or other rights of the update.
+
+*UPDATE_COLLECTION "update:collection"*::
+ The array containing the package list of the update.
+
+*UPDATE_COLLECTION_NAME "update:collection:name"*::
+ The name of the updated package.
+
+*UPDATE_COLLECTION_EVR "update:collection:evr"*::
+ The version of the updated package.
+
+*UPDATE_COLLECTION_ARCH "update:collection:arch"*::
+ The architecture of the updated package.
+
+*UPDATE_COLLECTION_FILENAME "update:collection:filename"*::
+ The file name of the updated package.
+
+*UPDATE_REFERENCE "update:reference"*::
+ The array containing the reference list of the update.
+
+*UPDATE_REFERENCE_TYPE "update:reference:type"*::
+ The type of the reference, e.g. bugzilla.
+
+*UPDATE_REFERENCE_HREF "update:reference:href"*::
+ The URL of the reference.
+
+*UPDATE_REFERENCE_ID "update:reference:id"*::
+ The identification string of the reference, e.g. the bug number.
+
+*UPDATE_REFERENCE_TITLE "update:reference:title"*::
+ The title of the reference, e.g. the bug summary.
+
+*PRODUCT_REFERENCEFILE "product:referencefile"*::
+ The basename of the product file in the package.
+
+*PRODUCT_SHORTLABEL "product:shortlabel"*::
+ An identification string of the product.
+
+*PRODUCT_DISTPRODUCT "product:distproduct"*::
+ Obsolete, do not use. Was a SUSE Code-10 product name.
+
+*PRODUCT_DISTVERSION "product:distversion"*::
+ Obsolete, do not use. Was a SUSE Code-10 product version.
+
+*PRODUCT_TYPE "product:type"*::
+ The type of the product, e.g. ``base''.
+
+*PRODUCT_URL "product:url"*::
+ An array of product URLs.
+
+*PRODUCT_URL_TYPE "product:url:type"*::
+ An array of product URL types.
+
+*PRODUCT_FLAGS "product:flags"*::
+ An array of product flags.
+
+*PRODUCT_PRODUCTLINE "product:productline"*::
+ A product line string used for product registering.
+
+*PRODUCT_REGISTER_TARGET "product:regtarget"*::
+ A target for product registering.
+
+*PRODUCT_REGISTER_RELEASE "product:regrelease"*::
+ A release string for product registering.
+
+*PUBKEY_KEYID "pubkey:keyid"*::
+ The keyid of a pubkey, consisting of 8 bytes in hex.
+
+*PUBKEY_FINGERPRINT "pubkey:fingerprint"*::
+ The fingerprint of a pubkey, usually a sha1sum in hex. Old V3 RSA keys
+ use a md5sum instead.
+
+*PUBKEY_EXPIRES "pubkey:expires"*::
+ The seconds since the unix epoch when the pubkey expires.
+
+*PUBKEY_SUBKEYOF "pubkey:subkeyof"*::
+ The keyid of the master pubkey for subkeys.
+
+*PUBKEY_DATA "pubkey:data"*::
+ The MPI data of the pubkey.
+
+*SOLVABLE_ISVISIBLE "solvable:isvisible"*::
+ An attribute describing if the package should be listed to the user
+ or not. Used for SUSE patterns.
+
+*SOLVABLE_CATEGORY "solvable:category"*::
+ The category of a pattern.
+
+*SOLVABLE_INCLUDES "solvable:includes"*::
+ A list of other patterns that this pattern includes.
+
+*SOLVABLE_EXTENDS "solvable:extends"*::
+ A list of other patterns that this pattern extends.
+
+*SOLVABLE_ICON "solvable:icon"*::
+ The icon name of a pattern.
+
+*SOLVABLE_ORDER "solvable:order"*::
+ An ordering clue of a pattern.
+
+*SUSETAGS_SHARE_NAME "susetags:share:name"*::
+ Internal attribute to implement susetags shared records. Holds the
+ name of the solvable used for sharing attributes.
+
+*SUSETAGS_SHARE_EVR "susetags:share:evr"*::
+ Internal attribute to implement susetags shared records. Holds the
+ version of the solvable used for sharing attributes.
+
+*SUSETAGS_SHARE_ARCH "susetags:share:arch"*::
+ Internal attribute to implement susetags shared records. Holds the
+ architecture of the solvable used for sharing attributes.
+
+
+Solvable Architectures
+----------------------
+Predefined architecture values for commonly used architectures.
+
+*ARCH_SRC "src"*::
+ Used for binary packages that contain the package sources.
+
+*ARCH_NOSRC "nosrc"*::
+ Used for binary packages that contain some of the package sources,
+ but not all files (because of restrictions).
+
+*ARCH_NOARCH "noarch"*::
+ This package can be installed on any architecture. Used for rpm.
+
+*ARCH_ALL "all"*::
+ This package can be installed on any architecture. Used for Debian.
+
+*ARCH_ANY "any"*::
+ This package can be installed on any architecture. Used for Archlinux
+ and Haiku.
+
+
+Dependency Ids
+--------------
+Namespaces are special modifiers that change the meaning of a dependency.
+Namespace dependencies are created with the REL_NAMESPACE flag. To make
+custom namespaces work you have to implement a namespace callback function.
+
+The dependency markers partition the dependency array in two parts with
+different semantics.
+
+*NAMESPACE_MODALIAS "namespace:modalias"*::
+ The dependency is a special modalias dependency that matches installed
+ hardware.
+
+*NAMESPACE_SPLITPROVIDES "namespace:splitprovides"*::
+ The dependency is a special splitprovides dependency used to implement
+ updates that include a package split. A splitprovides dependency contains
+ a filename and a package name, it is matched if a package with the
+ provided package name is installed that contains the filename.
+ This namespace is implemented in libsolv, so you do not need a callback.
+
+*NAMESPACE_LANGUAGE "namespace:language"*::
+ The dependency describes a language. The callback should return true
+ if the language was selected by the user.
+
+*NAMESPACE_FILESYSTEM "namespace:filesystem"*::
+ The dependency describes a filesystem. The callback should return true
+ if the filesystem is needed.
+
+*NAMESPACE_OTHERPROVIDERS "namespace:otherproviders"*::
+ This is a hack to allow self-conflicting packages. It is not needed
+ with current rpm version, so do not use this namespace.
+
+*SOLVABLE_PREREQMARKER "solvable:prereqmarker"*::
+ This marker partitions the normal require dependencies from the
+ prerequires. It is not needed for dependency solving, but it is
+ used by the transaction ordering algorithm when a dependency cycle
+ needs to be broken (non-prereq deps get broken first).
+
+*SOLVABLE_FILEMARKER "solvable:filemarker"*::
+ This marker partitions the package provides dependencies from the
+ synthetic file provides dependencies added by pool_addfileprovides().
+
+
+Data Types
+----------
+Each attribute data is stored with a type, so that the lookup functions
+know how to interpret the data. The following types are available:
+
+*REPOKEY_TYPE_VOID "repokey:type:void"*::
+ No data is stored with this attribute. Thus you can only test if
+ the attribute exists or not. Useful to store boolean values.
+
+*REPOKEY_TYPE_CONSTANT "repokey:type:constant"*::
+ The data is a constant 32bit number. The number is stored in the key
+ area, so using it does not cost extra storage space (but you need the
+ extra key space).
+
+*REPOKEY_TYPE_CONSTANTID "repokey:type:constantid"*::
+ The data is a constant Id. The Id is stored in the key area,
+ so using it does not cost extra storage space (but you need the
+ extra key space).
+
+*REPOKEY_TYPE_ID "repokey:type:id"*::
+ The data is an Id.
+
+*REPOKEY_TYPE_NUM "repokey:type:num"*::
+ The data is an unsigned 64bit number.
+
+*REPOKEY_TYPE_U32 "repokey:type:num32"*::
+ The data is an unsigned 32bit number. Obsolete, do not use.
+
+*REPOKEY_TYPE_DIR "repokey:type:dir"*::
+ The data is an Id of a directory.
+
+*REPOKEY_TYPE_STR "repokey:type:str"*::
+ The data is a regular string.
+
+*REPOKEY_TYPE_BINARY "repokey:type:binary"*::
+ The data is a binary blob.
+
+*REPOKEY_TYPE_IDARRAY "repokey:type:idarray"*::
+ The data is an array of non-zero Ids.
+
+*REPOKEY_TYPE_REL_IDARRAY "repokey:type:relidarray"*::
+ The data is an array of non-zero Ids ordered so that it needs less
+ space.
+
+*REPOKEY_TYPE_DIRSTRARRAY "repokey:type:dirstrarray"*::
+ The data is a tuple consisting of a directory Id and a basename.
+ Used to store file names.
+
+*REPOKEY_TYPE_DIRNUMNUMARRAY "repokey:type:dirnumnumarray"*::
+ The data is a triple consisting of a directory Id and two 32bit
+ unsigned integers. Used to store disk usage information.
+
+*REPOKEY_TYPE_MD5 "repokey:type:md5"*::
+ The data is a binary md5sum.
+
+*REPOKEY_TYPE_SHA1 "repokey:type:sha1"*::
+ The data is a binary sha1sum.
+
+*REPOKEY_TYPE_SHA256 "repokey:type:sha256"*::
+ The data is a binary sha256sum.
+
+*REPOKEY_TYPE_FIXARRAY "repokey:type:fixarray"*::
+ The data is an array of structures that have all the same layout
+ (i.e. the same keynames and keytypes in the same order).
+
+*REPOKEY_TYPE_FLEXARRAY "repokey:type:flexarray"*::
+ The data is an array of structures that have a different layout.
+
+*REPOKEY_TYPE_DELETED "repokey:type:deleted"*::
+ The data does not exist. Used to mark an attribute that was deleted.
+
+
+Repository Metadata
+-------------------
+This attributes contain meta information about the repository.
+
+*REPOSITORY_SOLVABLES "repository:solvables"*::
+ This attribute holds the array including all of the solvables. It is
+ only used in the on-disk solv files, internally the solvables are
+ stored in the pool's solvable array for fast access.
+
+*REPOSITORY_DELTAINFO "repository:deltainfo"*::
+ This attribute holds the array including all of the delta packages.
+
+*REPOSITORY_EXTERNAL "repository:external"*::
+ This attribute holds the array including all of the data to construct
+ stub repodata areas to support on-demand loading of metadata.
+
+*REPOSITORY_KEYS "repository:keys"*::
+ This should really be named "repository:external:keys", it contains an
+ array if Ids that consists of (keyname, keytype) pairs that describe the
+ keys of the stub.
+
+*REPOSITORY_LOCATION "repository:location"*::
+ This is used to provide a file name in the stub.
+
+*REPOSITORY_ADDEDFILEPROVIDES "repository:addedfileprovides"*::
+ This attribute holds an array of filename Ids, that tell the library,
+ that all of the Ids were already added to the solvable provides.
+
+*REPOSITORY_RPMDBCOOKIE "repository:rpmdbcookie"*::
+ An attribute that stores a sha256sum over the file stats of the
+ Packages database. It's used to detect rebuilds of the database,
+ as in that case the database Ids of every package are newly
+ distributed.
+
+*REPOSITORY_TIMESTAMP "repository:timestamp"*::
+ The seconds since the unix epoch when the repository was created.
+
+*REPOSITORY_EXPIRE "repository:expire"*::
+ The seconds after the timestamp when the repository will expire.
+
+*REPOSITORY_UPDATES "repository:updates"*::
+ An array of structures describing what this repository updates.
+
+*REPOSITORY_DISTROS "repository:distros"*::
+ Also an array of structures describing what this repository updates.
+ Seems to be the newer name of REPOSITORY_UPDATES.
+
+*REPOSITORY_PRODUCT_LABEL "repository:product:label"*::
+ Should really be called "repository:updates:label". What distribution
+ is updated with this repository.
+
+*REPOSITORY_PRODUCT_CPEID "repository:product:cpeid"*::
+ The cpeid of the platform updated by this repository. Is both used
+ in REPOSITORY_UPDATES and REPOSITORY_DISTROS to maximize confusion.
+
+*REPOSITORY_REPOID "repository:repoid"*::
+ An array of Id strings describing keywords/tags about the repository
+ itself.
+
+*REPOSITORY_KEYWORDS "repository:keywords"*::
+ An array of Id strings describing keywords/tags about the content of
+ the repository.
+
+*REPOSITORY_REVISION "repository:revision"*::
+ An arbitrary string describing the revision of the repository.
+
+*REPOSITORY_TOOLVERSION "repository:toolversion"*::
+ Some string describing somewhat the version of libsolv used to create
+ the solv file.
+
+
+Repository Metadata for Susetags Repos
+--------------------------------------
+Attributes describing repository files in a susetags repository.
+*SUSETAGS_DATADIR "susetags:datadir"*::
+ The directory that contains the packages.
+
+*SUSETAGS_DESCRDIR "susetags:descrdir"*::
+ The directory that contains the repository file resources.
+
+*SUSETAGS_DEFAULTVENDOR "susetags:defaultvendor"*::
+ The default vendor used when a package does not specify a vendor.
+
+*SUSETAGS_FILE "susetags:file"*::
+ An array of file resources of the repository.
+
+*SUSETAGS_FILE_NAME "susetags:file:name"*::
+ The filename of the resource.
+
+*SUSETAGS_FILE_TYPE "susetags:file:type"*::
+ The type of the resource, e.g. ``META''.
+
+*SUSETAGS_FILE_CHECKSUM "susetags:file:checksum"*::
+ The file checksum of the resource.
+
+
+Repository Metadata for RpmMD Repos
+-----------------------------------
+*REPOSITORY_REPOMD "repository:repomd"*::
+ An array of file resources of the repository.
+
+*REPOSITORY_REPOMD_TYPE "repository:repomd:type"*::
+ The type of the resource, e.g. ``primary''.
+
+*REPOSITORY_REPOMD_LOCATION "repository:repomd:location"*::
+ The location (aka filename) of the resource
+
+*REPOSITORY_REPOMD_TIMESTAMP "repository:repomd:timestamp"*::
+ The seconds since the unix epoch when the resource was created.
+
+*REPOSITORY_REPOMD_CHECKSUM "repository:repomd:checksum"*::
+ The file checksum of the resource.
+
+*REPOSITORY_REPOMD_OPENCHECKSUM "repository:repomd:openchecksum"*::
+ The checksum over the uncompressed contents of the resource.
+
+*REPOSITORY_REPOMD_SIZE "repository:repomd:size"*::
+ The size of the resource file.
+
+
+Delta Package Attributes
+------------------------
+*DELTA_PACKAGE_NAME "delta:pkgname"*::
+ The target package name for the delta package. Applying the delta
+ will recreate the target package.
+
+*DELTA_PACKAGE_EVR "delta:pkgevr"*::
+ The version of the target package.
+
+*DELTA_PACKAGE_ARCH "delta:pkgarch"*::
+ The architecture of the target package.
+
+*DELTA_LOCATION_DIR "delta:locdir"*::
+ The directory in the repository that contains the delta package.
+
+*DELTA_LOCATION_NAME "delta:locname"*::
+ The first part of the file name of the delta package.
+
+*DELTA_LOCATION_EVR "delta:locevr"*::
+ The version part of the file name of the delta package.
+
+*DELTA_LOCATION_SUFFIX "delta:locsuffix"*::
+ The suffix part of the file name of the delta package.
+
+*DELTA_LOCATION_BASE "delta:locbase"*::
+ This attribute can be used to overwrite the repositories base url for
+ the delta.
+
+*DELTA_DOWNLOADSIZE "delta:downloadsize"*::
+ The size of the delta rpm file.
+
+*DELTA_CHECKSUM "delta:checksum"*::
+ The checksum of the delta rpm file.
+
+*DELTA_BASE_EVR "delta:baseevr"*::
+ The version of the package the delta was built against.
+
+*DELTA_SEQ_NAME "delta:seqname"*::
+ The first part of the delta sequence, the base package name.
+
+*DELTA_SEQ_EVR "delta:seqevr"*::
+ The evr part of the delta sequence, the base package evr. Identical
+ to the DELTA_BASE_EVR attribute.
+
+*DELTA_SEQ_NUM "delta:seqnum"*::
+ The last part of the delta sequence, the content selection string.
+
+
+Author
+------
+Michael Schroeder <mls@suse.de>
+
--- /dev/null
+'\" t
+.\" Title: Libsolv-History
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "LIBSOLV\-HISTORY" "3" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+libsolv-history \- how the libsolv library came into existence
+.SH "HISTORY"
+.sp
+This project was started in May 2007 when the zypp folks decided to switch to a database to speed up installation\&. As I am not a big fan of databases, I (mls) wondered if there would be really some merit of using one for solving, as package dependencies of all packages have to be read in anyway\&.
+.sp
+Back in 2002, I researched that using a dictionary approach for storing dependencies can reduce the packages file to 1/3 of its size\&. Extending this idea a bit more, I decided to store all strings and relations as unique 32\-bit numbers\&. This has three big advantages:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+because of the unification, testing whether two strings are equal is the same as testing the equality of two numbers, thus very fast
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+much space is saved, as numbers do not take up as much space as strings the internal memory representation does not take more space on a 64\-bit system where a pointer is twice the size of a 32\-bit number
+.RE
+.sp
+Thus, the solv format was created, which stores a repository as a string dictionary, a relation dictionary and then all packages dependencies\&. Tests showed that reading and merging multiple solv repositories takes just some milliseconds\&.
+.SS "Early solver experiments"
+.sp
+Having a new repository format was one big step, but the other area where libzypp needed improvement was the solver\&. Libzypp\(cqs solver was a port from the Red Carpet solver, which was written to update packages in an already installed system\&. Using it for the complete installation progress brought it to its limits\&. Also, the added extensions like support for weak dependencies and patches made it fragile and unpredictable\&.
+.sp
+As I was not very pleased with the way the solver worked, I looked at other solver algorithms\&. I checked smart, yum and apt, but could not find a convincing algorithm\&. My own experiments also were not very convincing, they worked fine for some problems but failed miserably for other corner cases\&.
+.SS "Using SAT for solving"
+.sp
+SUSE\(cqs hack week at the end of June 2007 turned out to be a turning point for the solver\&. Googling for solver algorithms, I stumbled over some note saying that some people are trying to use SAT algorithms to improve solving on Debian\&. Looking at the SAT entry in Wikipedia, it was easy to see that this indeed was the missing piece: SAT algorithms are well researched and there are quite some open source implementations\&. I decided to look at the minisat code, as it is one of the fastest solvers while consisting of too many lines of code\&.
+.sp
+Of course, directly using minisat would not work, as a package solver does not need to find just one correct solution, but it also has to optimize some metrics, i\&.e\&. keep as many packages installed as possible\&. Thus, I needed to write my own solver incorporation the ideas and algorithms used in minisat\&. This wasn\(cqt very hard, and at the end of the hack week the solver calculated the first right solutions\&.
+.SS "Selling it to libzypp"
+.sp
+With those encouraging results, I went to Klaus Kaempf, the system management architect at SUSE\&. We spoke about how to convince the team to make libzypp switch to the new solver\&. Fortunately, libzypp comes with a plethora of solver test cases, so we decided to make the solver pass most of the test cases first\&. Klaus wrote a "deptestomatic" implementation to check the test cases\&. Together with Stephan Kulow, who is responsible for the openSUSE distribution, we tweaked and extended the solver until most of the test cases looked good\&.
+.sp
+Duncan Mac\-Vicar Prett, the team lead of the YaST team, also joined development by creating Ruby bindings for the solver\&. Later, Klaus improved the bindings and ported them to some other languages\&.
+.SS "The attribute store"
+.sp
+The progress with the repository format and the solver attracted another hacker to the project: Michael Matz from the compiler team\&. He started with improving the repository parsers so that patches and content files also generate solvables\&. After that, he concentrated on storing all of the other metadata of the repositories that are not used for solving, like the package summaries and descriptions\&. At the end of October, a first version of this "attribute store" was checked in\&. Its design goals were:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+space efficient storage of attributes
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+paging/on demand loading of data
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+page compression
+.RE
+.sp
+The first version of the attribute store used a different format for storing information, we later merged this format with the solv file format\&.
+.SS "libzypp integration"
+.sp
+Integration of the sat\-solver into libzypp also started in October 2007 by Stefan Schubert and Michael Andres from the YaST team\&. The first versions supported both the old solver and the new one by using the old repository read functions and converting the old package data in\-memory into a sat solver pool\&. Solvers could be switched with the environment variable ZYPP_SAT_SOLVER\&. The final decision to move to the new solver was made in January of 2008, first just by making the new solver the default one, later by completely throwing out the old solver code\&. This had the advantage that the internal solvable storage could also be done by using the solver pool, something Michael Matz already played with in a proof of concept implementation showing some drastic speed gains\&. The last traces of the old database code were removed in February\&.
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+Libsolv-History(3)
+==================
+:man manual: LIBSOLV
+:man source: libsolv
+
+Name
+----
+libsolv-history - how the libsolv library came into existence
+
+History
+-------
+This project was started in May 2007 when the zypp folks decided to switch
+to a database to speed up installation. As I am not a big fan of databases,
+I (mls) wondered if there would be really some merit of using one for solving,
+as package dependencies of all packages have to be read in anyway.
+
+Back in 2002, I researched that using a dictionary approach for storing
+dependencies can reduce the packages file to 1/3 of its size. Extending
+this idea a bit more, I decided to store all strings and relations
+as unique 32-bit numbers. This has three big advantages:
+
+- because of the unification, testing whether two strings are equal is the
+ same as testing the equality of two numbers, thus very fast
+- much space is saved, as numbers do not take up as much space as strings
+ the internal memory representation does not take more space on a
+ 64-bit system where a pointer is twice the size of a 32-bit number
+
+Thus, the solv format was created, which stores a repository as a string
+dictionary, a relation dictionary and then all packages dependencies.
+Tests showed that reading and merging multiple solv repositories takes
+just some milliseconds.
+
+=== Early solver experiments ===
+Having a new repository format was one big step, but the other area
+where libzypp needed improvement was the solver. Libzypp's solver was
+a port from the Red Carpet solver, which was written to update packages
+in an already installed system. Using it for the complete installation
+progress brought it to its limits. Also, the added extensions like
+support for weak dependencies and patches made it fragile and
+unpredictable.
+
+As I was not very pleased with the way the solver worked, I looked at
+other solver algorithms. I checked smart, yum and apt, but could not
+find a convincing algorithm. My own experiments also were not very
+convincing, they worked fine for some problems but failed miserably
+for other corner cases.
+
+=== Using SAT for solving ===
+SUSE's hack week at the end of June 2007 turned out to be a turning point
+for the solver. Googling for solver algorithms, I stumbled over some note
+saying that some people are trying to use SAT algorithms to improve
+solving on Debian. Looking at the SAT entry in Wikipedia, it was easy
+to see that this indeed was the missing piece: SAT algorithms are well
+researched and there are quite some open source implementations.
+I decided to look at the minisat code, as it is one of the fastest
+solvers while consisting of too many lines of code.
+
+Of course, directly using minisat would not work, as a package solver
+does not need to find just one correct solution, but it also has to
+optimize some metrics, i.e. keep as many packages installed as possible.
+Thus, I needed to write my own solver incorporation the ideas and
+algorithms used in minisat. This wasn't very hard, and at the end of
+the hack week the solver calculated the first right solutions.
+
+=== Selling it to libzypp ===
+With those encouraging results, I went to Klaus Kaempf, the system
+management architect at SUSE. We spoke about how to convince the
+team to make libzypp switch to the new solver. Fortunately, libzypp comes
+with a plethora of solver test cases, so we decided to make the solver pass
+most of the test cases first. Klaus wrote a "deptestomatic" implementation
+to check the test cases. Together with Stephan Kulow, who is responsible for the
+openSUSE distribution, we tweaked and extended the solver until most of
+the test cases looked good.
+
+Duncan Mac-Vicar Prett, the team lead of the YaST team, also joined
+development by creating Ruby bindings for the solver. Later, Klaus
+improved the bindings and ported them to some other languages.
+
+=== The attribute store ===
+The progress with the repository format and the solver attracted another
+hacker to the project: Michael Matz from the compiler team. He started
+with improving the repository parsers so that patches and content files
+also generate solvables. After that, he concentrated on storing all
+of the other metadata of the repositories that are not used for solving,
+like the package summaries and descriptions. At the end of October, a first
+version of this "attribute store" was checked in. Its design goals were:
+
+- space efficient storage of attributes
+- paging/on demand loading of data
+- page compression
+
+The first version of the attribute store used a different format for
+storing information, we later merged this format with the solv file
+format.
+
+=== libzypp integration ===
+Integration of the sat-solver into libzypp also started in October 2007 by
+Stefan Schubert and Michael Andres from the YaST team. The first
+versions supported both the old solver and the new one by using the
+old repository read functions and converting the old package data
+in-memory into a sat solver pool. Solvers could be switched with
+the environment variable ZYPP_SAT_SOLVER. The final decision to
+move to the new solver was made in January of 2008, first just by
+making the new solver the default one, later by completely throwing out
+the old solver code. This had the advantage that the internal solvable
+storage could also be done by using the solver pool, something Michael
+Matz already played with in a proof of concept implementation showing
+some drastic speed gains. The last traces of the old database code
+were removed in February.
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: Libsolv-Pool
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 12/14/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "LIBSOLV\-POOL" "3" "12/14/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+libsolv-pool \- Libsolv\*(Aqs pool object
+.SH "PUBLIC ATTRIBUTES"
+.PP
+\fBvoid *appdata\fR
+.RS 4
+A no\-purpose pointer free to use for the library user\&. Freeing the pool simply discards the pointer\&.
+.RE
+.PP
+\fBStringpool ss\fR
+.RS 4
+The pool of unified strings\&.
+.RE
+.PP
+\fBReldep *rels\fR
+.RS 4
+The pool of unified relation dependencies\&.
+.RE
+.PP
+\fBint nrels\fR
+.RS 4
+Number of allocated relation dependencies\&.
+.RE
+.PP
+\fBRepo **repos\fR
+.RS 4
+The array of repository pointers, indexed by repository Id\&.
+.RE
+.PP
+\fBint nrepos\fR
+.RS 4
+Number of allocated repository array elements, i\&.e\&. the size of the repos array\&.
+.RE
+.PP
+\fBint urepos\fR
+.RS 4
+Number of used (i\&.e\&. non\-zero) repository array elements\&.
+.RE
+.PP
+\fBRepo *installed\fR
+.RS 4
+Pointer to the repo holding the installed packages\&. You are free to read this attribute, but you should use pool_set_installed() if you want to change it\&.
+.RE
+.PP
+\fBSolvable *solvables\fR
+.RS 4
+The array of Solvable objects\&.
+.RE
+.PP
+\fBint nsolvables\fR
+.RS 4
+Number of Solvable objects, i\&.e\&. the size of the solvables array\&. Note that the array may contain freed solvables, in that case the repo pointer of the solvable will be zero\&.
+.RE
+.PP
+\fBint disttype\fR
+.RS 4
+The distribution type of your system, e\&.g\&. DISTTYPE_DEB\&. You are free to read this attribute, but you should use pool_setdisttype() if you want to change it\&.
+.RE
+.PP
+\fBId *whatprovidesdata\fR
+.RS 4
+Multi\-purpose Id storage holding zero terminated arrays of Ids\&. pool_whatprovides() returns an offset into this data\&.
+.RE
+.PP
+\fBMap *considered\fR
+.RS 4
+Optional bitmap that can make the library ignore solvables\&. If a bitmap is set, only solvables that have a set bit in the bitmap at their Id are considered usable\&.
+.RE
+.PP
+\fBint debugmask\fR
+.RS 4
+A mask that defines which debug events should be reported\&. pool_setdebuglevel() sets this mask\&.
+.RE
+.PP
+\fBDatapos pos\fR
+.RS 4
+An object storing some position in the repository data\&. Functions like dataiterator_set_pos() set this object, accessing data with a pseudo solvable Id of SOLVID_POS uses it\&.
+.RE
+.PP
+\fBQueue pooljobs\fR
+.RS 4
+A queue where fixed solver jobs can be stored\&. This jobs are automatically added when solver_solve() is called, they are useful to store configuration data like which packages should be multiversion installed\&.
+.RE
+.SH "CREATION AND DESTRUCTION"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBPool *pool_create()\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a new instance of a pool\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_free(Pool *\fR\fIpool\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Free a pool and all of the data it contains, e\&.g\&. the solvables, repositories, strings\&.
+.SH "DEBUGGING AND ERROR REPORTING"
+.SS "Constants"
+.PP
+\fBSOLV_FATAL\fR
+.RS 4
+Report the error and call \(lqexit(1)\(rq afterwards\&. You cannot mask this level\&. Reports to stderr instead of stdout\&.
+.RE
+.PP
+\fBSOLV_ERROR\fR
+.RS 4
+Used to report errors\&. Reports to stderr instead of stdout\&.
+.RE
+.PP
+\fBSOLV_WARN\fR
+.RS 4
+Used to report warnings\&.
+.RE
+.PP
+\fBSOLV_DEBUG_STATS\fR
+.RS 4
+Used to report statistical data\&.
+.RE
+.PP
+\fBSOLV_DEBUG_RULE_CREATION\fR
+.RS 4
+Used to report information about the solver\(cqs creation of rules\&.
+.RE
+.PP
+\fBSOLV_DEBUG_PROPAGATE\fR
+.RS 4
+Used to report information about the solver\(cqs unit rule propagation process\&.
+.RE
+.PP
+\fBSOLV_DEBUG_ANALYZE\fR
+.RS 4
+Used to report information about the solver\(cqs learnt rule generation mechanism\&.
+.RE
+.PP
+\fBSOLV_DEBUG_UNSOLVABLE\fR
+.RS 4
+Used to report information about the solver dealing with conflicting rules\&.
+.RE
+.PP
+\fBSOLV_DEBUG_SOLUTIONS\fR
+.RS 4
+Used to report information about the solver creating solutions to solve problems\&.
+.RE
+.PP
+\fBSOLV_DEBUG_POLICY\fR
+.RS 4
+Used to report information about the solver searching for an optimal solution\&.
+.RE
+.PP
+\fBSOLV_DEBUG_RESULT\fR
+.RS 4
+Used by the debug functions to output results\&.
+.RE
+.PP
+\fBSOLV_DEBUG_JOB\fR
+.RS 4
+Used to report information about the job rule generation process\&.
+.RE
+.PP
+\fBSOLV_DEBUG_SOLVER\fR
+.RS 4
+Used to report information about what the solver is currently doing\&.
+.RE
+.PP
+\fBSOLV_DEBUG_TRANSACTION\fR
+.RS 4
+Used to report information about the transaction generation and ordering process\&.
+.RE
+.PP
+\fBSOLV_DEBUG_TO_STDERR\fR
+.RS 4
+Write debug messages to stderr instead of stdout\&.
+.RE
+.SS "Functions"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_debug(Pool *\fR\fIpool\fR\fB, int\fR \fItype\fR\fB, const char *\fR\fIformat\fR\fB, \&.\&.\&.)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Report a message of the type \fItype\fR\&. You can filter debug messages by setting a debug mask\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_setdebuglevel(Pool *\fR\fIpool\fR\fB, int\fR \fIlevel\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set a predefined debug mask\&. A higher level generally means more bits in the mask are set, thus more messages are printed\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_setdebugmask(Pool *\fR\fIpool\fR\fB, int\fR \fImask\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the debug mask to filter debug messages\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_error(Pool *\fR\fIpool\fR\fB, int\fR \fIret\fR\fB, const char *\fR\fIformat\fR\fB, \&.\&.\&.)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the pool\(cqs error string\&. The \fIret\fR value is simply used as a return value of the function so that you can write code like return pool_error(\&...);\&. If the debug mask contains the \fBSOLV_ERROR\fR bit, pool_debug() is also called with the message and type \fBSOLV_ERROR\fR\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBextern char *pool_errstr(Pool *\fR\fIpool\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the current error string stored in the pool\&. Like with the libc\(cqs errno value, the string is only meaningful after a function returned an error\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_setdebugcallback(Pool *\fR\fIpool\fR\fB, void (*\fR\fIdebugcallback\fR\fB)(Pool *, void *\fR\fIdata\fR\fB, int\fR \fItype\fR\fB, const char *\fR\fIstr\fR\fB), void *\fR\fIdebugcallbackdata\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set a custom debug callback function\&. Instead of writing to stdout or stderr, the callback function will be called\&.
+.SH "POOL CONFIGURATION"
+.SS "Constants"
+.PP
+\fBDISTTYPE_RPM\fR
+.RS 4
+Used for systems which use rpm as low level package manager\&.
+.RE
+.PP
+\fBDISTTYPE_DEB\fR
+.RS 4
+Used for systems which use dpkg as low level package manager\&.
+.RE
+.PP
+\fBDISTTYPE_ARCH\fR
+.RS 4
+Used for systems which use the arch linux package manager\&.
+.RE
+.PP
+\fBDISTTYPE_HAIKU\fR
+.RS 4
+Used for systems which use haiku packages\&.
+.RE
+.PP
+\fBPOOL_FLAG_PROMOTEEPOCH\fR
+.RS 4
+Promote the epoch of the providing dependency to the requesting dependency if it does not contain an epoch\&. Used at some time in old rpm versions, modern systems should never need this\&.
+.RE
+.PP
+\fBPOOL_FLAG_FORBIDSELFCONFLICTS\fR
+.RS 4
+Disallow the installation of packages that conflict with themselves\&. Debian always allows self\-conflicting packages, rpm used to forbid them but switched to also allowing them recently\&.
+.RE
+.PP
+\fBPOOL_FLAG_OBSOLETEUSESPROVIDES\fR
+.RS 4
+Make obsolete type dependency match against provides instead of just the name and version of packages\&. Very old versions of rpm used the name/version, then it got switched to provides and later switched back again to just name/version\&.
+.RE
+.PP
+\fBPOOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES\fR
+.RS 4
+An implicit obsoletes is the internal mechanism to remove the old package on an update\&. The default is to remove all packages with the same name, rpm\-5 switched to also removing packages providing the same name\&.
+.RE
+.PP
+\fBPOOL_FLAG_OBSOLETEUSESCOLORS\fR
+.RS 4
+Rpm\(cqs multilib implementation (used in RedHat and Fedora) distinguishes between 32bit and 64bit packages (the terminology is that they have a different color)\&. If obsoleteusescolors is set, packages with different colors will not obsolete each other\&.
+.RE
+.PP
+\fBPOOL_FLAG_IMPLICITOBSOLETEUSESCOLORS\fR
+.RS 4
+Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if packages of the same name can be installed in parallel\&. For current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true (this is the default if FEDORA is defined when libsolv is compiled)\&.
+.RE
+.PP
+\fBPOOL_FLAG_NOINSTALLEDOBSOLETES\fR
+.RS 4
+New versions of rpm consider the obsoletes of installed packages when checking for dependency, thus you may not install a package that is obsoleted by some other installed package, unless you also erase the other package\&.
+.RE
+.PP
+\fBPOOL_FLAG_HAVEDISTEPOCH\fR
+.RS 4
+Mandriva added a new field called distepoch that gets checked in version comparison if the epoch/version/release of two packages are the same\&.
+.RE
+.PP
+\fBPOOL_FLAG_NOOBSOLETESMULTIVERSION\fR
+.RS 4
+If a package is installed in multiversionmode, rpm used to ignore both the implicit obsoletes and the obsolete dependency of a package\&. This was changed to ignoring just the implicit obsoletes, thus you may install multiple versions of the same name, but obsoleted packages still get removed\&.
+.RE
+.PP
+\fBPOOL_FLAG_ADDFILEPROVIDESFILTERED\fR
+.RS 4
+Make the addfileprovides method only add files from the standard locations (i\&.e\&. the \(lqbin\(rq and \(lqetc\(rq directories)\&. This is useful if you have only few packages that use non\-standard file dependencies, but you still want the fast speed that addfileprovides() generates\&.
+.RE
+.SS "Functions"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_setdisttype(Pool *\fR\fIpool\fR\fB, int\fR \fIdisttype\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the package type of your system\&. The disttype is used for example to define package comparison semantics\&. Libsolv\(cqs default disttype should match the package manager of your system, so you only need to use this function if you want to use the library to solve packaging problems for different systems\&. The Function returns the old disttype on success, and \-1 if the new disttype is not supported\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_set_flag(Pool *\fR\fIpool\fR\fB, int\fR \fIflag\fR\fB, int\fR \fIvalue\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set a flag to a new value\&. Returns the old value of the flag\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_get_flag(Pool *\fR\fIpool\fR\fB, int\fR \fIflag\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Get the value of a pool flag\&. See the constants section about the meaning of the flags\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_set_rootdir(Pool *\fR\fIpool\fR\fB, const char *\fR\fIrootdir\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set a specific root directory\&. Some library functions support a flag that tells the function to prepend the rootdir to file and directory names\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_get_rootdir(Pool *\fR\fIpool\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the current value of the root directory\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *pool_prepend_rootdir(Pool *\fR\fIpool\fR\fB, const char *\fR\fIdir\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Prepend the root directory to the \fIdir\fR argument string\&. The returned string has been newly allocated and needs to be freed after use\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *pool_prepend_rootdir_tmp(Pool *\fR\fIpool\fR\fB, const char *\fR\fIdir\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as pool_prepend_rootdir, but uses the pool\(cqs temporary space for allocation\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_set_installed(Pool *\fR\fIpool\fR\fB, Repo *\fR\fIrepo\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set which repository should be treated as the \(lqinstalled\(rq repository, i\&.e\&. the one that holds information about the installed packages\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_set_languages(Pool *\fR\fIpool\fR\fB, const char **\fR\fIlanguages\fR\fB, int\fR \fInlanguages\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the language of your system\&. The library provides lookup functions that return localized strings, for example for package descriptions\&. You can set an array of languages to provide a fallback mechanism if one language is not available\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_setarch(Pool *\fR\fIpool\fR\fB, const char *\fR\fIarch\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the architecture of your system\&. The architecture is used to determine which packages are installable and which packages cannot be installed\&. The \fIarch\fR argument is normally the \(lqmachine\(rq value of the \(lquname\(rq system call\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_setarchpolicy(Pool *, const char *)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the architecture policy for your system\&. This is the general version of pool_setarch (in fact pool_setarch calls pool_setarchpolicy internally)\&. See the section about architecture policies for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_addvendorclass(Pool *\fR\fIpool\fR\fB, const char **\fR\fIvendorclass\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a new vendor equivalence class to the system\&. A vendor equivalence class defines if an installed package of one vendor can be replaced by a package coming from a different vendor\&. The \fIvendorclass\fR argument must be a NULL terminated array of strings\&. See the section about vendor policies for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_setvendorclasses(Pool *\fR\fIpool\fR\fB, const char **\fR\fIvendorclasses\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set all allowed vendor equivalences\&. The vendorclasses argument must be an NULL terminated array consisting of all allowed classes concatenated\&. Each class itself must be NULL terminated, thus the last class ends with two NULL elements, one to finish the class and one to finish the list of classes\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_set_custom_vendorcheck(Pool *\fR\fIpool\fR\fB, int (*\fR\fIvendorcheck\fR\fB)(Pool *, Solvable *, Solvable *))\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Define a custom vendor check mechanism\&. You can use this if libsolv\(cqs internal vendor equivalence class mechanism does not match your needs\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_setloadcallback(Pool *\fR\fIpool\fR\fB, int (*\fR\fIcb\fR\fB)(Pool *, Repodata *, void *), void *\fR\fIloadcbdata\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Define a callback function that gets called when repository metadata needs to be loaded on demand\&. See the section about on demand loading in the libsolv\-repodata manual\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_setnamespacecallback(Pool *\fR\fIpool\fR\fB, Id (*\fR\fIcb\fR\fB)(Pool *, void *,\fR \fIId\fR\fB,\fR \fIId\fR\fB), void *\fR\fInscbdata\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Define a callback function to implement custom namespace support\&. See the section about namespace dependencies\&.
+.SH "ID POOL MANAGEMENT"
+.SS "Constants"
+.PP
+\fBID_EMPTY\fR
+.RS 4
+The Id of the empty string, it is always Id 1\&.
+.RE
+.PP
+\fBREL_LT\fR
+.RS 4
+Represents a \(lq<\(rq relation\&.
+.RE
+.PP
+\fBREL_EQ\fR
+.RS 4
+Represents a \(lq=\(rq relation\&.
+.RE
+.PP
+\fBREL_GT\fR
+.RS 4
+Represents a \(lq>\(rq relation\&. You can use combinations of REL_GT, REL_EQ, and REL_LT or\-ed together to create any relation you like\&.
+.RE
+.PP
+\fBREL_AND\fR
+.RS 4
+A boolean AND operation, the \(lqname\(rq and \(lqevr\(rq parts of the relation can be two sub\-dependencies\&. Packages must match both parts of the dependency\&.
+.RE
+.PP
+\fBREL_OR\fR
+.RS 4
+A boolean OR operation, the \(lqname\(rq and \(lqevr\(rq parts of the relation can be two sub\-dependencies\&. Packages can match any part of the dependency\&.
+.RE
+.PP
+\fBREL_WITH\fR
+.RS 4
+Like REL_AND, but packages must match both dependencies simultaneously\&. See the section about boolean dependencies about more information\&.
+.RE
+.PP
+\fBREL_NAMESPACE\fR
+.RS 4
+A special namespace relation\&. See the section about namespace dependencies for more information\&.
+.RE
+.PP
+\fBREL_ARCH\fR
+.RS 4
+An architecture filter dependency\&. The \(lqname\(rq part of the relation is a sub\-dependency, the \(lqevr\(rq part is the Id of an architecture that the matching packages must have (note that this is an exact match ignoring architecture policies)\&.
+.RE
+.PP
+\fBREL_FILECONFLICT\fR
+.RS 4
+An internal file conflict dependency used to represent file conflicts\&. See the pool_add_fileconflicts_deps() function\&.
+.RE
+.PP
+\fBREL_COND\fR
+.RS 4
+A conditional dependency, the \(lqname\(rq sub\-dependency is only considered if the \(lqevr\(rq sub\-dependency is fulfilled\&. See the section about boolean dependencies about more information\&.
+.RE
+.PP
+\fBREL_COMPAT\fR
+.RS 4
+A compat dependency used in Haiku to represent version ranges\&. The \(lqname\(rq part is the actual version, the \(lqevr\(rq part is the backwards compatibility version\&.
+.RE
+.SS "Functions"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId pool_str2id(Pool *\fR\fIpool\fR\fB, const char *\fR\fIstr\fR\fB, int\fR \fIcreate\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add a string to the pool of unified strings, returning the Id of the string\&. If \fIcreate\fR is zero, new strings will not be added to the pool, instead Id 0 is returned\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId pool_strn2id(Pool *\fR\fIpool\fR\fB, const char *\fR\fIstr\fR\fB, unsigned int\fR \fIlen\fR\fB, int\fR \fIcreate\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as pool_str2id, but only \fIlen\fR characters of the string are used\&. This can be used to add substrings to the pool\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId pool_rel2id(Pool *\fR\fIpool\fR\fB, Id\fR \fIname\fR\fB, Id\fR \fIevr\fR\fB, int\fR \fIflags\fR\fB, int\fR \fIcreate\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a relational dependency from to other dependencies, \fIname\fR and \fIevr\fR, and a \fIflag\fR\&. See the \fBREL_\fR constants for the supported flags\&. As with pool_str2id, \fIcreate\fR defines if new dependencies will get added or Id zero will be returned instead\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId pool_id2langid(Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB, const char *\fR\fIlang\fR\fB, int\fR \fIcreate\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Attach a language suffix to a string Id\&. This function can be used to create language keyname Ids from keynames, it is functional equivalent to converting the \fIid\fR argument to a string, adding a \(lq:\(rq character and the \fIlang\fR argument to the string and then converting the result back into an Id\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_id2str(const Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert an Id back into a string\&. If the Id is a relational Id, the \(lqname\(rq part will be converted instead\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_id2rel(const Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the relation string of a relational Id\&. Returns an empty string if the passed Id is not a relation\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_id2evr(const Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the \(lqevr\(rq part of a relational Id as string\&. Returns an empty string if the passed Id is not a relation\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_dep2str(Pool *\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert an Id back into a string\&. If the passed Id belongs to a relation, a string representing the relation is returned\&. Note that in that case the string is allocated on the pool\(cqs temporary space\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_freeidhashes(Pool *\fR\fIpool\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Free the hashes used to unify strings and relations\&. You can use this function to save memory if you know that you will no longer create new strings and relations\&.
+.SH "SOLVABLE FUNCTIONS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *pool_id2solvable(const Pool *\fR\fIpool\fR\fB, Id\fR \fIp\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert a solvable Id into a pointer to the solvable data\&. Note that the pointer may become invalid if new solvables are created or old solvables deleted, because the array storing all solvables may get reallocated\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_solvid2str(Pool *\fR\fIpool\fR\fB, Id\fR \fIp\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string representing the solvable with the Id \fIp\fR\&. The string will be some canonical representation of the solvable, usually a combination of the name, the version, and the architecture\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_solvable2str(Pool *\fR\fIpool\fR\fB, Solvable *\fR\fIs\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as pool_solvid2str, but instead of the Id, a pointer to the solvable is passed\&.
+.SH "DEPENDENCY MATCHING"
+.SS "Constants"
+.PP
+\fBEVRCMP_COMPARE\fR
+.RS 4
+Compare all parts of the version, treat missing parts as empty strings\&.
+.RE
+.PP
+\fBEVRCMP_MATCH_RELEASE\fR
+.RS 4
+A special mode for rpm version string matching\&. If a version misses a release part, it matches all releases\&. In that case the special values \(lq\-2\(rq and \(lq2\(rq are returned, depending on which of the two versions did not have a release part\&.
+.RE
+.PP
+\fBEVRCMP_MATCH\fR
+.RS 4
+A generic match, missing parts always match\&.
+.RE
+.PP
+\fBEVRCMP_COMPARE_EVONLY\fR
+.RS 4
+Only compare the epoch and the version parts, ignore the release part\&.
+.RE
+.SS "Functions"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_evrcmp(const Pool *\fR\fIpool\fR\fB, Id\fR \fIevr1id\fR\fB, Id\fR \fIevr2id\fR\fB, int\fR \fImode\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Compare two version Ids, return \-1 if the first version is less than the second version, 0 if they are identical, and 1 if the first version is bigger than the second one\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_evrcmp_str(const Pool *\fR\fIpool\fR\fB, const char *\fR\fIevr1\fR\fB, const char *\fR\fIevr2\fR\fB, int\fR \fImode\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as pool_evrcmp(), but uses strings instead of Ids\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_evrmatch(const Pool *\fR\fIpool\fR\fB, Id\fR \fIevrid\fR\fB, const char *\fR\fIepoch\fR\fB, const char *\fR\fIversion\fR\fB, const char *\fR\fIrelease\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Match a version Id against an epoch, a version and a release string\&. Passing NULL means that the part should match everything\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_match_dep(Pool *\fR\fIpool\fR\fB, Id\fR \fId1\fR\fB, Id\fR \fId2\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Returns \(lq1\(rq if the dependency \fId1\fR (the provider) is matched by the dependency \fId2\fR, otherwise \(lq0\(rq is returned\&. For two dependencies to match, both the \(lqname\(rq parts must match and the version range described by the \(lqevr\(rq parts must overlap\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_match_nevr(Pool *\fR\fIpool\fR\fB, Solvable *\fR\fIs\fR\fB, Id\fR \fId\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Like pool_match_dep, but the provider is the "self\-provides" dependency of the Solvable \fIs\fR, i\&.e\&. the dependency \(lqs→name = s→evr\(rq\&.
+.SH "WHATPROVIDES INDEX"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_createwhatprovides(Pool *\fR\fIpool\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create an index that maps dependency Ids to sets of packages that provide the dependency\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_freewhatprovides(Pool *\fR\fIpool\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Free the whatprovides index to save memory\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId pool_whatprovides(Pool *\fR\fIpool\fR\fB, Id\fR \fId\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return an offset into the Pool\(cqs whatprovidesdata array\&. The solvables with the Ids stored starting at that offset provide the dependency \fId\fR\&. The solvable list is zero terminated\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId *pool_whatprovides_ptr(Pool *\fR\fIpool\fR\fB, Id\fR \fId\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Instead of returning the offset, return the pointer to the Ids stored at that offset\&. Note that this pointer has a very limit validity time, as any call that adds new values to the whatprovidesdata area may reallocate the array\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId pool_queuetowhatprovides(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIq\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Add the contents of the Queue \fIq\fR to the end of the whatprovidesdata array, returning the offset into the array\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_addfileprovides(Pool *\fR\fIpool\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Some package managers like rpm allow dependencies on files contained in other packages\&. To allow libsolv to deal with those dependencies in an efficient way, you need to call the addfileprovides method after creating and reading all repositories\&. This method will scan all dependency for file names and then scan all packages for matching files\&. If a filename has been matched, it will be added to the provides list of the corresponding package\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_addfileprovides_queue(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIidq\fR\fB, Queue *\fR\fIidqinst\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as pool_addfileprovides, but the added Ids are returned in two Queues, \fIidq\fR for all repositories except the one containing the \(lqinstalled\(rq packages, \fIidqinst\fR for the latter one\&. This information can be stored in the meta section of the repositories to speed up the next time the repository is loaded and addfileprovides is called
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_flush_namespaceproviders(Pool *\fR\fIpool\fR\fB, Id\fR \fIns\fR\fB, Id\fR \fIevr\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Clear the cache of the providers for namespace dependencies matching namespace \fIns\fR\&. If the \fIevr\fR argument is non\-zero, the namespace dependency for exactly that dependency is cleared, otherwise all matching namespace dependencies are cleared\&. See the section about Namespace dependencies for further information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_add_fileconflicts_deps(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIconflicts\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Some package managers like rpm report conflicts when a package installation overwrites a file of another installed package with different content\&. As file content information is not stored in the repository metadata, those conflicts can only be detected after the packages are downloaded\&. Libsolv provides a function to check for such conflicts, pool_findfileconflicts()\&. If conflicts are found, they can be added as special \fBREL_FILECONFLICT\fR provides dependencies, so that the solver will know about the conflict when it is re\-run\&.
+.SH "UTILITY FUNCTIONS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *pool_alloctmpspace(Pool *\fR\fIpool\fR\fB, int\fR \fIlen\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Allocate space on the pool\(cqs temporary space area\&. This space has a limited lifetime, it will be automatically freed after a fixed amount (currently 16) of other pool_alloctmpspace() calls are done\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_freetmpspace(Pool *\fR\fIpool\fR\fB, const char *\fR\fIspace\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Give the space allocated with pool_alloctmpspace back to the system\&. You do not have to use this function, as the space is automatically reclaimed, but it can be useful to extend the lifetime of other pointers to the pool\(cqs temporary space area\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_bin2hex(Pool *\fR\fIpool\fR\fB, const unsigned char *\fR\fIbuf\fR\fB, int\fR \fIlen\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert some binary data to hexadecimal, returning a string allocated in the pool\(cqs temporary space area\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *pool_tmpjoin(Pool *\fR\fIpool\fR\fB, const char *\fR\fIstr1\fR\fB, const char *\fR\fIstr2\fR\fB, const char *\fR\fIstr3\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Join three strings and return the result in the pool\(cqs temporary space area\&. You can use NULL arguments if you just want to join less strings\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBchar *pool_tmpappend(Pool *\fR\fIpool\fR\fB, const char *\fR\fIstr1\fR\fB, const char *\fR\fIstr2\fR\fB, const char *\fR\fIstr3\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Like pool_tmpjoin(), but if the first argument is the last allocated space in the pool\(cqs temporary space area, it will be replaced with the result of the join and no new temporary space slot will be used\&. Thus you can join more than three strings by a combination of one pool_tmpjoin() and multiple pool_tmpappend() calls\&. Note that the \fIstr1\fR pointer is no longer usable after the call\&.
+.SH "DATA LOOKUP"
+.SS "Constants"
+.PP
+\fBSOLVID_POS\fR
+.RS 4
+Use the data position stored in the pool for the lookup instead of looking up the data of a solvable\&.
+.RE
+.PP
+\fBSOLVID_META\fR
+.RS 4
+Use the data stored in the meta section of a repository (or repodata area) instead of looking up the data of a solvable\&. This constant does not work for the pool\(cqs lookup functions, use it for the repo\(cqs or repodata\(cqs lookup functions instead\&. It\(cqs just listed for completeness\&.
+.RE
+.SS "Functions"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_lookup_str(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the string value stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBunsigned long long pool_lookup_num(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInotfound\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the 64bit unsigned number stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&. If no such number is found, the value of the \fInotfound\fR argument is returned instead\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId pool_lookup_id(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the Id stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_lookup_idarray(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Queue *\fR\fIq\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Fill the queue \fIq\fR with the content of the Id array stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&. Returns \(lq1\(rq if an array was found, otherwise the queue will be empty and \(lq0\(rq will be returned\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_lookup_void(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Returns \(lq1\(rq if a void value is stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR, otherwise \(lq0\(rq\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_lookup_checksum(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id *\fR\fItypep\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the checksum that is stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&. The type of the checksum will be returned over the \fItypep\fR pointer\&. If no such checksum is found, NULL will be returned and the type will be set to zero\&. Note that the result is stored in the Pool\(cqs temporary space area\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst unsigned char *pool_lookup_bin_checksum(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id *\fR\fItypep\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the checksum that is stored under the attribute \fIkeyname\fR in solvable \fIsolvid\fR\&. Returns the checksum as binary data, you can use the returned type to calculate the length of the checksum\&. No temporary space area is needed\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_lookup_deltalocation(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, unsigned int *\fR\fImedianrp\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+This is a utility lookup function to return the delta location for a delta rpm\&. As solvables cannot store deltas, you have to use SOLVID_POS as argument and set the Pool\(cqs datapos pointer to point to valid delta rpm data\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_search(Pool *\fR\fIpool\fR\fB, Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fImatch\fR\fB, int\fR \fIflags\fR\fB, int (*\fR\fIcallback\fR\fB)(void *\fR\fIcbdata\fR\fB, Solvable *\fR\fIs\fR\fB, Repodata *\fR\fIdata\fR\fB, Repokey *\fR\fIkey\fR\fB, KeyValue *\fR\fIkv\fR\fB), void *\fR\fIcbdata\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Perform a search on all data stored in the pool\&. You can limit the search area by using the \fIsolvid\fR and \fIkeyname\fR arguments\&. The values can be optionally matched against the \fImatch\fR argument, use NULL if you do not want this matching\&. See the Dataiterator manpage about the possible matches modes and the \fIflags\fR argument\&. For all (matching) values, the callback function is called with the \fIcbdata\fR callback argument and the data describing the value\&.
+.SH "JOB AND SELECTION FUNCTIONS"
+.sp
+A Job consists of two Ids, \fIhow\fR and \fIwhat\fR\&. The \fIhow\fR part describes the action, the job flags, and the selection method while the \fIwhat\fR part is in input for the selection\&. A Selection is a queue consisting of multiple jobs (thus the number of elements in the queue must be a multiple of two)\&. See the Solver manpage for more information about jobs\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_job2str(Pool *\fR\fIpool\fR\fB, Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB, Id\fR \fIflagmask\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert a job into a string\&. Useful for debugging purposes\&. The \fIflagmask\fR can be used to mask the flags of the job, use \(lq0\(rq if you do not want to see such flags, \(lq\-1\(rq to see all flags, or a combination of the flags you want to see\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_job2solvables(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIpkgs\fR\fB, Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a list of solvables that the specified job selects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint pool_isemptyupdatejob(Pool *\fR\fIpool\fR\fB, Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return \(lq1\(rq if the job is an update job that does not work with any installed package, i\&.e\&. the job is basically a no\-op\&. You can use this to turn no\-op update jobs into install jobs (as done by package managers like \(lqzypper\(rq)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *pool_selection2str(Pool *\fR\fIpool\fR\fB, Queue *\fR\fIselection\fR\fB, Id\fR \fIflagmask\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Convert a selection into a string\&. Useful for debugging purposes\&. See the pool_job2str() function for the \fIflagmask\fR argument\&.
+.SH "ODDS AND ENDS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_freeallrepos(Pool *\fR\fIpool\fR\fB, int\fR \fIreuseids\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Free all repos from the pool (including all solvables)\&. If \fIreuseids\fR is true, all Ids of the solvables are free to be reused the next time solvables are created\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid pool_clear_pos(Pool *\fR\fIpool\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Clear the data position stored in the pool\&.
+.SH "ARCHITECTURE POLICIES"
+.sp
+An architecture policy defines a list of architectures that can be installed on the system, and also the relationship between them (i\&.e\&. the ordering)\&. Architectures can be delimited with three different characters:
+.PP
+\fB\*(Aq:\*(Aq\fR
+.RS 4
+No relationship between the architectures\&. A package of one architecture can not be replaced with one of the other architecture\&.
+.RE
+.PP
+\fB\*(Aq>\*(Aq\fR
+.RS 4
+The first architecture is better than the second one\&. An installed package of the second architecture may be replaced with one from the first architecture and vice versa\&. The solver will select the better architecture if the versions are the same\&.
+.RE
+.PP
+\fB\*(Aq=\*(Aq\fR
+.RS 4
+The two architectures are freely exchangeable\&. Used to define aliases for architectures\&.
+.RE
+.sp
+An example would be \*(Aqx86_64:i686=athlon>i586\*(Aq\&. This means that x86_64 packages can only be replaced by other x86_64 packages, i686 packages can be replaced by i686 and i586 packages (but i686 packages will be preferred) and athlon is another name for the i686 architecture\&.
+.sp
+You can turn off the architecture replacement checks with the Solver\(cqs SOLVER_FLAG_ALLOW_ARCHCHANGE flag\&.
+.SH "VENDOR POLICIES"
+.sp
+Different vendors often compile packages with different features, so Libsolv only replace installed packages of one vendor with packages coming from the same vendor\&. Also, while the version of a package is normally defined by the upstream project, the release part of the version is set by the vendor\(cqs package maintainer, so it\(cqs not meaningful to do version comparisons for packages coming from different vendors\&.
+.sp
+Vendor in this case means the SOLVABLE_VENDOR string stored in each solvable\&. Sometimes a vendor changes names, or multiple vendors form a group that coordinate their package building, so libsolv offers a way to define that a group of vendors are compatible\&. You do that be defining vendor equivalence classes, packages from a vendor from one class may be replaced with packages from all the other vendors in the class\&.
+.sp
+There can be multiple equivalence classes, the set of allowed vendor changes for an installed package is calculated by building the union of all of the equivalence classes the vendor of the installed package is part of\&.
+.sp
+You can turn off the architecture replacement checks with the Solver\(cqs SOLVER_FLAG_ALLOW_VENDORCHANGE flag\&.
+.SH "BOOLEAN DEPENDENCIES"
+.sp
+Boolean Dependencies allow to build complex expressions from simple dependencies\&. While rpm does not support boolean expressions in dependencies and debian only allows an "OR" expression, libsolv allows arbitrary complex expressions\&. The following basic types are supported:
+.PP
+\fBREL_OR\fR
+.RS 4
+The expression is true if either the first dependency or the second one is true\&. This is useful for package dependencies like \(lqRequires\(rq, where you can specify that either one of the packages need to be installed\&.
+.RE
+.PP
+\fBREL_AND\fR
+.RS 4
+The expression is true if both dependencies are true\&. The packages fulfilling the dependencies may be different, i\&.e\&. \(lqSupplements: perl AND python\(rq is true if both a package providing perl and a package providing python are installed\&. The solver currently only supports REL_AND in Supplements/Enhances dependencies, in other types of dependencies it gets treated as REL_WITH\&.
+.RE
+.PP
+\fBREL_WITH\fR
+.RS 4
+The expression is true if both dependencies are true and are fulfilled by the same package\&. Thus \(lqSupplements: perl AND python\(rq would only be true if a package is installed that provides both dependencies (some kind of multi\-language interpreter)\&.
+.RE
+.PP
+\fBREL_COND\fR
+.RS 4
+The expression is true if the first dependency is true or the second dependency is false\&. Libsolv currently does not support this type of dependency in the solver code\&.
+.RE
+.sp
+Each sub\-dependency of a boolean dependency can in turn be a boolean dependency, so you can chain them to create complex dependencies\&.
+.SH "NAMESPACE DEPENDENCIES"
+.sp
+Namespace dependencies can be used to implement dependencies on attributes external to libsolv\&. An example would be a dependency on the language set by the user\&. This types of dependencies are usually only used for \(lqConflicts\(rq or \(lqSupplements\(rq dependencies, as the underlying package manager does not know how to deal with them\&.
+.sp
+If the library needs to evaluate a namespace dependency, it calls the namespace callback function set in the pool\&. The callback function can return a set of packages that \(lqprovide\(rq the dependency\&. If the dependency is provided by the system, the returned set should consist of just the system solvable (Solvable Id 1)\&.
+.sp
+The returned set of packages must be returned as offset into the whatprovidesdata array\&. You can use the pool_queuetowhatprovides function to convert a queue into such an offset\&. To ease programming the callback function, the return values \(lq0\(rq and \(lq1\(rq are not interpreted as an offset\&. \(lq0\(rq means that no package is in the return set, \(lq1\(rq means that just the system solvable is in the set\&.
+.sp
+The returned set is cached, so that for each namespace dependency the callback is just called once\&. If you need to flush the cache (maybe because the user has selected a different language), use the pool_flush_namespaceproviders() function\&.
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+Libsolv-Pool(3)
+===============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+libsolv-pool - Libsolv's pool object
+
+
+Public Attributes
+-----------------
+
+*void *appdata*::
+A no-purpose pointer free to use for the library user. Freeing the pool
+simply discards the pointer.
+
+*Stringpool ss*::
+The pool of unified strings.
+
+*Reldep *rels*::
+The pool of unified relation dependencies.
+
+*int nrels*::
+Number of allocated relation dependencies.
+
+*Repo **repos*::
+The array of repository pointers, indexed by repository Id.
+
+*int nrepos*::
+Number of allocated repository array elements, i.e. the size
+of the repos array.
+
+*int urepos*::
+Number of used (i.e. non-zero) repository array elements.
+
+*Repo *installed*::
+Pointer to the repo holding the installed packages. You are free to read
+this attribute, but you should use pool_set_installed() if you want to
+change it.
+
+*Solvable *solvables*::
+The array of Solvable objects.
+
+*int nsolvables*::
+Number of Solvable objects, i.e. the size of the solvables array. Note
+that the array may contain freed solvables, in that case the repo pointer
+of the solvable will be zero.
+
+*int disttype*::
+The distribution type of your system, e.g. DISTTYPE_DEB. You are free to
+read this attribute, but you should use pool_setdisttype() if you want to
+change it.
+
+*Id *whatprovidesdata*::
+Multi-purpose Id storage holding zero terminated arrays of Ids.
+pool_whatprovides() returns an offset into this data.
+
+*Map *considered*::
+Optional bitmap that can make the library ignore solvables. If a bitmap is
+set, only solvables that have a set bit in the bitmap at their Id are
+considered usable.
+
+*int debugmask*::
+A mask that defines which debug events should be reported.
+pool_setdebuglevel() sets this mask.
+
+*Datapos pos*::
+An object storing some position in the repository data. Functions like
+dataiterator_set_pos() set this object, accessing data with a pseudo
+solvable Id of SOLVID_POS uses it.
+
+*Queue pooljobs*::
+A queue where fixed solver jobs can be stored. This jobs are automatically
+added when solver_solve() is called, they are useful to store configuration
+data like which packages should be multiversion installed.
+
+Creation and Destruction
+------------------------
+
+ Pool *pool_create();
+
+Create a new instance of a pool.
+
+ void pool_free(Pool *pool);
+
+Free a pool and all of the data it contains, e.g. the solvables,
+repositories, strings.
+
+
+Debugging and error reporting
+-----------------------------
+
+=== Constants ===
+
+*SOLV_FATAL*::
+Report the error and call ``exit(1)'' afterwards. You cannot mask this
+level. Reports to stderr instead of stdout.
+
+*SOLV_ERROR*::
+Used to report errors. Reports to stderr instead of stdout.
+
+*SOLV_WARN*::
+Used to report warnings.
+
+*SOLV_DEBUG_STATS*::
+Used to report statistical data.
+
+*SOLV_DEBUG_RULE_CREATION*::
+Used to report information about the solver's creation of rules.
+
+*SOLV_DEBUG_PROPAGATE*::
+Used to report information about the solver's unit rule propagation
+process.
+
+*SOLV_DEBUG_ANALYZE*::
+Used to report information about the solver's learnt rule generation
+mechanism.
+
+*SOLV_DEBUG_UNSOLVABLE*::
+Used to report information about the solver dealing with conflicting
+rules.
+
+*SOLV_DEBUG_SOLUTIONS*::
+Used to report information about the solver creating solutions to solve
+problems.
+
+*SOLV_DEBUG_POLICY*::
+Used to report information about the solver searching for an optimal
+solution.
+
+*SOLV_DEBUG_RESULT*::
+Used by the debug functions to output results.
+
+*SOLV_DEBUG_JOB*::
+Used to report information about the job rule generation process.
+
+*SOLV_DEBUG_SOLVER*::
+Used to report information about what the solver is currently
+doing.
+
+*SOLV_DEBUG_TRANSACTION*::
+Used to report information about the transaction generation and
+ordering process.
+
+*SOLV_DEBUG_TO_STDERR*::
+Write debug messages to stderr instead of stdout.
+
+=== Functions ===
+
+ void pool_debug(Pool *pool, int type, const char *format, ...);
+
+Report a message of the type _type_. You can filter debug messages by
+setting a debug mask.
+
+ void pool_setdebuglevel(Pool *pool, int level);
+
+Set a predefined debug mask. A higher level generally means more bits in
+the mask are set, thus more messages are printed.
+
+ void pool_setdebugmask(Pool *pool, int mask);
+
+Set the debug mask to filter debug messages.
+
+ int pool_error(Pool *pool, int ret, const char *format, ...);
+
+Set the pool's error string. The _ret_ value is simply used as a
+return value of the function so that you can write code like
++return pool_error(...);+. If the debug mask contains the *SOLV_ERROR*
+bit, pool_debug() is also called with the message and type *SOLV_ERROR*.
+
+ extern char *pool_errstr(Pool *pool);
+
+Return the current error string stored in the pool. Like with the libc's
+errno value, the string is only meaningful after a function returned an
+error.
+
+ void pool_setdebugcallback(Pool *pool, void (*debugcallback)(Pool *, void *data, int type, const char *str), void *debugcallbackdata);
+
+Set a custom debug callback function. Instead of writing to stdout or
+stderr, the callback function will be called.
+
+
+Pool configuration
+------------------
+
+=== Constants ===
+
+*DISTTYPE_RPM*::
+Used for systems which use rpm as low level package manager.
+
+*DISTTYPE_DEB*::
+Used for systems which use dpkg as low level package manager.
+
+*DISTTYPE_ARCH*::
+Used for systems which use the arch linux package manager.
+
+*DISTTYPE_HAIKU*::
+Used for systems which use haiku packages.
+
+*POOL_FLAG_PROMOTEEPOCH*::
+Promote the epoch of the providing dependency to the requesting
+dependency if it does not contain an epoch. Used at some time
+in old rpm versions, modern systems should never need this.
+
+*POOL_FLAG_FORBIDSELFCONFLICTS*::
+Disallow the installation of packages that conflict with themselves.
+Debian always allows self-conflicting packages, rpm used to forbid
+them but switched to also allowing them recently.
+
+*POOL_FLAG_OBSOLETEUSESPROVIDES*::
+Make obsolete type dependency match against provides instead of
+just the name and version of packages. Very old versions of rpm
+used the name/version, then it got switched to provides and later
+switched back again to just name/version.
+
+*POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES*::
+An implicit obsoletes is the internal mechanism to remove the
+old package on an update. The default is to remove all packages
+with the same name, rpm-5 switched to also removing packages
+providing the same name.
+
+*POOL_FLAG_OBSOLETEUSESCOLORS*::
+Rpm's multilib implementation (used in RedHat and Fedora)
+distinguishes between 32bit and 64bit packages (the terminology
+is that they have a different color). If obsoleteusescolors is
+set, packages with different colors will not obsolete each other.
+
+*POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS*::
+Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if
+packages of the same name can be installed in parallel. For
+current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be
+false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true
+(this is the default if FEDORA is defined when libsolv is
+compiled).
+
+*POOL_FLAG_NOINSTALLEDOBSOLETES*::
+New versions of rpm consider the obsoletes of installed packages
+when checking for dependency, thus you may not install a package
+that is obsoleted by some other installed package, unless you
+also erase the other package.
+
+*POOL_FLAG_HAVEDISTEPOCH*::
+Mandriva added a new field called distepoch that gets checked in
+version comparison if the epoch/version/release of two packages
+are the same.
+
+*POOL_FLAG_NOOBSOLETESMULTIVERSION*::
+If a package is installed in multiversionmode, rpm used to ignore
+both the implicit obsoletes and the obsolete dependency of a
+package. This was changed to ignoring just the implicit obsoletes,
+thus you may install multiple versions of the same name, but
+obsoleted packages still get removed.
+
+*POOL_FLAG_ADDFILEPROVIDESFILTERED*::
+Make the addfileprovides method only add files from the standard
+locations (i.e. the ``bin'' and ``etc'' directories). This is
+useful if you have only few packages that use non-standard file
+dependencies, but you still want the fast speed that addfileprovides()
+generates.
+
+
+=== Functions ===
+ int pool_setdisttype(Pool *pool, int disttype);
+
+Set the package type of your system. The disttype is used for example
+to define package comparison semantics. Libsolv's default disttype
+should match the package manager of your system, so you only need to
+use this function if you want to use the library to solve packaging
+problems for different systems. The Function returns the old
+disttype on success, and -1 if the new disttype is not supported.
+
+ int pool_set_flag(Pool *pool, int flag, int value);
+
+Set a flag to a new value. Returns the old value of the flag.
+
+ int pool_get_flag(Pool *pool, int flag);
+
+Get the value of a pool flag. See the constants section about the meaning
+of the flags.
+
+ void pool_set_rootdir(Pool *pool, const char *rootdir);
+
+Set a specific root directory. Some library functions support a flag that
+tells the function to prepend the rootdir to file and directory names.
+
+ const char *pool_get_rootdir(Pool *pool);
+
+Return the current value of the root directory.
+
+ char *pool_prepend_rootdir(Pool *pool, const char *dir);
+
+Prepend the root directory to the _dir_ argument string. The returned
+string has been newly allocated and needs to be freed after use.
+
+ char *pool_prepend_rootdir_tmp(Pool *pool, const char *dir);
+
+Same as pool_prepend_rootdir, but uses the pool's temporary space for
+allocation.
+
+ void pool_set_installed(Pool *pool, Repo *repo);
+
+Set which repository should be treated as the ``installed'' repository,
+i.e. the one that holds information about the installed packages.
+
+ void pool_set_languages(Pool *pool, const char **languages, int nlanguages);
+
+Set the language of your system. The library provides lookup functions that
+return localized strings, for example for package descriptions. You can
+set an array of languages to provide a fallback mechanism if one language
+is not available.
+
+ void pool_setarch(Pool *pool, const char *arch);
+
+Set the architecture of your system. The architecture is used to determine
+which packages are installable and which packages cannot be installed.
+The _arch_ argument is normally the ``machine'' value of the ``uname''
+system call.
+
+ void pool_setarchpolicy(Pool *, const char *);
+
+Set the architecture policy for your system. This is the general version
+of pool_setarch (in fact pool_setarch calls pool_setarchpolicy internally).
+See the section about architecture policies for more information.
+
+ void pool_addvendorclass(Pool *pool, const char **vendorclass);
+
+Add a new vendor equivalence class to the system. A vendor equivalence class
+defines if an installed package of one vendor can be replaced by a package
+coming from a different vendor. The _vendorclass_ argument must be a
+NULL terminated array of strings. See the section about vendor policies for
+more information.
+
+ void pool_setvendorclasses(Pool *pool, const char **vendorclasses);
+
+Set all allowed vendor equivalences. The vendorclasses argument must be an
+NULL terminated array consisting of all allowed classes concatenated.
+Each class itself must be NULL terminated, thus the last class ends with
+two NULL elements, one to finish the class and one to finish the list
+of classes.
+
+ void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(Pool *, Solvable *, Solvable *));
+
+Define a custom vendor check mechanism. You can use this if libsolv's
+internal vendor equivalence class mechanism does not match your needs.
+
+ void pool_setloadcallback(Pool *pool, int (*cb)(Pool *, Repodata *, void *), void *loadcbdata);
+
+Define a callback function that gets called when repository metadata needs
+to be loaded on demand. See the section about on demand loading in the
+libsolv-repodata manual.
+
+ void pool_setnamespacecallback(Pool *pool, Id (*cb)(Pool *, void *, Id, Id), void *nscbdata);
+
+Define a callback function to implement custom namespace support. See the
+section about namespace dependencies.
+
+
+Id pool management
+------------------
+=== Constants ===
+
+*ID_EMPTY*::
+The Id of the empty string, it is always Id 1.
+
+*REL_LT*::
+Represents a ``<'' relation.
+
+*REL_EQ*::
+Represents a ``='' relation.
+
+*REL_GT*::
+Represents a ``>'' relation. You can use combinations of REL_GT, REL_EQ,
+and REL_LT or-ed together to create any relation you like.
+
+*REL_AND*::
+A boolean AND operation, the ``name'' and ``evr'' parts of the relation can
+be two sub-dependencies. Packages must match both parts of the dependency.
+
+*REL_OR*::
+A boolean OR operation, the ``name'' and ``evr'' parts of the relation can
+be two sub-dependencies. Packages can match any part of the dependency.
+
+*REL_WITH*::
+Like REL_AND, but packages must match both dependencies simultaneously. See
+the section about boolean dependencies about more information.
+
+*REL_NAMESPACE*::
+A special namespace relation. See the section about namespace dependencies
+for more information.
+
+*REL_ARCH*::
+An architecture filter dependency. The ``name'' part of the relation is a
+sub-dependency, the ``evr'' part is the Id of an architecture that the
+matching packages must have (note that this is an exact match ignoring
+architecture policies).
+
+*REL_FILECONFLICT*::
+An internal file conflict dependency used to represent file conflicts. See
+the pool_add_fileconflicts_deps() function.
+
+*REL_COND*::
+A conditional dependency, the ``name'' sub-dependency is only considered if
+the ``evr'' sub-dependency is fulfilled. See the section about boolean
+dependencies about more information.
+
+*REL_COMPAT*::
+A compat dependency used in Haiku to represent version ranges. The
+``name'' part is the actual version, the ``evr'' part is the backwards
+compatibility version.
+
+=== Functions ===
+ Id pool_str2id(Pool *pool, const char *str, int create);
+
+Add a string to the pool of unified strings, returning the Id of the string.
+If _create_ is zero, new strings will not be added to the pool, instead
+Id 0 is returned.
+
+ Id pool_strn2id(Pool *pool, const char *str, unsigned int len, int create);
+
+Same as pool_str2id, but only _len_ characters of the string are used. This
+can be used to add substrings to the pool.
+
+ Id pool_rel2id(Pool *pool, Id name, Id evr, int flags, int create);
+
+Create a relational dependency from to other dependencies, _name_ and _evr_,
+and a _flag_. See the *REL_* constants for the supported flags. As with
+pool_str2id, _create_ defines if new dependencies will get added or Id zero
+will be returned instead.
+
+ Id pool_id2langid(Pool *pool, Id id, const char *lang, int create);
+
+Attach a language suffix to a string Id. This function can be used to
+create language keyname Ids from keynames, it is functional equivalent
+to converting the _id_ argument to a string, adding a ``:'' character
+and the _lang_ argument to the string and then converting the result back
+into an Id.
+
+ const char *pool_id2str(const Pool *pool, Id id);
+
+Convert an Id back into a string. If the Id is a relational Id, the
+``name'' part will be converted instead.
+
+ const char *pool_id2rel(const Pool *pool, Id id);
+
+Return the relation string of a relational Id. Returns an empty string if
+the passed Id is not a relation.
+
+ const char *pool_id2evr(const Pool *pool, Id id);
+
+Return the ``evr'' part of a relational Id as string. Returns an empty
+string if the passed Id is not a relation.
+
+ const char *pool_dep2str(Pool *pool, Id id);
+
+Convert an Id back into a string. If the passed Id belongs to a relation,
+a string representing the relation is returned. Note that in that case
+the string is allocated on the pool's temporary space.
+
+ void pool_freeidhashes(Pool *pool);
+
+Free the hashes used to unify strings and relations. You can use this
+function to save memory if you know that you will no longer create new
+strings and relations.
+
+
+Solvable functions
+------------------
+
+ Solvable *pool_id2solvable(const Pool *pool, Id p);
+
+Convert a solvable Id into a pointer to the solvable data. Note that the
+pointer may become invalid if new solvables are created or old solvables
+deleted, because the array storing all solvables may get reallocated.
+
+ const char *pool_solvid2str(Pool *pool, Id p);
+
+Return a string representing the solvable with the Id _p_. The string will
+be some canonical representation of the solvable, usually a combination of
+the name, the version, and the architecture.
+
+ const char *pool_solvable2str(Pool *pool, Solvable *s);
+
+Same as pool_solvid2str, but instead of the Id, a pointer to the solvable
+is passed.
+
+
+Dependency matching
+-------------------
+
+=== Constants ===
+*EVRCMP_COMPARE*::
+Compare all parts of the version, treat missing parts as empty strings.
+
+*EVRCMP_MATCH_RELEASE*::
+A special mode for rpm version string matching. If a version misses a
+release part, it matches all releases. In that case the special values
+``-2'' and ``2'' are returned, depending on which of the two versions
+did not have a release part.
+
+*EVRCMP_MATCH*::
+A generic match, missing parts always match.
+
+*EVRCMP_COMPARE_EVONLY*::
+Only compare the epoch and the version parts, ignore the release part.
+
+=== Functions ===
+ int pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode);
+
+Compare two version Ids, return -1 if the first version is less than the
+second version, 0 if they are identical, and 1 if the first version is
+bigger than the second one.
+
+ int pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode);
+
+Same as pool_evrcmp(), but uses strings instead of Ids.
+
+ int pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release);
+
+Match a version Id against an epoch, a version and a release string. Passing
+NULL means that the part should match everything.
+
+ int pool_match_dep(Pool *pool, Id d1, Id d2);
+
+Returns ``1'' if the dependency _d1_ (the provider) is matched by the
+dependency _d2_, otherwise ``0'' is returned. For two dependencies to
+match, both the ``name'' parts must match and the version range described
+by the ``evr'' parts must overlap.
+
+ int pool_match_nevr(Pool *pool, Solvable *s, Id d);
+
+Like pool_match_dep, but the provider is the "self-provides" dependency
+of the Solvable _s_, i.e. the dependency ``s->name = s->evr''.
+
+
+Whatprovides Index
+------------------
+ void pool_createwhatprovides(Pool *pool);
+
+Create an index that maps dependency Ids to sets of packages that provide the
+dependency.
+
+ void pool_freewhatprovides(Pool *pool);
+
+Free the whatprovides index to save memory.
+
+ Id pool_whatprovides(Pool *pool, Id d);
+
+Return an offset into the Pool's whatprovidesdata array. The solvables with
+the Ids stored starting at that offset provide the dependency _d_. The
+solvable list is zero terminated.
+
+ Id *pool_whatprovides_ptr(Pool *pool, Id d);
+
+Instead of returning the offset, return the pointer to the Ids stored at
+that offset. Note that this pointer has a very limit validity time, as any
+call that adds new values to the whatprovidesdata area may reallocate the
+array.
+
+ Id pool_queuetowhatprovides(Pool *pool, Queue *q);
+
+Add the contents of the Queue _q_ to the end of the whatprovidesdata array,
+returning the offset into the array.
+
+ void pool_addfileprovides(Pool *pool);
+
+Some package managers like rpm allow dependencies on files contained in
+other packages. To allow libsolv to deal with those dependencies in an
+efficient way, you need to call the addfileprovides method after creating
+and reading all repositories. This method will scan all dependency for file
+names and then scan all packages for matching files. If a filename has been
+matched, it will be added to the provides list of the corresponding
+package.
+
+ void pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst);
+
+Same as pool_addfileprovides, but the added Ids are returned in two Queues,
+_idq_ for all repositories except the one containing the ``installed''
+packages, _idqinst_ for the latter one. This information can be stored in
+the meta section of the repositories to speed up the next time the
+repository is loaded and addfileprovides is called
+
+ void pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr);
+
+Clear the cache of the providers for namespace dependencies matching
+namespace _ns_. If the _evr_ argument is non-zero, the namespace dependency
+for exactly that dependency is cleared, otherwise all matching namespace
+dependencies are cleared. See the section about Namespace dependencies
+for further information.
+
+ void pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts);
+
+Some package managers like rpm report conflicts when a package installation
+overwrites a file of another installed package with different content. As
+file content information is not stored in the repository metadata, those
+conflicts can only be detected after the packages are downloaded. Libsolv
+provides a function to check for such conflicts, pool_findfileconflicts().
+If conflicts are found, they can be added as special *REL_FILECONFLICT*
+provides dependencies, so that the solver will know about the conflict when
+it is re-run.
+
+
+Utility functions
+-----------------
+ char *pool_alloctmpspace(Pool *pool, int len);
+
+Allocate space on the pool's temporary space area. This space has a limited
+lifetime, it will be automatically freed after a fixed amount (currently
+16) of other pool_alloctmpspace() calls are done.
+
+ void pool_freetmpspace(Pool *pool, const char *space);
+
+Give the space allocated with pool_alloctmpspace back to the system. You
+do not have to use this function, as the space is automatically reclaimed,
+but it can be useful to extend the lifetime of other pointers to the pool's
+temporary space area.
+
+ const char *pool_bin2hex(Pool *pool, const unsigned char *buf, int len);
+
+Convert some binary data to hexadecimal, returning a string allocated in
+the pool's temporary space area.
+
+ char *pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const char *str3);
+
+Join three strings and return the result in the pool's temporary space
+area. You can use NULL arguments if you just want to join less strings.
+
+ char *pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3);
+
+Like pool_tmpjoin(), but if the first argument is the last allocated space
+in the pool's temporary space area, it will be replaced with the result of
+the join and no new temporary space slot will be used. Thus you can join
+more than three strings by a combination of one pool_tmpjoin() and multiple
+pool_tmpappend() calls. Note that the _str1_ pointer is no longer usable
+after the call.
+
+
+Data lookup
+-----------
+=== Constants ===
+
+*SOLVID_POS*::
+Use the data position stored in the pool for the lookup instead of looking
+up the data of a solvable.
+
+*SOLVID_META*::
+Use the data stored in the meta section of a repository (or repodata
+area) instead of looking up the data of a solvable. This constant does
+not work for the pool's lookup functions, use it for the repo's or
+repodata's lookup functions instead. It's just listed for completeness.
+
+=== Functions ===
+ const char *pool_lookup_str(Pool *pool, Id solvid, Id keyname);
+
+Return the string value stored under the attribute _keyname_ in solvable
+_solvid_.
+
+ unsigned long long pool_lookup_num(Pool *pool, Id solvid, Id keyname, unsigned long long notfound);
+
+Return the 64bit unsigned number stored under the attribute _keyname_ in
+solvable _solvid_. If no such number is found, the value of the _notfound_
+argument is returned instead.
+
+ Id pool_lookup_id(Pool *pool, Id solvid, Id keyname);
+
+Return the Id stored under the attribute _keyname_ in solvable _solvid_.
+
+ int pool_lookup_idarray(Pool *pool, Id solvid, Id keyname, Queue *q);
+
+Fill the queue _q_ with the content of the Id array stored under the
+attribute _keyname_ in solvable _solvid_. Returns ``1'' if an array was
+found, otherwise the queue will be empty and ``0'' will be returned.
+
+ int pool_lookup_void(Pool *pool, Id solvid, Id keyname);
+
+Returns ``1'' if a void value is stored under the attribute _keyname_ in
+solvable _solvid_, otherwise ``0''.
+
+ const char *pool_lookup_checksum(Pool *pool, Id solvid, Id keyname, Id *typep);
+
+Return the checksum that is stored under the attribute _keyname_ in
+solvable _solvid_. The type of the checksum will be returned over the
+_typep_ pointer. If no such checksum is found, NULL will be returned and
+the type will be set to zero. Note that the result is stored in the Pool's
+temporary space area.
+
+ const unsigned char *pool_lookup_bin_checksum(Pool *pool, Id solvid, Id keyname, Id *typep);
+
+Return the checksum that is stored under the attribute _keyname_ in
+solvable _solvid_. Returns the checksum as binary data, you can use the
+returned type to calculate the length of the checksum. No temporary space
+area is needed.
+
+ const char *pool_lookup_deltalocation(Pool *pool, Id solvid, unsigned int *medianrp);
+
+This is a utility lookup function to return the delta location for a delta
+rpm. As solvables cannot store deltas, you have to use SOLVID_POS as
+argument and set the Pool's datapos pointer to point to valid delta rpm
+data.
+
+ void pool_search(Pool *pool, Id solvid, Id keyname, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata);
+
+Perform a search on all data stored in the pool. You can limit the search
+area by using the _solvid_ and _keyname_ arguments. The values can be
+optionally matched against the _match_ argument, use NULL if you do not
+want this matching. See the Dataiterator manpage about the possible matches
+modes and the _flags_ argument. For all (matching) values, the callback
+function is called with the _cbdata_ callback argument and the data
+describing the value.
+
+
+Job and Selection functions
+---------------------------
+A Job consists of two Ids, _how_ and _what_. The _how_ part describes the
+action, the job flags, and the selection method while the _what_ part is
+in input for the selection. A Selection is a queue consisting of multiple
+jobs (thus the number of elements in the queue must be a multiple of two).
+See the Solver manpage for more information about jobs.
+
+ const char *pool_job2str(Pool *pool, Id how, Id what, Id flagmask);
+
+Convert a job into a string. Useful for debugging purposes. The _flagmask_
+can be used to mask the flags of the job, use ``0'' if you do not want to
+see such flags, ``-1'' to see all flags, or a combination of the flags
+you want to see.
+
+ void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what);
+
+Return a list of solvables that the specified job selects.
+
+ int pool_isemptyupdatejob(Pool *pool, Id how, Id what);
+
+Return ``1'' if the job is an update job that does not work with any
+installed package, i.e. the job is basically a no-op. You can use this
+to turn no-op update jobs into install jobs (as done by package managers
+like ``zypper'').
+
+ const char *pool_selection2str(Pool *pool, Queue *selection, Id flagmask);
+
+Convert a selection into a string. Useful for debugging purposes. See the
+pool_job2str() function for the _flagmask_ argument.
+
+
+Odds and Ends
+-------------
+ void pool_freeallrepos(Pool *pool, int reuseids);
+
+Free all repos from the pool (including all solvables). If _reuseids_ is
+true, all Ids of the solvables are free to be reused the next time
+solvables are created.
+
+ void pool_clear_pos(Pool *pool);
+
+Clear the data position stored in the pool.
+
+
+Architecture Policies
+---------------------
+An architecture policy defines a list of architectures that can be
+installed on the system, and also the relationship between them (i.e. the
+ordering). Architectures can be delimited with three different characters:
+
+*\':'*::
+No relationship between the architectures. A package of one architecture
+can not be replaced with one of the other architecture.
+
+*\'>'*::
+The first architecture is better than the second one. An installed package
+of the second architecture may be replaced with one from the first
+architecture and vice versa. The solver will select the better architecture
+if the versions are the same.
+
+*\'='*::
+The two architectures are freely exchangeable. Used to define aliases
+for architectures.
+
+An example would be \'+x86_64:i686=athlon>i586+'. This means that x86_64
+packages can only be replaced by other x86_64 packages, i686 packages
+can be replaced by i686 and i586 packages (but i686 packages will be
+preferred) and athlon is another name for the i686 architecture.
+
+You can turn off the architecture replacement checks with the Solver's
+SOLVER_FLAG_ALLOW_ARCHCHANGE flag.
+
+Vendor Policies
+---------------
+Different vendors often compile packages with different features, so
+Libsolv only replace installed packages of one vendor with packages coming
+from the same vendor. Also, while the version of a package is normally
+defined by the upstream project, the release part of the version is
+set by the vendor's package maintainer, so it's not meaningful to
+do version comparisons for packages coming from different vendors.
+
+Vendor in this case means the SOLVABLE_VENDOR string stored in each
+solvable. Sometimes a vendor changes names, or multiple vendors form a
+group that coordinate their package building, so libsolv offers a way
+to define that a group of vendors are compatible. You do that be
+defining vendor equivalence classes, packages from a vendor from
+one class may be replaced with packages from all the other vendors
+in the class.
+
+There can be multiple equivalence classes, the set of allowed vendor
+changes for an installed package is calculated by building the union
+of all of the equivalence classes the vendor of the installed package
+is part of.
+
+You can turn off the architecture replacement checks with the Solver's
+SOLVER_FLAG_ALLOW_VENDORCHANGE flag.
+
+
+Boolean Dependencies
+--------------------
+Boolean Dependencies allow to build complex expressions from simple
+dependencies. While rpm does not support boolean expressions in
+dependencies and debian only allows an "OR" expression, libsolv
+allows arbitrary complex expressions. The following basic types
+are supported:
+
+*REL_OR*::
+The expression is true if either the first dependency or the second
+one is true. This is useful for package dependencies like ``Requires'',
+where you can specify that either one of the packages need to be
+installed.
+
+*REL_AND*::
+The expression is true if both dependencies are true. The packages
+fulfilling the dependencies may be different, i.e.
+``Supplements: perl AND python'' is true if both a package providing
+perl and a package providing python are installed. The solver currently
+only supports REL_AND in Supplements/Enhances dependencies, in other
+types of dependencies it gets treated as REL_WITH.
+
+*REL_WITH*::
+The expression is true if both dependencies are true and are fulfilled by
+the same package. Thus ``Supplements: perl AND python'' would only be true
+if a package is installed that provides both dependencies (some kind
+of multi-language interpreter).
+
+*REL_COND*::
+The expression is true if the first dependency is true or the second
+dependency is false. Libsolv currently does not support this type of
+dependency in the solver code.
+
+Each sub-dependency of a boolean dependency can in turn be a boolean
+dependency, so you can chain them to create complex dependencies.
+
+
+Namespace Dependencies
+----------------------
+Namespace dependencies can be used to implement dependencies on
+attributes external to libsolv. An example would be a dependency
+on the language set by the user. This types of dependencies are
+usually only used for ``Conflicts'' or ``Supplements'' dependencies,
+as the underlying package manager does not know how to deal with
+them.
+
+If the library needs to evaluate a namespace dependency, it calls
+the namespace callback function set in the pool. The callback
+function can return a set of packages that ``provide'' the
+dependency. If the dependency is provided by the system, the
+returned set should consist of just the system solvable (Solvable
+Id 1).
+
+The returned set of packages must be returned as offset into
+the whatprovidesdata array. You can use the pool_queuetowhatprovides
+function to convert a queue into such an offset. To ease programming
+the callback function, the return values ``0'' and ``1'' are not
+interpreted as an offset. ``0'' means that no package is in the
+return set, ``1'' means that just the system solvable is in the set.
+
+The returned set is cached, so that for each namespace dependency
+the callback is just called once. If you need to flush the cache (maybe
+because the user has selected a different language), use the
+pool_flush_namespaceproviders() function.
+
+
+Author
+------
+Michael Schroeder <mls@suse.de>
+
--- /dev/null
+'\" t
+.\" Title: Libsolv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "LIBSOLV" "3" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+libsolv \- package dependency solver library using a satisfiability algorithm
+.SH "DOCUMENTATION"
+.sp
+The libsolv documentation is split into multiple parts:
+.PP
+\fBlibsolv\-history\fR
+.RS 4
+how the libsolv library came into existence
+.RE
+.PP
+\fBlibsolv\-constantids\fR
+.RS 4
+fixed Ids for often used strings
+.RE
+.PP
+\fBlibsolv\-bindings\fR
+.RS 4
+access libsolv from perl/python/ruby
+.RE
+.PP
+\fBlibsolv\-pool\fR
+.RS 4
+libsolv\(cqs pool object
+.RE
+.SH "POINTER VALIDITY"
+.sp
+Note that all pointers to objects that have an Id have only a limited validity period, with the exception of Repo pointers\&. There are only guaranteed to be valid until a new object of that type is added or an object of that type is removed\&. Thus pointers to Solvable objects are only valid until another solvable is created, because adding a Solvable may relocate the Pool\(cqs Solvable array\&. This is also true for Pool strings, you should use solv_strdup() to create a copy of the string if you want to use it at some later time\&. You should use the Ids in the code and not the pointers, except for short times where you know that the pointer is safe\&.
+.sp
+Note also that the data lookup functions or the dataiterator also return values with limited lifetime, this is especially true for data stored in the paged data segment of solv files\&. This is normally data that consists of big strings like package descriptions or is not often needed like package checksums\&. Thus looking up a description of a solvable and then looking up the description of a different solvable or even the checksum of the same solvable may invalidate the first result\&. (The dataiterator supports a dataiterator_strdup() function to create a safe copy\&.)
+.sp
+The language bindings already deal with pointer validity, so you do not have to worry about this issue when using the bindings\&.
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+Libsolv(3)
+==========
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+libsolv - package dependency solver library using a satisfiability algorithm
+
+
+Documentation
+-------------
+The libsolv documentation is split into multiple parts:
+
+*libsolv-history*::
+ how the libsolv library came into existence
+
+*libsolv-constantids*::
+ fixed Ids for often used strings
+
+*libsolv-bindings*::
+ access libsolv from perl/python/ruby
+
+*libsolv-pool*::
+ libsolv's pool object
+
+Pointer Validity
+----------------
+Note that all pointers to objects that have an Id have only a limited
+validity period, with the exception of Repo pointers. There are only
+guaranteed to be valid until a new object of that type is added or an
+object of that type is removed. Thus pointers to Solvable objects are only
+valid until another solvable is created, because adding a Solvable may
+relocate the Pool's Solvable array. This is also true for Pool strings,
+you should use solv_strdup() to create a copy of the string if you
+want to use it at some later time. You should use the Ids in the code
+and not the pointers, except for short times where you know that the
+pointer is safe.
+
+Note also that the data lookup functions or the dataiterator also
+return values with limited lifetime, this is especially true for data
+stored in the paged data segment of solv files. This is normally
+data that consists of big strings like package descriptions or is not
+often needed like package checksums. Thus looking up a description of
+a solvable and then looking up the description of a different solvable
+or even the checksum of the same solvable may invalidate the first
+result. (The dataiterator supports a dataiterator_strdup() function
+to create a safe copy.)
+
+The language bindings already deal with pointer validity, so you do
+not have to worry about this issue when using the bindings.
+
+
+Author
+------
+Michael Schroeder <mls@suse.de>
+
--- /dev/null
+'\" t
+.\" Title: mdk2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "MDK2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+mdk2solv \- convert files in Mandriva synthesis format into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBmdk2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The mdk2solv tool reads Mandriva synthesis data (\fBhdlist\fR) from stdin, and writes it as solv file to standard output\&.
+.PP
+\fB\-i\fR \fIINFO\&.xml\fR
+.RS 4
+Also read the info file containing url, license, and src information from the specified xml file\&.
+.RE
+.PP
+\fB\-f\fR \fIFILES\&.xml\fR
+.RS 4
+Also read filelist information from the specified xml file\&.
+.RE
+.SH "SEE ALSO"
+.sp
+genhdlist2(1)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+mdk2solv(1)
+===========
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+mdk2solv - convert files in Mandriva synthesis format into a solv file
+
+Synopsis
+--------
+*mdk2solv* ['OPTIONS']
+
+Description
+-----------
+The mdk2solv tool reads Mandriva synthesis data (*hdlist*) from stdin, and writes
+it as solv file to standard output.
+
+*-i* 'INFO.xml'::
+Also read the info file containing url, license, and src information from
+the specified xml file.
+
+*-f* 'FILES.xml'::
+Also read filelist information from the specified xml file.
+
+See Also
+--------
+genhdlist2(1)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: mergesolv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "MERGESOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+mergesolv \- merge multiple files in solv format into a single one
+.SH "SYNOPSIS"
+.sp
+\fBmergesolv\fR [\fIOPTIONS\fR] \fIFILE1\&.solv\fR \fIFILE2\&.solv\fR \&...
+.SH "DESCRIPTION"
+.sp
+The mergesolv tool reads all solv files specified on the command line, and writes a merged version to standard output\&.
+.PP
+\fB\-X\fR
+.RS 4
+Autoexpand SUSE pattern and product provides into packages\&.
+.RE
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+mergesolv(1)
+============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+mergesolv - merge multiple files in solv format into a single one
+
+Synopsis
+--------
+*mergesolv* ['OPTIONS'] 'FILE1.solv' 'FILE2.solv' ...
+
+Description
+-----------
+The mergesolv tool reads all solv files specified on the command line,
+and writes a merged version to standard output.
+
+*-X*::
+Autoexpand SUSE pattern and product provides into packages.
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: repomdxml2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "REPOMDXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+repomdxml2solv \- convert a repomd\&.xml file into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBrepomdxml2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The repomd\&.xml file is the index file of a rpm\-md repository, containing references to all data file with checksums\&. The repomdxml2solv tool reads the repomd\&.xml file from stdin and writes the parsed data as solv file to standard output\&. The data is stored as meta attributes in the result\&.
+.PP
+\fB\-q\fR \fIWHAT\fR
+.RS 4
+Data query mode: instead of writing a solv file, select the
+\fIWHAT\fR
+element in the input data and write it to standard output\&. Examples for
+\fIWHAT\fR
+are
+\fBtype\fR
+to get a list of all types, and
+\fBprimary:location\fR
+to get the location of the element with type
+\fBprimary\fR\&.
+.RE
+.SH "SEE ALSO"
+.sp
+rpmmd2solv(1), mergesolv(1), createrepo(8)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+repomdxml2solv(1)
+=================
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+repomdxml2solv - convert a repomd.xml file into a solv file
+
+Synopsis
+--------
+*repomdxml2solv* ['OPTIONS']
+
+Description
+-----------
+The repomd.xml file is the index file of a rpm-md repository,
+containing references to all data file with checksums. The
+repomdxml2solv tool reads the repomd.xml file from stdin and
+writes the parsed data as solv file to standard output. The
+data is stored as meta attributes in the result.
+
+*-q* 'WHAT'::
+Data query mode: instead of writing a solv file, select the
+'WHAT' element in the input data and write it to standard output.
+Examples for 'WHAT' are *type* to get a list of all types, and
+*primary:location* to get the location of the element with
+type *primary*.
+
+See Also
+--------
+rpmmd2solv(1), mergesolv(1), createrepo(8)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: rpmdb2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "RPMDB2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+rpmdb2solv \- convert the rpm database into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBrpmdb2solv\fR [\fIOPTIONS\fR] [\fIREFFILE\&.solv\fR]
+.SH "DESCRIPTION"
+.sp
+The rpmdb2solv tool reads rpm\(cqs installed packages database and writes it in solv file format to standard output\&. You can make use of an old version of the database by specifying a \fIREFFILE\&.solv\fR file\&.
+.PP
+\fB\-o\fR \fIOUTFILE\fR
+.RS 4
+Write the generated solv to
+\fIOUTFILE\fR
+instead of standard output\&.
+.RE
+.PP
+\fB\-P\fR
+.RS 4
+Print percentages as packages are being read in\&. The output format is like rpm\(cqs \-\-percent option\&.
+.RE
+.PP
+\fB\-r\fR \fIROOTDIR\fR
+.RS 4
+Use
+\fIROOTDIR\fR
+as root directory\&.
+.RE
+.PP
+\fB\-k\fR
+.RS 4
+Read pubkeys from the rpm database instead of installed packages\&. Note that many distributions stopped storing pubkeys in the database but use a directory like
+\fB/var/lib/rpm/pubkeys\fR
+instead\&.
+.RE
+.PP
+\fB\-A\fR
+.RS 4
+Also scan the
+\fB/usr/share/appdata\fR
+for installed appdata files and create pseudo packages for each file\&.
+.RE
+.PP
+\fB\-p\fR \fIPRODDIR\fR
+.RS 4
+Also read SUSE product files from directory
+\fIPRODDIR\fR\&. The standard directory is
+\fB/etc/products\&.d\fR\&.
+.RE
+.PP
+\fB\-n\fR
+.RS 4
+Do not read any packages from the rpm database\&. This is useful together with
+\fB\-p\fR
+to only convert SUSE products\&.
+.RE
+.PP
+\fB\-X\fR
+.RS 4
+Autoexpand SUSE pattern and product provides into packages\&.
+.RE
+.SH "SEE ALSO"
+.sp
+rpms2solv(1)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+rpmdb2solv(1)
+=============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+rpmdb2solv - convert the rpm database into a solv file
+
+Synopsis
+--------
+*rpmdb2solv* ['OPTIONS'] ['REFFILE.solv']
+
+Description
+-----------
+The rpmdb2solv tool reads rpm's installed packages database
+and writes it in solv file format to standard output. You can
+make use of an old version of the database by specifying a
+'REFFILE.solv' file.
+
+*-o* 'OUTFILE'::
+Write the generated solv to 'OUTFILE' instead of standard output.
+
+*-P*::
+Print percentages as packages are being read in. The output
+format is like rpm's --percent option.
+
+*-r* 'ROOTDIR'::
+Use 'ROOTDIR' as root directory.
+
+*-k*::
+Read pubkeys from the rpm database instead of installed packages.
+Note that many distributions stopped storing pubkeys in the
+database but use a directory like */var/lib/rpm/pubkeys*
+instead.
+
+*-A*::
+Also scan the */usr/share/appdata* for installed appdata files
+and create pseudo packages for each file.
+
+*-p* 'PRODDIR'::
+Also read SUSE product files from directory 'PRODDIR'. The
+standard directory is */etc/products.d*.
+
+*-n*::
+Do not read any packages from the rpm database. This is useful
+together with *-p* to only convert SUSE products.
+
+*-X*::
+Autoexpand SUSE pattern and product provides into packages.
+
+See Also
+--------
+rpms2solv(1)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: rpmmd2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "RPMMD2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+rpmmd2solv \- convert files in rpm\-md format into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBrpmmd2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The rpmmd2solv tool reads rpm\-md xml data from stdin, and writes it as solv file to standard output\&. It understands the \fBprimary\fR, \fBfilelist\fR, \fBother\fR, and \fBsusedata\fR format\&.
+.PP
+\fB\-X\fR
+.RS 4
+Autoexpand SUSE pattern and product provides into packages\&.
+.RE
+.SH "SEE ALSO"
+.sp
+repomdxml2solv(1), mergesolv(1), createrepo(8)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+rpmmd2solv(1)
+=============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+rpmmd2solv - convert files in rpm-md format into a solv file
+
+Synopsis
+--------
+*rpmmd2solv* ['OPTIONS']
+
+Description
+-----------
+The rpmmd2solv tool reads rpm-md xml data from stdin, and writes
+it as solv file to standard output. It understands the *primary*,
+*filelist*, *other*, and *susedata* format.
+
+*-X*::
+Autoexpand SUSE pattern and product provides into packages.
+
+See Also
+--------
+repomdxml2solv(1), mergesolv(1), createrepo(8)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: rpms2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "RPMS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+rpms2solv \- convert one or more rpms into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBrpms2solv\fR [\fIOPTIONS\fR] \fIRPM1\&.rpm\fR \&...
+.SH "DESCRIPTION"
+.sp
+The rpms2solv tool converts the header data from one or more rpms into the solv file written to standard output\&.
+.PP
+\fB\-m\fR \fIMANIFESTFILE\fR
+.RS 4
+Read the rpm file names from the specified
+\fIMANIFESTFILE\fR\&. You can use
+\fB\-\fR
+to read the manifest from standard input\&.
+.RE
+.PP
+\fB\-0\fR
+.RS 4
+Use a null byte as line terminator for manifest files instead of a newline\&. This is useful if the file names can contain newlines\&. See also the
+\fB\-print0\fR
+option in
+\fBfind\fR\&.
+.RE
+.PP
+\fB\-F\fR
+.RS 4
+Do not put all files from the headers into the file list, but instead use the filtering also found in
+\fBcreaterepo\fR\&.
+.RE
+.PP
+\fB\-k\fR
+.RS 4
+Read pubkeys instead of rpms\&.
+.RE
+.PP
+\fB\-K\fR
+.RS 4
+Read pubkey keyrings instead of rpms\&.
+.RE
+.PP
+\fB\-X\fR
+.RS 4
+Autoexpand SUSE pattern and product provides into packages\&.
+.RE
+.SH "SEE ALSO"
+.sp
+rpmdb2solv(1)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+rpms2solv(1)
+============
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+rpms2solv - convert one or more rpms into a solv file
+
+Synopsis
+--------
+*rpms2solv* ['OPTIONS'] 'RPM1.rpm' ...
+
+Description
+-----------
+The rpms2solv tool converts the header data from one or more
+rpms into the solv file written to standard output.
+
+*-m* 'MANIFESTFILE'::
+Read the rpm file names from the specified 'MANIFESTFILE'. You can
+use *-* to read the manifest from standard input.
+
+*-0*::
+Use a null byte as line terminator for manifest files instead of
+a newline. This is useful if the file names can contain newlines.
+See also the *-print0* option in *find*.
+
+*-F*::
+Do not put all files from the headers into the file list, but
+instead use the filtering also found in *createrepo*.
+
+*-k*::
+Read pubkeys instead of rpms.
+
+*-K*::
+Read pubkey keyrings instead of rpms.
+
+*-X*::
+Autoexpand SUSE pattern and product provides into packages.
+
+See Also
+--------
+rpmdb2solv(1)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: susetags2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "SUSETAGS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+susetags2solv \- convert the susetags repository format into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBsusetags2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The susetags format is most as repository format on most products created by SUSE\&. The susetags2solv reads data from standard input, converts the format into a solv file, and writes it to standard output\&.
+.PP
+\fB\-c\fR \fICONTENTFILE\fR
+.RS 4
+Also parse the specified content file containing meta information about the repository\&.
+.RE
+.PP
+\fB\-q\fR \fIWHAT\fR
+.RS 4
+Data query mode: instead of writing a solv file, select the
+\fIWHAT\fR
+element in the input data and write it to standard output\&. An example for
+\fIWHAT\fR
+is
+\fBdefaultvendor\fR
+to get a default vendor for the repository\&.
+.RE
+.PP
+\fB\-M\fR \fIMERGEFILE\&.solv\fR
+.RS 4
+Merge the content of the specified solv file into the output\&.
+.RE
+.PP
+\fB\-X\fR
+.RS 4
+Autoexpand SUSE pattern and product provides into packages\&.
+.RE
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+susetags2solv(1)
+================
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+susetags2solv - convert the susetags repository format into a solv file
+
+Synopsis
+--------
+*susetags2solv* ['OPTIONS']
+
+Description
+-----------
+The susetags format is most as repository format on most products
+created by SUSE. The susetags2solv reads data from standard input,
+converts the format into a solv file, and writes it to standard output.
+
+*-c* 'CONTENTFILE'::
+Also parse the specified content file containing meta information
+about the repository.
+
+*-q* 'WHAT'::
+Data query mode: instead of writing a solv file, select the
+'WHAT' element in the input data and write it to standard output.
+An example for 'WHAT' is *defaultvendor* to get a default vendor for
+the repository.
+
+*-M* 'MERGEFILE.solv'::
+Merge the content of the specified solv file into the output.
+
+*-X*::
+Autoexpand SUSE pattern and product provides into packages.
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: testsolv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "TESTSOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+testsolv \- run a libsolv testcase through the solver
+.SH "SYNOPSIS"
+.sp
+\fBtestsolv\fR [\fIOPTIONS\fR] \fITESTCASE\fR
+.SH "DESCRIPTION"
+.sp
+The testsolv tools can be used to run a testcase\&. Testcases can either be manually created to test specific features, or they can be written by libsolv\(cqs testcase_write function\&. This is useful to evaluate bug reports about the solver\&.
+.PP
+\fB\-v\fR
+.RS 4
+Increase the debug level of the solver\&. This option can be specified multiple times to further increase the amount of debug data\&.
+.RE
+.PP
+\fB\-r\fR
+.RS 4
+Write the output in testcase format instead of human readable text\&. The output can then be used in the result section of the test case\&. If the
+\fB\-r\fR
+option is given twice, the output is formated for verbatim inclusion\&.
+.RE
+.PP
+\fB\-l\fR \fIPKGSPEC\fR
+.RS 4
+Instead of running the solver, list packages in the repositories\&.
+.RE
+.PP
+\fB\-s\fR \fISOLUTIONSPEC\fR
+.RS 4
+This is used in the solver test suite to test the calculated solutions to encountered problems\&.
+.RE
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+testsolv(1)
+===========
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+testsolv - run a libsolv testcase through the solver
+
+Synopsis
+--------
+*testsolv* ['OPTIONS'] 'TESTCASE'
+
+Description
+-----------
+The testsolv tools can be used to run a testcase. Testcases can
+either be manually created to test specific features, or they
+can be written by libsolv's testcase_write function. This is useful
+to evaluate bug reports about the solver.
+
+*-v*::
+Increase the debug level of the solver. This option can be specified
+multiple times to further increase the amount of debug data.
+
+*-r*::
+Write the output in testcase format instead of human readable text.
+The output can then be used in the result section of the test case.
+If the *-r* option is given twice, the output is formated for
+verbatim inclusion.
+
+*-l* 'PKGSPEC'::
+Instead of running the solver, list packages in the repositories.
+
+*-s* 'SOLUTIONSPEC'::
+This is used in the solver test suite to test the calculated solutions
+to encountered problems.
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+'\" t
+.\" Title: updateinfoxml2solv
+.\" Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 08/26/2015
+.\" Manual: LIBSOLV
+.\" Source: libsolv
+.\" Language: English
+.\"
+.TH "UPDATEINFOXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+updateinfoxml2solv \- convert rpm\-md\*(Aqs updateinfo\&.xml format into a solv file
+.SH "SYNOPSIS"
+.sp
+\fBupdateinfoxml2solv\fR [\fIOPTIONS\fR]
+.SH "DESCRIPTION"
+.sp
+The updateinfoxml2solv tool reads rpm\-md\(cqs updateinfo xml data from stdin, and writes it as solv file to standard output\&. Update elements are converted into special \fBpatch:\fR pseudo packages\&.
+.SH "SEE ALSO"
+.sp
+mergesolv(1), createrepo(8)
+.SH "AUTHOR"
+.sp
+Michael Schroeder <mls@suse\&.de>
--- /dev/null
+updateinfoxml2solv(1)
+=====================
+:man manual: LIBSOLV
+:man source: libsolv
+
+
+Name
+----
+updateinfoxml2solv - convert rpm-md's updateinfo.xml format into a solv file
+
+Synopsis
+--------
+*updateinfoxml2solv* ['OPTIONS']
+
+Description
+-----------
+The updateinfoxml2solv tool reads rpm-md's updateinfo xml data from stdin,
+and writes it as solv file to standard output. Update elements are converted
+into special *patch:* pseudo packages.
+
+See Also
+--------
+mergesolv(1), createrepo(8)
+
+Author
+------
+Michael Schroeder <mls@suse.de>
--- /dev/null
+IF (SUSE OR FEDORA OR DEBIAN OR MANDRIVA OR MAGEIA)
+
+ADD_SUBDIRECTORY (solv)
+
+ENDIF (SUSE OR FEDORA OR DEBIAN OR MANDRIVA OR MAGEIA)
--- /dev/null
+#!/usr/bin/perl -w
+
+use POSIX;
+use Fcntl;
+use Config::IniFiles;
+use Data::Dumper;
+use solv;
+use Devel::Peek;
+use FileHandle;
+use File::Temp ();
+use strict;
+
+package Repo::generic;
+
+sub new {
+ my ($class, $alias, $type, $attr) = @_;
+ my $r = { %{$attr || {}} };
+ $r->{alias} = $alias;
+ $r->{type} = $type;
+ return bless $r, $class;
+}
+
+sub calc_cookie_fp {
+ my ($self, $fp) = @_;
+ my $chksum = solv::Chksum->new($solv::REPOKEY_TYPE_SHA256);
+ $chksum->add("1.1");
+ $chksum->add_fp($fp);
+ return $chksum->raw();
+}
+
+sub calc_cookie_file {
+ my ($self, $filename) = @_;
+ my $chksum = solv::Chksum->new($solv::REPOKEY_TYPE_SHA256);
+ $chksum->add("1.1");
+ $chksum->add_stat($filename);
+ return $chksum->raw();
+}
+
+sub calc_cookie_ext {
+ my ($self, $f, $cookie) = @_;
+ my $chksum = solv::Chksum->new($solv::REPOKEY_TYPE_SHA256);
+ $chksum->add("1.1");
+ $chksum->add($cookie);
+ $chksum->add_fstat(fileno($f));
+ return $chksum->raw();
+}
+
+sub cachepath {
+ my ($self, $ext) = @_;
+ my $path = $self->{alias};
+ $path =~ s/^\./_/s;
+ $path .= $ext ? "_$ext.solvx" : '.solv';
+ $path =~ s!/!_!gs;
+ return "/var/cache/solv/$path";
+}
+
+sub load {
+ my ($self, $pool) = @_;
+ $self->{handle} = $pool->add_repo($self->{alias});
+ $self->{handle}->{appdata} = $self;
+ $self->{handle}->{priority} = 99 - $self->{priority};
+ my $dorefresh = $self->{autorefresh};
+ if ($dorefresh) {
+ my @s = stat($self->cachepath());
+ $dorefresh = 0 if @s && ($self->{metadata_expire} == -1 || time() - $s[9] < $self->{metadata_expire});
+ }
+ $self->{cookie} = '';
+ $self->{extcookie} = '';
+ if (!$dorefresh && $self->usecachedrepo()) {
+ print "repo: '$self->{alias}' cached\n";
+ return 1;
+ }
+ return 0;
+}
+
+sub load_ext {
+ return 0;
+}
+
+sub download {
+ my ($self, $file, $uncompress, $chksum, $markincomplete) = @_;
+ if (!$self->{baseurl}) {
+ print "$self->{alias}: no baseurl\n";
+ return undef;
+ }
+ my $url = $self->{baseurl};
+ $url =~ s!/$!!;
+ $url .= "/$file";
+ open(my $f, '+>', undef) || die;
+ fcntl($f, Fcntl::F_SETFD, 0); # turn off CLOEXEC
+ my $st = system('curl', '-f', '-s', '-L', '-o', "/dev/fd/" . fileno($f), '--', $url);
+ if (POSIX::lseek(fileno($f), 0, POSIX::SEEK_END) == 0 && ($st == 0 || !$chksum)) {
+ return undef;
+ }
+ POSIX::lseek(fileno($f), 0, POSIX::SEEK_SET);
+ if ($st) {
+ print "$file: download error #$st\n";
+ $self->{incomplete} = 1 if $markincomplete;
+ return undef;
+ }
+ if ($chksum) {
+ my $fchksum = solv::Chksum->new($chksum->{type});
+ $fchksum->add_fd(fileno($f));
+ if ($fchksum != $chksum) {
+ print "$file: checksum error\n";
+ $self->{incomplete} = 1 if $markincomplete;
+ return undef;
+ }
+ }
+ if ($uncompress) {
+ return solv::xfopen_fd($file, fileno($f));
+ } else {
+ return solv::xfopen_fd(undef, fileno($f));
+ }
+}
+
+sub usecachedrepo {
+ my ($self, $ext, $mark) = @_;
+ my $cookie = $ext ? $self->{extcookie} : $self->{cookie};
+ my $cachepath = $self->cachepath($ext);
+ my $fextcookie;
+ if (sysopen(my $f, $cachepath, POSIX::O_RDONLY)) {
+ sysseek($f, -32, Fcntl::SEEK_END);
+ my $fcookie = '';
+ return undef if sysread($f, $fcookie, 32) != 32;
+ return undef if $cookie && $fcookie ne $cookie;
+ if ($self->{type} ne 'system' && !$ext) {
+ sysseek($f, -32 * 2, Fcntl::SEEK_END);
+ return undef if sysread($f, $fextcookie, 32) != 32;
+ }
+ sysseek($f, 0, Fcntl::SEEK_SET);
+ my $fd = solv::xfopen_fd(undef, fileno($f));
+ my $flags = $ext ? $solv::Repo::REPO_USE_LOADING|$solv::Repo::REPO_EXTEND_SOLVABLES : 0;
+ $flags |= $solv::Repo::REPO_LOCALPOOL if $ext && $ext ne 'DL';
+ if (!$self->{handle}->add_solv($fd, $flags)) {
+ return undef;
+ }
+ $self->{cookie} = $fcookie unless $ext;
+ $self->{extcookie} = $fextcookie if $fextcookie;
+ utime undef, undef, $f if $mark;
+ return 1;
+ }
+ return undef;
+}
+
+sub writecachedrepo {
+ my ($self, $ext, $repodata) = @_;
+ return if $self->{incomplete};
+ mkdir("/var/cache/solv", 0755) unless -d "/var/cache/solv";
+ my ($f, $tmpname);
+ eval {
+ ($f, $tmpname) = File::Temp::tempfile(".newsolv-XXXXXX", 'DIR' => '/var/cache/solv');
+ };
+ return unless $f;
+ chmod 0444, $f;
+ my $ff = solv::xfopen_fd(undef, fileno($f));
+ if (!$repodata) {
+ $self->{handle}->write($ff);
+ } elsif ($ext) {
+ $repodata->write($ff);
+ } else {
+ $self->{handle}->write_first_repodata($ff);
+ }
+ undef $ff; # also flushes
+ if ($self->{type} ne 'system' && !$ext) {
+ $self->{extcookie} ||= $self->calc_cookie_ext($f, $self->{cookie});
+ syswrite($f, $self->{extcookie});
+ }
+ syswrite($f, $ext ? $self->{extcookie} : $self->{cookie});
+ close($f);
+ if ($self->{handle}->iscontiguous()) {
+ $f = solv::xfopen($tmpname);
+ if ($f) {
+ if (!$ext) {
+ $self->{handle}->empty();
+ die("internal error, cannot reload solv file\n") unless $self->{handle}->add_solv($f, $repodata ? 0 : $solv::Repo::SOLV_ADD_NO_STUBS);
+ } else {
+ $repodata->extend_to_repo();
+ my $flags = $solv::Repo::REPO_EXTEND_SOLVABLES;
+ $flags |= $solv::Repo::REPO_LOCALPOOL if $ext ne 'DL';
+ $repodata->add_solv($f, $flags);
+ }
+ }
+ }
+ rename($tmpname, $self->cachepath($ext));
+}
+
+sub packagespath {
+ my ($self) = @_;
+ return '';
+}
+
+my %langtags = (
+ $solv::SOLVABLE_SUMMARY => $solv::REPOKEY_TYPE_STR,
+ $solv::SOLVABLE_DESCRIPTION => $solv::REPOKEY_TYPE_STR,
+ $solv::SOLVABLE_EULA => $solv::REPOKEY_TYPE_STR,
+ $solv::SOLVABLE_MESSAGEINS => $solv::REPOKEY_TYPE_STR,
+ $solv::SOLVABLE_MESSAGEDEL => $solv::REPOKEY_TYPE_STR,
+ $solv::SOLVABLE_CATEGORY => $solv::REPOKEY_TYPE_ID,
+);
+
+sub add_ext_keys {
+ my ($self, $ext, $repodata, $handle) = @_;
+ if ($ext eq 'DL') {
+ $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOSITORY_DELTAINFO);
+ $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_FLEXARRAY);
+ } elsif ($ext eq 'DU') {
+ $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_DISKUSAGE);
+ $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRNUMNUMARRAY);
+ } elsif ($ext eq 'FL') {
+ $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_FILELIST);
+ $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRSTRARRAY);
+ } else {
+ for my $langid (sort { $a <=> $b } keys %langtags) {
+ $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $self->{handle}->{pool}->id2langid($langid, $ext, 1));
+ $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $langtags{$langid});
+ }
+ }
+}
+
+package Repo::rpmmd;
+
+our @ISA = ('Repo::generic');
+
+sub find {
+ my ($self, $what) = @_;
+ my $di = $self->{handle}->Dataiterator_meta($solv::REPOSITORY_REPOMD_TYPE, $what, $solv::Dataiterator::SEARCH_STRING);
+ $di->prepend_keyname($solv::REPOSITORY_REPOMD);
+ for my $d (@$di) {
+ my $dp = $d->parentpos();
+ my $filename = $dp->lookup_str($solv::REPOSITORY_REPOMD_LOCATION);
+ next unless $filename;
+ my $chksum = $dp->lookup_checksum($solv::REPOSITORY_REPOMD_CHECKSUM);
+ if (!$chksum) {
+ print "no $filename file checksum!\n";
+ return (undef, undef);
+ }
+ return ($filename, $chksum);
+ }
+ return (undef, undef);
+}
+
+sub add_ext {
+ my ($self, $repodata, $what, $ext) = @_;
+ my ($filename, $chksum) = $self->find($what);
+ ($filename, $chksum) = $self->find('prestodelta') if !$filename && $what eq 'deltainfo';
+ return unless $filename;
+ my $handle = $repodata->new_handle();
+ $repodata->set_poolstr($handle, $solv::REPOSITORY_REPOMD_TYPE, $what);
+ $repodata->set_str($handle, $solv::REPOSITORY_REPOMD_LOCATION, $filename);
+ $repodata->set_checksum($handle, $solv::REPOSITORY_REPOMD_CHECKSUM, $chksum);
+ $self->add_ext_keys($ext, $repodata, $handle);
+ $repodata->add_flexarray($solv::SOLVID_META, $solv::REPOSITORY_EXTERNAL, $handle);
+}
+
+sub add_exts {
+ my ($self) = @_;
+ my $repodata = $self->{handle}->add_repodata(0);
+ $self->add_ext($repodata, 'deltainfo', 'DL');
+ $self->add_ext($repodata, 'filelists', 'FL');
+ $repodata->internalize();
+}
+
+sub load_ext {
+ my ($self, $repodata) = @_;
+ my $repomdtype = $repodata->lookup_str($solv::SOLVID_META, $solv::REPOSITORY_REPOMD_TYPE);
+ my $ext;
+ if ($repomdtype eq 'filelists') {
+ $ext = 'FL';
+ } elsif ($repomdtype eq 'deltainfo') {
+ $ext = 'DL';
+ } else {
+ return 0;
+ }
+ print("[$self->{alias}:$ext: ");
+ STDOUT->flush();
+ if ($self->usecachedrepo($ext)) {
+ print "cached]\n";
+ return 1;
+ }
+ print "fetching]\n";
+ my $filename = $repodata->lookup_str($solv::SOLVID_META, $solv::REPOSITORY_REPOMD_LOCATION);
+ my $filechksum = $repodata->lookup_checksum($solv::SOLVID_META, $solv::REPOSITORY_REPOMD_CHECKSUM);
+ my $f = $self->download($filename, 1, $filechksum);
+ return 0 unless $f;
+ if ($ext eq 'FL') {
+ $self->{handle}->add_rpmmd($f, 'FL', $solv::Repo::REPO_USE_LOADING|$solv::Repo::REPO_EXTEND_SOLVABLES|$solv::Repo::REPO_LOCALPOOL);
+ } elsif ($ext eq 'DL') {
+ $self->{handle}->add_deltainfoxml($f, $solv::Repo::REPO_USE_LOADING);
+ }
+ $self->writecachedrepo($ext, $repodata);
+ return 1;
+}
+
+sub load {
+ my ($self, $pool) = @_;
+ return 1 if $self->Repo::generic::load($pool);
+ print "rpmmd repo '$self->{alias}': ";
+ STDOUT->flush();
+ my $f = $self->download("repodata/repomd.xml");
+ if (!$f) {
+ print "no repomd.xml file, skipped\n";
+ $self->{handle}->free(1);
+ delete $self->{handle};
+ return undef;
+ }
+ $self->{cookie} = $self->calc_cookie_fp($f);
+ if ($self->usecachedrepo(undef, 1)) {
+ print "cached\n";
+ return 1;
+ }
+ $self->{handle}->add_repomdxml($f, 0);
+ print "fetching\n";
+ my ($filename, $filechksum) = $self->find('primary');
+ if ($filename) {
+ $f = $self->download($filename, 1, $filechksum, 1);
+ if ($f) {
+ $self->{handle}->add_rpmmd($f, undef, 0);
+ }
+ return undef if $self->{incomplete};
+ }
+ ($filename, $filechksum) = $self->find('updateinfo');
+ if ($filename) {
+ $f = $self->download($filename, 1, $filechksum, 1);
+ if ($f) {
+ $self->{handle}->add_updateinfoxml($f, 0);
+ }
+ }
+ $self->add_exts();
+ $self->writecachedrepo();
+ $self->{handle}->create_stubs();
+ return 1;
+}
+
+package Repo::susetags;
+
+our @ISA = ('Repo::generic');
+
+sub find {
+ my ($self, $what) = @_;
+
+ my $di = $self->{handle}->Dataiterator_meta($solv::SUSETAGS_FILE_NAME, $what, $solv::Dataiterator::SEARCH_STRING);
+ $di->prepend_keyname($solv::SUSETAGS_FILE);
+ for my $d (@$di) {
+ my $dp = $d->parentpos();
+ my $chksum = $dp->lookup_checksum($solv::SUSETAGS_FILE_CHECKSUM);
+ return ($what, $chksum);
+ }
+ return (undef, undef);
+}
+
+
+sub add_ext {
+ my ($self, $repodata, $what, $ext) = @_;
+ my ($filename, $chksum) = $self->find($what);
+ return unless $filename;
+ my $handle = $repodata->new_handle();
+ $repodata->set_str($handle, $solv::SUSETAGS_FILE_NAME, $filename);
+ $repodata->set_checksum($handle, $solv::SUSETAGS_FILE_CHECKSUM, $chksum);
+ $self->add_ext_keys($ext, $repodata, $handle);
+ $repodata->add_flexarray($solv::SOLVID_META, $solv::REPOSITORY_EXTERNAL, $handle);
+}
+
+sub add_exts {
+ my ($self) = @_;
+ my $repodata = $self->{handle}->add_repodata(0);
+ my $di = $self->{handle}->Dataiterator_meta($solv::SUSETAGS_FILE_NAME, undef, 0);
+ $di->prepend_keyname($solv::SUSETAGS_FILE);
+ for my $d (@$di) {
+ my $filename = $d->str();
+ next unless $filename && $filename =~ /^packages\.(..)(?:\..*)$/;
+ next if $1 eq 'en' || $1 eq 'gz';
+ $self->add_ext($repodata, $filename, $1);
+ }
+ $repodata->internalize();
+}
+
+sub load_ext {
+ my ($self, $repodata) = @_;
+ my $filename = $repodata->lookup_str($solv::SOLVID_META, $solv::SUSETAGS_FILE_NAME);
+ my $ext = substr($filename, 9, 2);
+ print("[$self->{alias}:$ext: ");
+ STDOUT->flush();
+ if ($self->usecachedrepo($ext)) {
+ print "cached]\n";
+ return 1;
+ }
+ print "fetching]\n";
+ my $defvendorid = $self->{handle}->{meta}->lookup_id($solv::SUSETAGS_DEFAULTVENDOR);
+ my $descrdir = $self->{handle}->{meta}->lookup_str($solv::SUSETAGS_DESCRDIR) || 'suse/setup/descr';
+ my $filechksum = $repodata->lookup_checksum($solv::SOLVID_META, $solv::SUSETAGS_FILE_CHECKSUM);
+ my $f = $self->download("$descrdir/$filename", 1, $filechksum);
+ return 0 unless $f;
+ my $flags = $solv::Repo::REPO_USE_LOADING|$solv::Repo::REPO_EXTEND_SOLVABLES;
+ $flags |= $solv::Repo::REPO_LOCALPOOL if $ext ne 'DL';
+ $self->{handle}->add_susetags($f, $defvendorid, $ext, $flags);
+ $self->writecachedrepo($ext, $repodata);
+ return 1;
+}
+
+sub load {
+ my ($self, $pool) = @_;
+ return 1 if $self->Repo::generic::load($pool);
+ print "susetags repo '$self->{alias}': ";
+ STDOUT->flush();
+ my $f = $self->download("content");
+ if (!$f) {
+ print "no content file, skipped\n";
+ $self->{handle}->free(1);
+ delete $self->{handle};
+ return undef;
+ }
+ $self->{cookie} = $self->calc_cookie_fp($f);
+ if ($self->usecachedrepo(undef, 1)) {
+ print "cached\n";
+ return 1;
+ }
+ $self->{handle}->add_content($f, 0);
+ print "fetching\n";
+ my $defvendorid = $self->{handle}->{meta}->lookup_id($solv::SUSETAGS_DEFAULTVENDOR);
+ my $descrdir = $self->{handle}->{meta}->lookup_str($solv::SUSETAGS_DESCRDIR) || 'suse/setup/descr';
+ my ($filename, $filechksum) = $self->find('packages.gz');
+ ($filename, $filechksum) = $self->find('packages') unless $filename;
+ if ($filename) {
+ $f = $self->download("$descrdir/$filename", 1, $filechksum, 1);
+ if ($f) {
+ $self->{handle}->add_susetags($f, $defvendorid, undef, $solv::Repo::REPO_NO_INTERNALIZE|$solv::Repo::SUSETAGS_RECORD_SHARES);
+ ($filename, $filechksum) = $self->find('packages.en.gz');
+ ($filename, $filechksum) = $self->find('packages.en') unless $filename;
+ if ($filename) {
+ $f = $self->download("$descrdir/$filename", 1, $filechksum, 1);
+ if ($f) {
+ $self->{handle}->add_susetags($f, $defvendorid, undef, $solv::Repo::REPO_NO_INTERNALIZE|$solv::Repo::REPO_REUSE_REPODATA|$solv::Repo::REPO_EXTEND_SOLVABLES);
+ }
+ }
+ $self->{handle}->internalize();
+ }
+ }
+ $self->add_exts();
+ $self->writecachedrepo();
+ $self->{handle}->create_stubs();
+ return undef;
+}
+
+sub packagespath {
+ my ($self) = @_;
+ return ($self->{handle}->{meta}->lookup_str($solv::SUSETAGS_DATADIR) || 'suse') . '/';
+}
+
+package Repo::unknown;
+
+our @ISA = ('Repo::generic');
+
+sub load {
+ my ($self) = @_;
+ print "unsupported repo '$self->{alias}': skipped\n";
+ return 0;
+}
+
+package Repo::system;
+
+our @ISA = ('Repo::generic');
+
+sub load {
+ my ($self, $pool) = @_;
+
+ $self->{handle} = $pool->add_repo($self->{alias});
+ $self->{handle}->{appdata} = $self;
+ $pool->{installed} = $self->{handle};
+ print "rpm database: ";
+ $self->{cookie} = $self->calc_cookie_file('/var/lib/rpm/Packages');
+ if ($self->usecachedrepo()) {
+ print "cached\n";
+ return 1;
+ }
+ print "reading\n";
+ if (defined(&solv::Repo::add_products)) {
+ $self->{handle}->add_products("/etc/products.d", $solv::Repo::REPO_NO_INTERNALIZE);
+ }
+ my $f = solv::xfopen($self->cachepath());
+ $self->{handle}->add_rpmdb_reffp($f, $solv::Repo::REPO_REUSE_REPODATA);
+ $self->writecachedrepo();
+ return 1;
+}
+
+package main;
+
+sub load_stub {
+ my ($repodata) = @_;
+ my $repo = $repodata->{repo}->{appdata};
+ return $repo ? $repo->load_ext($repodata) : 0;
+}
+
+die("Usage: p5solv COMMAND [ARGS]\n") unless @ARGV;
+my $cmd = shift @ARGV;
+my %cmdabbrev = ( 'ls' => 'list', 'in' => 'install', 'rm' => 'erase',
+ 've' => 'verify', 'se' => 'search' );
+$cmd = $cmdabbrev{$cmd} if $cmdabbrev{$cmd};
+
+my %cmdactionmap = (
+ 'install' => $solv::Job::SOLVER_INSTALL,
+ 'erase' => $solv::Job::SOLVER_ERASE,
+ 'up' => $solv::Job::SOLVER_UPDATE,
+ 'dup' => $solv::Job::SOLVER_DISTUPGRADE,
+ 'verify' => $solv::Job::SOLVER_VERIFY,
+ 'list' => 0,
+ 'info' => 0,
+);
+
+my @repos;
+my @reposdirs;
+if (-d '/etc/zypp/repos.d') {
+ @reposdirs = ( '/etc/zypp/repos.d' );
+} else {
+ @reposdirs = ( '/etc/yum/repos.d' );
+}
+for my $reposdir (@reposdirs) {
+ next unless -d $reposdir;
+ my $dir;
+ next unless opendir($dir, $reposdir);
+ for my $reponame (sort(grep {/\.repo$/} readdir($dir))) {
+ my $cfg = new Config::IniFiles('-file' => "$reposdir/$reponame");
+ for my $alias ($cfg->Sections()) {
+ my $repoattr = {'alias' => $alias, 'enabled' => 0, 'priority' => 99, 'autorefresh' => 1, 'type' => 'rpm-md', 'metadata_expire' => 900};
+ for my $p ($cfg->Parameters($alias)) {
+ $repoattr->{$p} = $cfg->val($alias, $p);
+ }
+ my $repo;
+ if ($repoattr->{type} eq 'rpm-md') {
+ $repo = Repo::rpmmd->new($alias, 'repomd', $repoattr);
+ } elsif ($repoattr->{type} eq 'yast2') {
+ $repo = Repo::susetags->new($alias, 'susetags', $repoattr);
+ } else {
+ $repo = Repo::unknown->new($alias, 'unknown', $repoattr);
+ }
+ push @repos, $repo;
+ }
+ }
+}
+
+my $pool = solv::Pool->new();
+$pool->setarch();
+$pool->set_loadcallback(\&load_stub);
+
+my $sysrepo = Repo::system->new('@System', 'system');
+$sysrepo->load($pool);
+for my $repo (@repos) {
+ $repo->load($pool) if $repo->{enabled};
+}
+
+if ($cmd eq 'search') {
+ $pool->createwhatprovides();
+ my $sel = $pool->Selection();
+ my $di = $pool->Dataiterator($solv::SOLVABLE_NAME, $ARGV[0], $solv::Dataiterator::SEARCH_SUBSTRING | $solv::Dataiterator::SEARCH_NOCASE);
+ for my $d (@$di) {
+ $sel->add_raw($solv::Job::SOLVER_SOLVABLE, $d->{solvid});
+ }
+ for my $s ($sel->solvables()) {
+ print "- ".$s->str()." [$s->{repo}->{name}]: ".$s->lookup_str($solv::SOLVABLE_SUMMARY)."\n";
+ }
+ exit(0);
+}
+
+die("unknown command '$cmd'\n") unless defined $cmdactionmap{$cmd};
+
+$pool->addfileprovides();
+$pool->createwhatprovides();
+
+my @jobs;
+for my $arg (@ARGV) {
+ my $flags = $solv::Selection::SELECTION_NAME | $solv::Selection::SELECTION_PROVIDES | $solv::Selection::SELECTION_GLOB;
+ $flags |= $solv::Selection::SELECTION_CANON | $solv::Selection::SELECTION_DOTARCH | $solv::Selection::SELECTION_REL;
+ if ($arg =~ m!^/!) {
+ $flags |= $solv::Selection::SELECTION_FILELIST;
+ $flags |= $solv::Selection::SELECTION_INSTALLED_ONLY if $cmd eq 'erase';
+ }
+ my $sel = $pool->select($arg, $flags);
+ if ($sel->isempty()) {
+ $sel = $pool->select($arg, $flags | $solv::Selection::SELECTION_NOCASE);
+ print "[ignoring case for '$arg']\n" unless $sel->isempty();
+ }
+ die("nothing matches '$arg'\n") if $sel->isempty();
+ print "[using file list match for '$arg']\n" if $sel->flags() & $solv::Selection::SELECTION_FILELIST;
+ print "[using capability match for '$arg']\n" if $sel->flags() & $solv::Selection::SELECTION_PROVIDES;
+ push @jobs, $sel->jobs($cmdactionmap{$cmd});
+}
+
+if (!@jobs && ($cmd eq 'up' || $cmd eq 'dup' || $cmd eq 'verify')) {
+ my $sel = $pool->Selection_all();
+ push @jobs, $sel->jobs($cmdactionmap{$cmd});
+}
+
+die("no package matched.\n") unless @jobs;
+
+if ($cmd eq 'list' || $cmd eq 'info') {
+ for my $job (@jobs) {
+ for my $s ($job->solvables()) {
+ if ($cmd eq 'info') {
+ printf "Name: %s\n", $s->str();
+ printf "Repo: %s\n", $s->{repo}->{name};
+ printf "Summary: %s\n", $s->lookup_str($solv::SOLVABLE_SUMMARY);
+ my $str = $s->lookup_str($solv::SOLVABLE_URL);
+ printf "Url: %s\n", $str if $str;
+ $str = $s->lookup_str($solv::SOLVABLE_LICENSE);
+ printf "License: %s\n", $str if $str;
+ printf "Description:\n%s\n", $s->lookup_str($solv::SOLVABLE_DESCRIPTION);
+ } else {
+ printf " - %s [%s]\n", $s->str(), $s->{repo}->{name};
+ printf " %s\n", $s->lookup_str($solv::SOLVABLE_SUMMARY);
+ }
+ }
+ }
+ exit 0;
+}
+
+# up magic, turn into install if nothing matches
+for my $job (@jobs) {
+ $job->{how} ^= $solv::Job::SOLVER_UPDATE ^ $solv::Job::SOLVER_INSTALL if $cmd eq 'up' && $job->isemptyupdate();
+}
+
+my $solver = $pool->Solver();
+$solver->set_flag($solv::Solver::SOLVER_FLAG_SPLITPROVIDES, 1);
+$solver->set_flag($solv::Solver::SOLVER_FLAG_ALLOW_UNINSTALL, 1) if $cmd eq 'erase';
+
+while (1) {
+ my @problems = $solver->solve(\@jobs);
+ last unless @problems;
+ for my $problem (@problems) {
+ print "Problem $problem->{id}/".@problems.":\n";
+ print $problem->str()."\n";
+ my @solutions = $problem->solutions();
+ for my $solution (@solutions) {
+ print " Solution $solution->{id}:\n";
+ for my $element ($solution->elements(1)) {
+ print " - ".$element->str()."\n";
+ }
+ print "\n";
+ }
+ my $sol;
+ while (1) {
+ print "Please choose a solution: ";
+ $sol = <STDIN>;
+ chomp $sol;
+ last if $sol eq 's' || $sol eq 'q' || ($sol =~ /^\d+$/ && $sol >= 1 && $sol <= @solutions);
+ }
+ next if $sol eq 's';
+ exit(1) if $sol eq 'q';
+ my $solution = $solutions[$sol - 1];
+ for my $element ($solution->elements()) {
+ my $newjob = $element->Job();
+ if ($element->{type} == $solv::Solver::SOLVER_SOLUTION_JOB) {
+ $jobs[$element->{jobidx}] = $newjob;
+ } else {
+ push @jobs, $newjob if $newjob && !grep {$_ == $newjob} @jobs;
+ }
+ }
+ }
+}
+
+my $trans = $solver->transaction();
+undef $solver;
+if ($trans->isempty()) {
+ print "Nothing to do.\n";
+ exit 0;
+}
+
+print "\nTransaction summary:\n\n";
+for my $c ($trans->classify($solv::Transaction::SOLVER_TRANSACTION_SHOW_OBSOLETES|$solv::Transaction::SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE)) {
+ if ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_ERASE) {
+ print "$c->{count} erased packages:\n";
+ } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_INSTALL) {
+ print "$c->{count} installed packages:\n";
+ } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_REINSTALLED) {
+ print "$c->{count} reinstalled packages:\n";
+ } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED) {
+ print "$c->{count} downgraded packages:\n";
+ } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_CHANGED) {
+ print "$c->{count} changed packages:\n";
+ } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_UPGRADED) {
+ print "$c->{count} upgraded packages:\n";
+ } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_VENDORCHANGE) {
+ printf "$c->{count} vendor changes from '%s' to '%s':\n", $c->{fromstr}, $c->{tostr};
+ } elsif ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_ARCHCHANGE) {
+ printf "$c->{count} arch changes from '%s' to '%s':\n", $c->{fromstr}, $c->{tostr};
+ } else {
+ next;
+ }
+ for my $p ($c->solvables()) {
+ if ($c->{type} == $solv::Transaction::SOLVER_TRANSACTION_UPGRADED || $c->{type} == $solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED) {
+ my $other = $trans->othersolvable($p);
+ printf " - %s -> %s\n", $p->str(), $other->str();
+ } else {
+ printf " - %s\n", $p->str();
+ }
+ }
+ print "\n";
+}
+printf "install size change: %d K\n\n", $trans->calc_installsizechange();
+
+while (1) {
+ print("OK to continue (y/n)? ");
+ my $yn = <STDIN>;
+ chomp $yn;
+ last if $yn eq 'y';
+ exit(1) if $yn eq 'n' || $yn eq 'q';
+}
+
+my @newpkgs = $trans->newsolvables();
+my %newpkgsfps;
+if (@newpkgs) {
+ my $downloadsize = 0;
+ $downloadsize += $_->lookup_num($solv::SOLVABLE_DOWNLOADSIZE) for @newpkgs;
+ printf "Downloading %d packages, %d K\n", scalar(@newpkgs), $downloadsize / 1024;
+ for my $p (@newpkgs) {
+ my $repo = $p->{repo}->{appdata};
+ my ($location) = $p->lookup_location();
+ next unless $location;
+ $location = $repo->packagespath() . $location;
+ my $chksum = $p->lookup_checksum($solv::SOLVABLE_CHECKSUM);
+ my $f = $repo->download($location, 0, $chksum);
+ die("\n$repo->{alias}: $location not found in repository\n") unless $f;
+ $newpkgsfps{$p->{id}} = $f;
+ print ".";
+ STDOUT->flush();
+ }
+ print "\n";
+}
+
+print "Committing transaction:\n\n";
+$trans->order();
+for my $p ($trans->steps()) {
+ my $steptype = $trans->steptype($p, $solv::Transaction::SOLVER_TRANSACTION_RPM_ONLY);
+ if ($steptype == $solv::Transaction::SOLVER_TRANSACTION_ERASE) {
+ print "erase ".$p->str()."\n";
+ next unless $p->lookup_num($solv::RPM_RPMDBID);
+ my $evr = $p->{evr};
+ $evr =~ s/^[0-9]+://; # strip epoch
+ system('rpm', '-e', '--nodeps', '--nodigest', '--nosignature', "$p->{name}-$evr.$p->{arch}") && die("rpm failed: $?\n");
+ } elsif ($steptype == $solv::Transaction::SOLVER_TRANSACTION_INSTALL || $steptype == $solv::Transaction::SOLVER_TRANSACTION_MULTIINSTALL) {
+ print "install ".$p->str()."\n";
+ my $f = $newpkgsfps{$p->{id}};
+ my $mode = $steptype == $solv::Transaction::SOLVER_TRANSACTION_INSTALL ? '-U' : '-i';
+ $f->cloexec(0);
+ system('rpm', $mode, '--force', '--nodeps', '--nodigest', '--nosignature', "/dev/fd/".$f->fileno()) && die("rpm failed: $?\n");
+ delete $newpkgsfps{$p->{id}};
+ }
+}
+
+exit 0;
--- /dev/null
+#!/usr/bin/python
+
+#
+# Copyright (c) 2011, Novell Inc.
+#
+# This program is licensed under the BSD license, read LICENSE.BSD
+# for further information
+#
+
+# pysolv a little software installer demoing the sat solver library/bindings
+
+# things it does:
+# - understands globs for package names / dependencies
+# - understands .arch suffix
+# - repository data caching
+# - on demand loading of secondary repository data
+# - checksum verification
+# - deltarpm support
+# - installation of commandline packages
+#
+# things not yet ported:
+# - gpg verification
+# - file conflicts
+# - fastestmirror implementation
+#
+# things available in the library but missing from pysolv:
+# - vendor policy loading
+# - soft locks file handling
+# - multi version handling
+
+import sys
+import os
+import glob
+import solv
+import re
+import tempfile
+import time
+import subprocess
+import rpm
+from stat import *
+from iniparse import INIConfig
+from optparse import OptionParser
+
+#import gc
+#gc.set_debug(gc.DEBUG_LEAK)
+
+class repo_generic(dict):
+ def __init__(self, name, type, attribs = {}):
+ for k in attribs:
+ self[k] = attribs[k]
+ self.name = name
+ self.type = type
+
+ def calc_cookie_file(self, filename):
+ chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
+ chksum.add("1.1")
+ chksum.add_stat(filename)
+ return chksum.raw()
+
+ def calc_cookie_fp(self, fp):
+ chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
+ chksum.add("1.1");
+ chksum.add_fp(fp)
+ return chksum.raw()
+
+ def calc_cookie_ext(self, f, cookie):
+ chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
+ chksum.add("1.1");
+ chksum.add(cookie)
+ chksum.add_fstat(f.fileno())
+ return chksum.raw()
+
+ def cachepath(self, ext = None):
+ path = re.sub(r'^\.', '_', self.name)
+ if ext:
+ path += "_" + ext + ".solvx"
+ else:
+ path += ".solv"
+ return "/var/cache/solv/" + re.sub(r'[/]', '_', path)
+
+ def load(self, pool):
+ self.handle = pool.add_repo(self.name)
+ self.handle.appdata = self
+ self.handle.priority = 99 - self['priority']
+ dorefresh = bool(int(self['autorefresh']))
+ if dorefresh:
+ try:
+ st = os.stat(self.cachepath())
+ if self['metadata_expire'] == -1 or time.time() - st[ST_MTIME] < self['metadata_expire']:
+ dorefresh = False
+ except OSError:
+ pass
+ self['cookie'] = ''
+ self['extcookie'] = ''
+ if not dorefresh and self.usecachedrepo(None):
+ print("repo: '%s': cached" % self.name)
+ return True
+ return False
+
+ def load_ext(self, repodata):
+ return False
+
+ def setfromurls(self, urls):
+ if not urls:
+ return
+ url = urls[0]
+ print("[using mirror %s]" % re.sub(r'^(.*?/...*?)/.*$', r'\1', url))
+ self['baseurl'] = url
+
+ def setfrommetalink(self, metalink):
+ f = self.download(metalink, False, None)
+ if not f:
+ return None
+ f = os.fdopen(f.dup(), 'r')
+ urls = []
+ chksum = None
+ for l in f.readlines():
+ l = l.strip()
+ m = re.match(r'^<hash type="sha256">([0-9a-fA-F]{64})</hash>', l)
+ if m:
+ chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256, m.group(1))
+ m = re.match(r'^<url.*>(https?://.+)repodata/repomd.xml</url>', l)
+ if m:
+ urls.append(m.group(1))
+ if not urls:
+ chksum = None # in case the metalink is about a different file
+ f.close()
+ self.setfromurls(urls)
+ return chksum
+
+ def setfrommirrorlist(self, mirrorlist):
+ f = self.download(mirrorlist, False, None)
+ if not f:
+ return
+ f = os.fdopen(f.dup(), 'r')
+ urls = []
+ for l in f.readline():
+ l = l.strip()
+ if l[0:6] == 'http://' or l[0:7] == 'https://':
+ urls.append(l)
+ self.setfromurls(urls)
+ f.close()
+
+ def download(self, file, uncompress, chksum, markincomplete=False):
+ url = None
+ if 'baseurl' not in self:
+ if 'metalink' in self:
+ if file != self['metalink']:
+ metalinkchksum = self.setfrommetalink(self['metalink'])
+ if file == 'repodata/repomd.xml' and metalinkchksum and not chksum:
+ chksum = metalinkchksum
+ else:
+ url = file
+ elif 'mirrorlist' in self:
+ if file != self['mirrorlist']:
+ self.setfrommirrorlist(self['mirrorlist'])
+ else:
+ url = file
+ if not url:
+ if 'baseurl' not in self:
+ print("%s: no baseurl" % self.name)
+ return None
+ url = re.sub(r'/$', '', self['baseurl']) + '/' + file
+ f = tempfile.TemporaryFile()
+ st = subprocess.call(['curl', '-f', '-s', '-L', url], stdout=f.fileno())
+ if os.lseek(f.fileno(), 0, os.SEEK_CUR) == 0 and (st == 0 or not chksum):
+ return None
+ os.lseek(f.fileno(), 0, os.SEEK_SET)
+ if st:
+ print("%s: download error %d" % (file, st))
+ if markincomplete:
+ self['incomplete'] = True
+ return None
+ if chksum:
+ fchksum = solv.Chksum(chksum.type)
+ if not fchksum:
+ print("%s: unknown checksum type" % file)
+ if markincomplete:
+ self['incomplete'] = True
+ return None
+ fchksum.add_fd(f.fileno())
+ if fchksum != chksum:
+ print("%s: checksum mismatch" % file)
+ if markincomplete:
+ self['incomplete'] = True
+ return None
+ if uncompress:
+ return solv.xfopen_fd(file, f.fileno())
+ return solv.xfopen_fd(None, f.fileno())
+
+ def usecachedrepo(self, ext, mark=False):
+ try:
+ repopath = self.cachepath(ext)
+ f = open(repopath, 'rb')
+ f.seek(-32, os.SEEK_END)
+ fcookie = f.read(32)
+ if len(fcookie) != 32:
+ return False
+ if not ext:
+ cookie = self['cookie']
+ else:
+ cookie = self['extcookie']
+ if cookie and fcookie != cookie:
+ return False
+ if self.type != 'system' and not ext:
+ f.seek(-32 * 2, os.SEEK_END)
+ fextcookie = f.read(32)
+ if len(fextcookie) != 32:
+ return False
+ f.seek(0)
+ f = solv.xfopen_fd('', f.fileno())
+ flags = 0
+ if ext:
+ flags = solv.Repo.REPO_USE_LOADING|solv.Repo.REPO_EXTEND_SOLVABLES
+ if ext != 'DL':
+ flags |= solv.Repo.REPO_LOCALPOOL
+ if not self.handle.add_solv(f, flags):
+ return False
+ if self.type != 'system' and not ext:
+ self['cookie'] = fcookie
+ self['extcookie'] = fextcookie
+ if mark:
+ # no futimes in python?
+ try:
+ os.utime(repopath, None)
+ except Exception:
+ pass
+ except IOError:
+ return False
+ return True
+
+ def writecachedrepo(self, ext, repodata=None):
+ if 'incomplete' in self:
+ return
+ tmpname = None
+ try:
+ if not os.path.isdir("/var/cache/solv"):
+ os.mkdir("/var/cache/solv", 0o755)
+ (fd, tmpname) = tempfile.mkstemp(prefix='.newsolv-', dir='/var/cache/solv')
+ os.fchmod(fd, 0o444)
+ f = os.fdopen(fd, 'wb+')
+ f = solv.xfopen_fd(None, f.fileno())
+ if not repodata:
+ self.handle.write(f)
+ elif ext:
+ repodata.write(f)
+ else: # rewrite_repos case, do not write stubs
+ self.handle.write_first_repodata(f)
+ f.flush()
+ if self.type != 'system' and not ext:
+ if not self['extcookie']:
+ self['extcookie'] = self.calc_cookie_ext(f, self['cookie'])
+ f.write(self['extcookie'])
+ if not ext:
+ f.write(self['cookie'])
+ else:
+ f.write(self['extcookie'])
+ f.close
+ if self.handle.iscontiguous():
+ # switch to saved repo to activate paging and save memory
+ nf = solv.xfopen(tmpname)
+ if not ext:
+ # main repo
+ self.handle.empty()
+ flags = solv.Repo.SOLV_ADD_NO_STUBS
+ if repodata:
+ flags = 0 # rewrite repos case, recreate stubs
+ if not self.handle.add_solv(nf, flags):
+ sys.exit("internal error, cannot reload solv file")
+ else:
+ # extension repodata
+ # need to extend to repo boundaries, as this is how
+ # repodata.write() has written the data
+ repodata.extend_to_repo()
+ flags = solv.Repo.REPO_EXTEND_SOLVABLES
+ if ext != 'DL':
+ flags |= solv.Repo.REPO_LOCALPOOL
+ repodata.add_solv(nf, flags)
+ os.rename(tmpname, self.cachepath(ext))
+ except (OSError, IOError):
+ if tmpname:
+ os.unlink(tmpname)
+
+ def updateaddedprovides(self, addedprovides):
+ if 'incomplete' in self:
+ return
+ if not hasattr(self, 'handle'):
+ return
+ if self.handle.isempty():
+ return
+ # make sure there's just one real repodata with extensions
+ repodata = self.handle.first_repodata()
+ if not repodata:
+ return
+ oldaddedprovides = repodata.lookup_idarray(solv.SOLVID_META, solv.REPOSITORY_ADDEDFILEPROVIDES)
+ if not set(addedprovides) <= set(oldaddedprovides):
+ for id in addedprovides:
+ repodata.add_idarray(solv.SOLVID_META, solv.REPOSITORY_ADDEDFILEPROVIDES, id)
+ repodata.internalize()
+ self.writecachedrepo(None, repodata)
+
+ def packagespath(self):
+ return ''
+
+ def add_ext_keys(self, ext, repodata, handle):
+ if ext == 'DL':
+ repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOSITORY_DELTAINFO)
+ repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_FLEXARRAY)
+ elif ext == 'DU':
+ repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_DISKUSAGE)
+ repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRNUMNUMARRAY)
+ elif ext == 'FL':
+ repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_FILELIST)
+ repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRSTRARRAY)
+ else:
+ for langtag, langtagtype in [
+ (solv.SOLVABLE_SUMMARY, solv.REPOKEY_TYPE_STR),
+ (solv.SOLVABLE_DESCRIPTION, solv.REPOKEY_TYPE_STR),
+ (solv.SOLVABLE_EULA, solv.REPOKEY_TYPE_STR),
+ (solv.SOLVABLE_MESSAGEINS, solv.REPOKEY_TYPE_STR),
+ (solv.SOLVABLE_MESSAGEDEL, solv.REPOKEY_TYPE_STR),
+ (solv.SOLVABLE_CATEGORY, solv.REPOKEY_TYPE_ID)
+ ]:
+ repodata.add_idarray(handle, solv.REPOSITORY_KEYS, self.handle.pool.id2langid(langtag, ext, 1))
+ repodata.add_idarray(handle, solv.REPOSITORY_KEYS, langtagtype)
+
+
+class repo_repomd(repo_generic):
+ def load(self, pool):
+ if super(repo_repomd, self).load(pool):
+ return True
+ sys.stdout.write("rpmmd repo '%s': " % self.name)
+ sys.stdout.flush()
+ f = self.download("repodata/repomd.xml", False, None, None)
+ if not f:
+ print("no repomd.xml file, skipped")
+ self.handle.free(True)
+ del self.handle
+ return False
+ self['cookie'] = self.calc_cookie_fp(f)
+ if self.usecachedrepo(None, True):
+ print("cached")
+ return True
+ self.handle.add_repomdxml(f, 0)
+ print("fetching")
+ (filename, filechksum) = self.find('primary')
+ if filename:
+ f = self.download(filename, True, filechksum, True)
+ if f:
+ self.handle.add_rpmmd(f, None, 0)
+ if 'incomplete' in self:
+ return False # hopeless, need good primary
+ (filename, filechksum) = self.find('updateinfo')
+ if filename:
+ f = self.download(filename, True, filechksum, True)
+ if f:
+ self.handle.add_updateinfoxml(f, 0)
+ self.add_exts()
+ self.writecachedrepo(None)
+ # must be called after writing the repo
+ self.handle.create_stubs()
+ return True
+
+ def find(self, what):
+ di = self.handle.Dataiterator_meta(solv.REPOSITORY_REPOMD_TYPE, what, solv.Dataiterator.SEARCH_STRING)
+ di.prepend_keyname(solv.REPOSITORY_REPOMD)
+ for d in di:
+ dp = d.parentpos()
+ filename = dp.lookup_str(solv.REPOSITORY_REPOMD_LOCATION)
+ chksum = dp.lookup_checksum(solv.REPOSITORY_REPOMD_CHECKSUM)
+ if filename and not chksum:
+ print("no %s file checksum!" % filename)
+ filename = None
+ chksum = None
+ if filename:
+ return (filename, chksum)
+ return (None, None)
+
+ def add_ext(self, repodata, what, ext):
+ filename, chksum = self.find(what)
+ if not filename and what == 'deltainfo':
+ filename, chksum = self.find('prestodelta')
+ if not filename:
+ return
+ handle = repodata.new_handle()
+ repodata.set_poolstr(handle, solv.REPOSITORY_REPOMD_TYPE, what)
+ repodata.set_str(handle, solv.REPOSITORY_REPOMD_LOCATION, filename)
+ repodata.set_checksum(handle, solv.REPOSITORY_REPOMD_CHECKSUM, chksum)
+ self.add_ext_keys(ext, repodata, handle)
+ repodata.add_flexarray(solv.SOLVID_META, solv.REPOSITORY_EXTERNAL, handle)
+
+ def add_exts(self):
+ repodata = self.handle.add_repodata(0)
+ self.add_ext(repodata, 'deltainfo', 'DL')
+ self.add_ext(repodata, 'filelists', 'FL')
+ repodata.internalize()
+
+ def load_ext(self, repodata):
+ repomdtype = repodata.lookup_str(solv.SOLVID_META, solv.REPOSITORY_REPOMD_TYPE)
+ if repomdtype == 'filelists':
+ ext = 'FL'
+ elif repomdtype == 'deltainfo':
+ ext = 'DL'
+ else:
+ return False
+ sys.stdout.write("[%s:%s: " % (self.name, ext))
+ if self.usecachedrepo(ext):
+ sys.stdout.write("cached]\n")
+ sys.stdout.flush()
+ return True
+ sys.stdout.write("fetching]\n")
+ sys.stdout.flush()
+ filename = repodata.lookup_str(solv.SOLVID_META, solv.REPOSITORY_REPOMD_LOCATION)
+ filechksum = repodata.lookup_checksum(solv.SOLVID_META, solv.REPOSITORY_REPOMD_CHECKSUM)
+ f = self.download(filename, True, filechksum)
+ if not f:
+ return False
+ if ext == 'FL':
+ self.handle.add_rpmmd(f, 'FL', solv.Repo.REPO_USE_LOADING|solv.Repo.REPO_EXTEND_SOLVABLES|solv.Repo.REPO_LOCALPOOL)
+ elif ext == 'DL':
+ self.handle.add_deltainfoxml(f, solv.Repo.REPO_USE_LOADING)
+ self.writecachedrepo(ext, repodata)
+ return True
+
+class repo_susetags(repo_generic):
+ def load(self, pool):
+ if super(repo_susetags, self).load(pool):
+ return True
+ sys.stdout.write("susetags repo '%s': " % self.name)
+ sys.stdout.flush()
+ f = self.download("content", False, None, None)
+ if not f:
+ print("no content file, skipped")
+ self.handle.free(True)
+ del self.handle
+ return False
+ self['cookie'] = self.calc_cookie_fp(f)
+ if self.usecachedrepo(None, True):
+ print("cached")
+ return True
+ self.handle.add_content(f, 0)
+ print("fetching")
+ defvendorid = self.handle.meta.lookup_id(solv.SUSETAGS_DEFAULTVENDOR)
+ descrdir = self.handle.meta.lookup_str(solv.SUSETAGS_DESCRDIR)
+ if not descrdir:
+ descrdir = "suse/setup/descr"
+ (filename, filechksum) = self.find('packages.gz')
+ if not filename:
+ (filename, filechksum) = self.find('packages')
+ if filename:
+ f = self.download(descrdir + '/' + filename, True, filechksum, True)
+ if f:
+ self.handle.add_susetags(f, defvendorid, None, solv.Repo.REPO_NO_INTERNALIZE|solv.Repo.SUSETAGS_RECORD_SHARES)
+ (filename, filechksum) = self.find('packages.en.gz')
+ if not filename:
+ (filename, filechksum) = self.find('packages.en')
+ if filename:
+ f = self.download(descrdir + '/' + filename, True, filechksum, True)
+ if f:
+ self.handle.add_susetags(f, defvendorid, None, solv.Repo.REPO_NO_INTERNALIZE|solv.Repo.REPO_REUSE_REPODATA|solv.Repo.REPO_EXTEND_SOLVABLES)
+ self.handle.internalize()
+ self.add_exts()
+ self.writecachedrepo(None)
+ # must be called after writing the repo
+ self.handle.create_stubs()
+ return True
+
+ def find(self, what):
+ di = self.handle.Dataiterator_meta(solv.SUSETAGS_FILE_NAME, what, solv.Dataiterator.SEARCH_STRING)
+ di.prepend_keyname(solv.SUSETAGS_FILE)
+ for d in di:
+ dp = d.parentpos()
+ chksum = dp.lookup_checksum(solv.SUSETAGS_FILE_CHECKSUM)
+ return (what, chksum)
+ return (None, None)
+
+ def add_ext(self, repodata, what, ext):
+ (filename, chksum) = self.find(what)
+ if not filename:
+ return
+ handle = repodata.new_handle()
+ repodata.set_str(handle, solv.SUSETAGS_FILE_NAME, filename)
+ if chksum:
+ repodata.set_checksum(handle, solv.SUSETAGS_FILE_CHECKSUM, chksum)
+ self.add_ext_keys(ext, repodata, handle)
+ repodata.add_flexarray(solv.SOLVID_META, solv.REPOSITORY_EXTERNAL, handle)
+
+ def add_exts(self):
+ repodata = self.handle.add_repodata(0)
+ di = self.handle.Dataiterator_meta(solv.SUSETAGS_FILE_NAME, None, 0)
+ di.prepend_keyname(solv.SUSETAGS_FILE)
+ for d in di:
+ filename = d.str
+ if not filename:
+ continue
+ if filename[0:9] != "packages.":
+ continue
+ if len(filename) == 11 and filename != "packages.gz":
+ ext = filename[9:11]
+ elif filename[11:12] == ".":
+ ext = filename[9:11]
+ else:
+ continue
+ if ext == "en":
+ continue
+ self.add_ext(repodata, filename, ext)
+ repodata.internalize()
+
+ def load_ext(self, repodata):
+ filename = repodata.lookup_str(solv.SOLVID_META, solv.SUSETAGS_FILE_NAME)
+ ext = filename[9:11]
+ sys.stdout.write("[%s:%s: " % (self.name, ext))
+ if self.usecachedrepo(ext):
+ sys.stdout.write("cached]\n")
+ sys.stdout.flush()
+ return True
+ sys.stdout.write("fetching]\n")
+ sys.stdout.flush()
+ defvendorid = self.handle.meta.lookup_id(solv.SUSETAGS_DEFAULTVENDOR)
+ descrdir = self.handle.meta.lookup_str(solv.SUSETAGS_DESCRDIR)
+ if not descrdir:
+ descrdir = "suse/setup/descr"
+ filechksum = repodata.lookup_checksum(solv.SOLVID_META, solv.SUSETAGS_FILE_CHECKSUM)
+ f = self.download(descrdir + '/' + filename, True, filechksum)
+ if not f:
+ return False
+ flags = solv.Repo.REPO_USE_LOADING|solv.Repo.REPO_EXTEND_SOLVABLES
+ if ext != 'DL':
+ flags |= solv.Repo.REPO_LOCALPOOL
+ self.handle.add_susetags(f, defvendorid, ext, flags)
+ self.writecachedrepo(ext, repodata)
+ return True
+
+ def packagespath(self):
+ datadir = repo.handle.meta.lookup_str(solv.SUSETAGS_DATADIR)
+ if not datadir:
+ datadir = 'suse'
+ return datadir + '/'
+
+class repo_unknown(repo_generic):
+ def load(self, pool):
+ print("unsupported repo '%s': skipped" % self.name)
+ return False
+
+class repo_system(repo_generic):
+ def load(self, pool):
+ self.handle = pool.add_repo(self.name)
+ self.handle.appdata = self
+ pool.installed = self.handle
+ sys.stdout.write("rpm database: ")
+ self['cookie'] = self.calc_cookie_file("/var/lib/rpm/Packages")
+ if self.usecachedrepo(None):
+ print("cached")
+ return True
+ print("reading")
+ if hasattr(self.handle.__class__, 'add_products'):
+ self.handle.add_products("/etc/products.d", solv.Repo.REPO_NO_INTERNALIZE)
+ f = solv.xfopen(self.cachepath())
+ self.handle.add_rpmdb_reffp(f, solv.Repo.REPO_REUSE_REPODATA)
+ self.writecachedrepo(None)
+ return True
+
+class repo_cmdline(repo_generic):
+ def load(self, pool):
+ self.handle = pool.add_repo(self.name)
+ self.handle.appdata = self
+ return True
+
+def load_stub(repodata):
+ repo = repodata.repo.appdata
+ if repo:
+ return repo.load_ext(repodata)
+ return False
+
+
+parser = OptionParser(usage="usage: solv.py [options] COMMAND")
+parser.add_option('-r', '--repo', action="append", type="string", dest="repos", help="limit to specified repositories")
+parser.add_option('--best', action="store_true", dest="best", help="force installation/update to best packages")
+parser.add_option('--clean', action="store_true", dest="clean", help="delete no longer needed packages")
+(options, args) = parser.parse_args()
+if not args:
+ parser.print_help(sys.stderr)
+ sys.exit(1)
+
+cmd = args[0]
+args = args[1:]
+
+cmdabbrev = {'ls': 'list', 'in': 'install', 'rm': 'erase', 've': 'verify', 'se': 'search'}
+if cmd in cmdabbrev:
+ cmd = cmdabbrev[cmd]
+
+cmdactionmap = {
+ 'install': solv.Job.SOLVER_INSTALL,
+ 'erase': solv.Job.SOLVER_ERASE,
+ 'up': solv.Job.SOLVER_UPDATE,
+ 'dup': solv.Job.SOLVER_DISTUPGRADE,
+ 'verify': solv.Job.SOLVER_VERIFY,
+ 'list': 0,
+ 'info': 0
+}
+
+# read all repo configs
+repos = []
+reposdirs = []
+if os.path.isdir("/etc/zypp/repos.d"):
+ reposdirs = [ "/etc/zypp/repos.d" ]
+else:
+ reposdirs = [ "/etc/yum/repos.d" ]
+
+for reposdir in reposdirs:
+ if not os.path.isdir(reposdir):
+ continue
+ for reponame in sorted(glob.glob('%s/*.repo' % reposdir)):
+ cfg = INIConfig(open(reponame))
+ for alias in cfg:
+ repoattr = {'enabled': 0, 'priority': 99, 'autorefresh': 1, 'type': 'rpm-md', 'metadata_expire': 900}
+ for k in cfg[alias]:
+ repoattr[k] = cfg[alias][k]
+ if 'mirrorlist' in repoattr and 'metalink' not in repoattr:
+ if repoattr['mirrorlist'].find('/metalink'):
+ repoattr['metalink'] = repoattr['mirrorlist']
+ del repoattr['mirrorlist']
+ if repoattr['type'] == 'rpm-md':
+ repo = repo_repomd(alias, 'repomd', repoattr)
+ elif repoattr['type'] == 'yast2':
+ repo = repo_susetags(alias, 'susetags', repoattr)
+ else:
+ repo = repo_unknown(alias, 'unknown', repoattr)
+ repos.append(repo)
+
+pool = solv.Pool()
+pool.setarch()
+pool.set_loadcallback(load_stub)
+
+# now load all enabled repos into the pool
+sysrepo = repo_system('@System', 'system')
+sysrepo.load(pool)
+for repo in repos:
+ if int(repo['enabled']):
+ repo.load(pool)
+
+repofilter = None
+if options.repos:
+ for reponame in options.repos:
+ mrepos = [ repo for repo in repos if repo.name == reponame ]
+ if not mrepos:
+ print("no repository matches '%s'" % reponame)
+ sys.exit(1)
+ repo = mrepos[0]
+ if hasattr(repo, 'handle'):
+ if not repofilter:
+ repofilter = pool.Selection()
+ repofilter.add(repo.handle.Selection(solv.Job.SOLVER_SETVENDOR))
+
+if cmd == 'search':
+ pool.createwhatprovides()
+ sel = pool.Selection()
+ di = pool.Dataiterator(solv.SOLVABLE_NAME, args[0], solv.Dataiterator.SEARCH_SUBSTRING|solv.Dataiterator.SEARCH_NOCASE)
+ for d in di:
+ sel.add_raw(solv.Job.SOLVER_SOLVABLE, d.solvid)
+ if repofilter:
+ sel.filter(repofilter)
+ for s in sel.solvables():
+ print(" - %s [%s]: %s" % (s, s.repo.name, s.lookup_str(solv.SOLVABLE_SUMMARY)))
+ sys.exit(0)
+
+if cmd not in cmdactionmap:
+ print("unknown command %s" % cmd)
+ sys.exit(1)
+
+cmdlinerepo = None
+if cmd == 'list' or cmd == 'info' or cmd == 'install':
+ for arg in args:
+ if arg.endswith(".rpm") and os.access(arg, os.R_OK):
+ if not cmdlinerepo:
+ cmdlinerepo = repo_cmdline('@commandline', 'cmdline')
+ cmdlinerepo.load(pool)
+ cmdlinerepo['packages'] = {}
+ s = cmdlinerepo.handle.add_rpm(arg, solv.Repo.REPO_REUSE_REPODATA|solv.Repo.REPO_NO_INTERNALIZE)
+ if not s:
+ print(pool.errstr)
+ sys.exit(1)
+ cmdlinerepo['packages'][arg] = s
+ if cmdlinerepo:
+ cmdlinerepo.handle.internalize()
+
+addedprovides = pool.addfileprovides_queue()
+if addedprovides:
+ sysrepo.updateaddedprovides(addedprovides)
+ for repo in repos:
+ repo.updateaddedprovides(addedprovides)
+
+pool.createwhatprovides()
+
+# convert arguments into jobs
+jobs = []
+for arg in args:
+ if cmdlinerepo and arg in cmdlinerepo['packages']:
+ jobs.append(pool.Job(solv.Job.SOLVER_SOLVABLE, cmdlinerepo['packages'][arg].id))
+ else:
+ flags = solv.Selection.SELECTION_NAME|solv.Selection.SELECTION_PROVIDES|solv.Selection.SELECTION_GLOB
+ flags |= solv.Selection.SELECTION_CANON|solv.Selection.SELECTION_DOTARCH|solv.Selection.SELECTION_REL
+ if len(arg) and arg[0] == '/':
+ flags |= solv.Selection.SELECTION_FILELIST
+ if cmd == 'erase':
+ flags |= solv.Selection.SELECTION_INSTALLED_ONLY
+ sel = pool.select(arg, flags)
+ if repofilter:
+ sel.filter(repofilter)
+ if sel.isempty():
+ sel = pool.select(arg, flags | solv.Selection.SELECTION_NOCASE)
+ if repofilter:
+ sel.filter(repofilter)
+ if not sel.isempty():
+ print("[ignoring case for '%s']" % arg)
+ if sel.isempty():
+ print("nothing matches '%s'" % arg)
+ sys.exit(1)
+ if sel.flags() & solv.Selection.SELECTION_FILELIST:
+ print("[using file list match for '%s']" % arg)
+ if sel.flags() & solv.Selection.SELECTION_PROVIDES:
+ print("[using capability match for '%s']" % arg)
+ jobs += sel.jobs(cmdactionmap[cmd])
+
+if not jobs and (cmd == 'up' or cmd == 'dup' or cmd == 'verify' or repofilter):
+ sel = pool.Selection_all()
+ if repofilter:
+ sel.filter(repofilter)
+ jobs += sel.jobs(cmdactionmap[cmd])
+
+if not jobs:
+ print("no package matched.")
+ sys.exit(1)
+
+if cmd == 'list' or cmd == 'info':
+ for job in jobs:
+ for s in job.solvables():
+ if cmd == 'info':
+ print("Name: %s" % s)
+ print("Repo: %s" % s.repo)
+ print("Summary: %s" % s.lookup_str(solv.SOLVABLE_SUMMARY))
+ str = s.lookup_str(solv.SOLVABLE_URL)
+ if str:
+ print("Url: %s" % str)
+ str = s.lookup_str(solv.SOLVABLE_LICENSE)
+ if str:
+ print("License: %s" % str)
+ print("Description:\n%s" % s.lookup_str(solv.SOLVABLE_DESCRIPTION))
+ print('')
+ else:
+ print(" - %s [%s]" % (s, s.repo))
+ print(" %s" % s.lookup_str(solv.SOLVABLE_SUMMARY))
+ sys.exit(0)
+
+# up magic: use install instead of update if no installed package matches
+for job in jobs:
+ if cmd == 'up' and job.isemptyupdate():
+ job.how ^= solv.Job.SOLVER_UPDATE ^ solv.Job.SOLVER_INSTALL
+ if options.best:
+ job.how |= solv.Job.SOLVER_FORCEBEST
+ if options.clean:
+ job.how |= solv.Job.SOLVER_CLEANDEPS
+
+#pool.set_debuglevel(2)
+solver = pool.Solver()
+solver.set_flag(solv.Solver.SOLVER_FLAG_SPLITPROVIDES, 1);
+if cmd == 'erase':
+ solver.set_flag(solv.Solver.SOLVER_FLAG_ALLOW_UNINSTALL, 1);
+
+while True:
+ problems = solver.solve(jobs)
+ if not problems:
+ break
+ for problem in problems:
+ print("Problem %d/%d:" % (problem.id, len(problems)))
+ print(problem)
+ solutions = problem.solutions()
+ for solution in solutions:
+ print(" Solution %d:" % solution.id)
+ elements = solution.elements(True)
+ for element in elements:
+ print(" - %s" % element.str())
+ print('')
+ sol = ''
+ while not (sol == 's' or sol == 'q' or (sol.isdigit() and int(sol) >= 1 and int(sol) <= len(solutions))):
+ sys.stdout.write("Please choose a solution: ")
+ sys.stdout.flush()
+ sol = sys.stdin.readline().strip()
+ if sol == 's':
+ continue # skip problem
+ if sol == 'q':
+ sys.exit(1)
+ solution = solutions[int(sol) - 1]
+ for element in solution.elements():
+ newjob = element.Job()
+ if element.type == solv.Solver.SOLVER_SOLUTION_JOB:
+ jobs[element.jobidx] = newjob
+ else:
+ if newjob and newjob not in jobs:
+ jobs.append(newjob)
+
+# no problems, show transaction
+trans = solver.transaction()
+del solver
+if trans.isempty():
+ print("Nothing to do.")
+ sys.exit(0)
+print('')
+print("Transaction summary:")
+print('')
+for cl in trans.classify(solv.Transaction.SOLVER_TRANSACTION_SHOW_OBSOLETES | solv.Transaction.SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE):
+ if cl.type == solv.Transaction.SOLVER_TRANSACTION_ERASE:
+ print("%d erased packages:" % cl.count)
+ elif cl.type == solv.Transaction.SOLVER_TRANSACTION_INSTALL:
+ print("%d installed packages:" % cl.count)
+ elif cl.type == solv.Transaction.SOLVER_TRANSACTION_REINSTALLED:
+ print("%d reinstalled packages:" % cl.count)
+ elif cl.type == solv.Transaction.SOLVER_TRANSACTION_DOWNGRADED:
+ print("%d downgraded packages:" % cl.count)
+ elif cl.type == solv.Transaction.SOLVER_TRANSACTION_CHANGED:
+ print("%d changed packages:" % cl.count)
+ elif cl.type == solv.Transaction.SOLVER_TRANSACTION_UPGRADED:
+ print("%d upgraded packages:" % cl.count)
+ elif cl.type == solv.Transaction.SOLVER_TRANSACTION_VENDORCHANGE:
+ print("%d vendor changes from '%s' to '%s':" % (cl.count, cl.fromstr, cl.tostr))
+ elif cl.type == solv.Transaction.SOLVER_TRANSACTION_ARCHCHANGE:
+ print("%d arch changes from '%s' to '%s':" % (cl.count, cl.fromstr, cl.tostr))
+ else:
+ continue
+ for p in cl.solvables():
+ if cl.type == solv.Transaction.SOLVER_TRANSACTION_UPGRADED or cl.type == solv.Transaction.SOLVER_TRANSACTION_DOWNGRADED:
+ op = trans.othersolvable(p)
+ print(" - %s -> %s" % (p, op))
+ else:
+ print(" - %s" % p)
+ print('')
+print("install size change: %d K" % trans.calc_installsizechange())
+print('')
+
+while True:
+ sys.stdout.write("OK to continue (y/n)? ")
+ sys.stdout.flush()
+ yn = sys.stdin.readline().strip()
+ if yn == 'y': break
+ if yn == 'n' or yn == 'q': sys.exit(1)
+newpkgs = trans.newsolvables()
+newpkgsfp = {}
+if newpkgs:
+ downloadsize = 0
+ for p in newpkgs:
+ downloadsize += p.lookup_num(solv.SOLVABLE_DOWNLOADSIZE)
+ print("Downloading %d packages, %d K" % (len(newpkgs), downloadsize / 1024))
+ for p in newpkgs:
+ repo = p.repo.appdata
+ location, medianr = p.lookup_location()
+ if not location:
+ continue
+ if repo.type == 'commandline':
+ f = solv.xfopen(location)
+ if not f:
+ sys.exit("\n%s: %s not found" % location)
+ newpkgsfp[p.id] = f
+ continue
+ if not sysrepo.handle.isempty() and os.access('/usr/bin/applydeltarpm', os.X_OK):
+ pname = p.name
+ di = p.repo.Dataiterator_meta(solv.DELTA_PACKAGE_NAME, pname, solv.Dataiterator.SEARCH_STRING)
+ di.prepend_keyname(solv.REPOSITORY_DELTAINFO)
+ for d in di:
+ dp = d.parentpos()
+ if dp.lookup_id(solv.DELTA_PACKAGE_EVR) != p.evrid or dp.lookup_id(solv.DELTA_PACKAGE_ARCH) != p.archid:
+ continue
+ baseevrid = dp.lookup_id(solv.DELTA_BASE_EVR)
+ candidate = None
+ for installedp in pool.whatprovides(p.nameid):
+ if installedp.isinstalled() and installedp.nameid == p.nameid and installedp.archid == p.archid and installedp.evrid == baseevrid:
+ candidate = installedp
+ if not candidate:
+ continue
+ seq = dp.lookup_deltaseq()
+ st = subprocess.call(['/usr/bin/applydeltarpm', '-a', p.arch, '-c', '-s', seq])
+ if st:
+ continue
+ chksum = dp.lookup_checksum(solv.DELTA_CHECKSUM)
+ if not chksum:
+ continue
+ dloc, dmedianr = dp.lookup_deltalocation()
+ dloc = repo.packagespath() + dloc
+ f = repo.download(dloc, False, chksum)
+ if not f:
+ continue
+ nf = tempfile.TemporaryFile()
+ nf = os.dup(nf.fileno()) # get rid of CLOEXEC
+ f.cloexec(0)
+ st = subprocess.call(['/usr/bin/applydeltarpm', '-a', p.arch, "/dev/fd/%d" % f.fileno(), "/dev/fd/%d" % nf])
+ if st:
+ os.close(nf)
+ continue
+ os.lseek(nf, 0, os.SEEK_SET)
+ newpkgsfp[p.id] = solv.xfopen_fd("", nf)
+ os.close(nf)
+ break
+ if p.id in newpkgsfp:
+ sys.stdout.write("d")
+ sys.stdout.flush()
+ continue
+
+ chksum = p.lookup_checksum(solv.SOLVABLE_CHECKSUM)
+ location = repo.packagespath() + location
+ f = repo.download(location, False, chksum)
+ if not f:
+ sys.exit("\n%s: %s not found in repository" % (repo.name, location))
+ newpkgsfp[p.id] = f
+ sys.stdout.write(".")
+ sys.stdout.flush()
+ print('')
+print("Committing transaction:")
+print('')
+ts = rpm.TransactionSet('/')
+ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
+erasenamehelper = {}
+for p in trans.steps():
+ type = trans.steptype(p, solv.Transaction.SOLVER_TRANSACTION_RPM_ONLY)
+ if type == solv.Transaction.SOLVER_TRANSACTION_ERASE:
+ rpmdbid = p.lookup_num(solv.RPM_RPMDBID)
+ erasenamehelper[p.name] = p
+ if not rpmdbid:
+ sys.exit("\ninternal error: installed package %s has no rpmdbid\n" % p)
+ ts.addErase(rpmdbid)
+ elif type == solv.Transaction.SOLVER_TRANSACTION_INSTALL:
+ f = newpkgsfp[p.id]
+ h = ts.hdrFromFdno(f.fileno())
+ os.lseek(f.fileno(), 0, os.SEEK_SET)
+ ts.addInstall(h, p, 'u')
+ elif type == solv.Transaction.SOLVER_TRANSACTION_MULTIINSTALL:
+ f = newpkgsfp[p.id]
+ h = ts.hdrFromFdno(f.fileno())
+ os.lseek(f.fileno(), 0, os.SEEK_SET)
+ ts.addInstall(h, p, 'i')
+checkproblems = ts.check()
+if checkproblems:
+ print(checkproblems)
+ sys.exit("Sorry.")
+ts.order()
+def runCallback(reason, amount, total, p, d):
+ if reason == rpm.RPMCALLBACK_INST_OPEN_FILE:
+ return newpkgsfp[p.id].fileno()
+ if reason == rpm.RPMCALLBACK_INST_START:
+ print("install %s" % p)
+ if reason == rpm.RPMCALLBACK_UNINST_START:
+ # argh, p is just the name of the package
+ if p in erasenamehelper:
+ p = erasenamehelper[p]
+ print("erase %s" % p)
+runproblems = ts.run(runCallback, '')
+if runproblems:
+ print(runproblems)
+ sys.exit(1)
+sys.exit(0)
+
+# vim: sw=4 et
--- /dev/null
+#!/usr/bin/ruby
+
+require 'solv'
+require 'rubygems'
+require 'inifile'
+require 'tempfile'
+
+class Repo_generic
+ def initialize(name, type, attribs = {})
+ @name = name
+ @type = type
+ @attribs = attribs.dup
+ @incomplete = false
+ end
+
+ def enabled?
+ return @attribs['enabled'].to_i != 0
+ end
+
+ def autorefresh?
+ return @attribs['autorefresh'].to_i != 0
+ end
+
+ def id
+ return @handle ? @handle.id : 0
+ end
+
+ def calc_cookie_fp(f)
+ chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
+ chksum.add("1.1")
+ chksum.add_fp(f)
+ return chksum.raw
+ end
+
+ def calc_cookie_file(filename)
+ chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
+ chksum.add("1.1")
+ chksum.add_stat(filename)
+ return chksum.raw
+ end
+
+ def calc_cookie_ext(f, cookie)
+ chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
+ chksum.add("1.1")
+ chksum.add(cookie)
+ chksum.add_fstat(f.fileno)
+ return chksum.raw()
+ end
+
+ def cachepath(ext = nil)
+ path = @name.sub(/^\./, '_')
+ path += ext ? "_#{ext}.solvx" : '.solv'
+ return '/var/cache/solv/' + path.gsub(/\//, '_')
+ end
+
+ def load(pool)
+ @handle = pool.add_repo(@name)
+ @handle.appdata = self
+ @handle.priority = 99 - @attribs['priority'].to_i if @attribs['priority']
+ dorefresh = autorefresh?
+ if dorefresh
+ begin
+ s = File.stat(cachepath)
+ dorefresh = false if s && (@attribs['metadata_expire'].to_i == -1 || Time.now - s.mtime < @attribs['metadata_expire'].to_i)
+ rescue SystemCallError
+ end
+ end
+ @cookie = nil
+ @extcookie = nil
+ if !dorefresh && usecachedrepo(nil)
+ puts "repo: '#{@name}' cached"
+ return true
+ end
+ return false
+ end
+
+ def load_ext(repodata)
+ return false
+ end
+
+ def download(file, uncompress, chksum, markincomplete = false)
+ url = @attribs['baseurl']
+ if !url
+ puts "%{@name}: no baseurl"
+ return nil
+ end
+ url = url.sub(/\/$/, '') + "/#{file}"
+ f = Tempfile.new('rbsolv')
+ f.unlink
+ st = system('curl', '-f', '-s', '-L', '-o', "/dev/fd/" + f.fileno.to_s, '--', url)
+ return nil if f.stat.size == 0 && (st || !chksum)
+ if !st
+ puts "#{file}: download error #{$? >> 8}"
+ @incomplete = true if markincomplete
+ return nil
+ end
+ if chksum
+ fchksum = Solv::Chksum.new(chksum.type)
+ fchksum.add_fd(f.fileno)
+ if !fchksum == chksum
+ puts "#{file}: checksum error"
+ @incomplete = true if markincomplete
+ return nil
+ end
+ end
+ rf = nil
+ if uncompress
+ rf = Solv::xfopen_fd(file, f.fileno)
+ else
+ rf = Solv::xfopen_fd('', f.fileno)
+ end
+ f.close
+ return rf
+ end
+
+ def usecachedrepo(ext, mark = false)
+ cookie = ext ? @extcookie : @cookie
+ begin
+ repopath = cachepath(ext)
+ f = File.new(repopath, "r")
+ f.sysseek(-32, IO::SEEK_END)
+ fcookie = f.sysread(32)
+ return false if fcookie.length != 32
+ return false if cookie && fcookie != cookie
+ if !ext && @type != 'system'
+ f.sysseek(-32 * 2, IO::SEEK_END)
+ fextcookie = f.sysread(32)
+ return false if fextcookie.length != 32
+ end
+ f.sysseek(0, IO::SEEK_SET)
+ nf = Solv::xfopen_fd('', f.fileno)
+ f.close
+ flags = ext ? Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES : 0
+ flags |= Solv::Repo::REPO_LOCALPOOL if ext && ext != 'DL'
+ if ! @handle.add_solv(nf, flags)
+ nf.close
+ return false
+ end
+ nf.close()
+ @cookie = fcookie unless ext
+ @extcookie = fextcookie if !ext && @type != 'system'
+ now = Time.now
+ begin
+ File::utime(now, now, repopath) if mark
+ rescue SystemCallError
+ end
+ return true
+ rescue SystemCallError
+ return false
+ end
+ return true
+ end
+
+ def writecachedrepo(ext, repodata = nil)
+ return if @incomplete
+ begin
+ Dir::mkdir("/var/cache/solv", 0755) unless FileTest.directory?("/var/cache/solv")
+ f = Tempfile.new('.newsolv-', '/var/cache/solv')
+ f.chmod(0444)
+ sf = Solv::xfopen_fd('', f.fileno)
+ if !repodata
+ @handle.write(sf)
+ elsif ext
+ repodata.write(sf)
+ else
+ @handle.write_first_repodata(sf)
+ end
+ sf.close
+ f.sysseek(0, IO::SEEK_END)
+ if @type != 'system' && !ext
+ @extcookie = calc_cookie_ext(f, @cookie) unless @extcookie
+ f.syswrite(@extcookie)
+ end
+ f.syswrite(ext ? @extcookie : @cookie)
+ f.close
+ if @handle.iscontiguous?
+ sf = Solv::xfopen(f.path)
+ if sf
+ if !ext
+ @handle.empty()
+ abort("internal error, cannot reload solv file") unless @handle.add_solv(sf, repodata ? 0 : Solv::Repo::SOLV_ADD_NO_STUBS)
+ else
+ repodata.extend_to_repo()
+ flags = Solv::Repo::REPO_EXTEND_SOLVABLES
+ flags |= Solv::Repo::REPO_LOCALPOOL if ext != 'DL'
+ repodata.add_solv(sf, flags)
+ end
+ sf.close
+ end
+ end
+ File.rename(f.path, cachepath(ext))
+ f.unlink
+ return true
+ rescue SystemCallError
+ return false
+ end
+ end
+
+ def updateaddedprovides(addedprovides)
+ return if @incomplete
+ return unless @handle && !@handle.isempty?
+ repodata = @handle.first_repodata()
+ return unless repodata
+ oldaddedprovides = repodata.lookup_idarray(Solv::SOLVID_META, Solv::REPOSITORY_ADDEDFILEPROVIDES)
+ return if (oldaddedprovides | addedprovides) == oldaddedprovides
+ for id in addedprovides
+ repodata.add_idarray(Solv::SOLVID_META, Solv::REPOSITORY_ADDEDFILEPROVIDES, id)
+ end
+ repodata.internalize()
+ writecachedrepo(nil, repodata)
+ end
+
+ def packagespath()
+ return ''
+ end
+
+ @@langtags = {
+ Solv::SOLVABLE_SUMMARY => Solv::REPOKEY_TYPE_STR,
+ Solv::SOLVABLE_DESCRIPTION => Solv::REPOKEY_TYPE_STR,
+ Solv::SOLVABLE_EULA => Solv::REPOKEY_TYPE_STR,
+ Solv::SOLVABLE_MESSAGEINS => Solv::REPOKEY_TYPE_STR,
+ Solv::SOLVABLE_MESSAGEDEL => Solv::REPOKEY_TYPE_STR,
+ Solv::SOLVABLE_CATEGORY => Solv::REPOKEY_TYPE_ID,
+ }
+
+ def add_ext_keys(ext, repodata, h)
+ if ext == 'DL'
+ repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOSITORY_DELTAINFO)
+ repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_FLEXARRAY)
+ elsif ext == 'DU'
+ repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_DISKUSAGE)
+ repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRNUMNUMARRAY)
+ elsif ext == 'FL'
+ repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_FILELIST)
+ repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRSTRARRAY)
+ else
+ @@langtags.sort.each do |langid, langtype|
+ repodata.add_idarray(h, Solv::REPOSITORY_KEYS, @handle.pool.id2langid(langid, ext, true))
+ repodata.add_idarray(h, Solv::REPOSITORY_KEYS, langtype)
+ end
+ end
+ end
+end
+
+class Repo_rpmmd < Repo_generic
+
+ def find(what)
+ di = @handle.Dataiterator_meta(Solv::REPOSITORY_REPOMD_TYPE, what, Solv::Dataiterator::SEARCH_STRING)
+ di.prepend_keyname(Solv::REPOSITORY_REPOMD)
+ for d in di
+ dp = d.parentpos()
+ filename = dp.lookup_str(Solv::REPOSITORY_REPOMD_LOCATION)
+ next unless filename
+ checksum = dp.lookup_checksum(Solv::REPOSITORY_REPOMD_CHECKSUM)
+ if !checksum
+ puts "no #{filename} checksum!"
+ return nil, nil
+ end
+ return filename, checksum
+ end
+ return nil, nil
+ end
+
+ def load(pool)
+ return true if super(pool)
+ print "rpmmd repo '#{@name}: "
+ f = download("repodata/repomd.xml", false, nil, nil)
+ if !f
+ puts "no repomd.xml file, skipped"
+ @handle.free(true)
+ @handle = nil
+ return false
+ end
+ @cookie = calc_cookie_fp(f)
+ if usecachedrepo(nil, true)
+ puts "cached"
+ f.close
+ return true
+ end
+ @handle.add_repomdxml(f, 0)
+ f.close
+ puts "fetching"
+ filename, filechksum = find('primary')
+ if filename
+ f = download(filename, true, filechksum, true)
+ if f
+ @handle.add_rpmmd(f, nil, 0)
+ f.close
+ end
+ return false if @incomplete
+ end
+ filename, filechksum = find('updateinfo')
+ if filename
+ f = download(filename, true, filechksum, true)
+ if f
+ @handle.add_updateinfoxml(f, 0)
+ f.close
+ end
+ end
+ add_exts()
+ writecachedrepo(nil)
+ @handle.create_stubs()
+ return true
+ end
+
+ def add_ext(repodata, what, ext)
+ filename, filechksum = find(what)
+ filename, filechksum = find('prestodelta') if !filename && what == 'deltainfo'
+ return unless filename
+ h = repodata.new_handle()
+ repodata.set_poolstr(h, Solv::REPOSITORY_REPOMD_TYPE, what)
+ repodata.set_str(h, Solv::REPOSITORY_REPOMD_LOCATION, filename)
+ repodata.set_checksum(h, Solv::REPOSITORY_REPOMD_CHECKSUM, filechksum)
+ add_ext_keys(ext, repodata, h)
+ repodata.add_flexarray(Solv::SOLVID_META, Solv::REPOSITORY_EXTERNAL, h)
+ end
+
+ def add_exts
+ repodata = @handle.add_repodata(0)
+ add_ext(repodata, 'deltainfo', 'DL')
+ add_ext(repodata, 'filelists', 'FL')
+ repodata.internalize()
+ end
+
+ def load_ext(repodata)
+ repomdtype = repodata.lookup_str(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_TYPE)
+ if repomdtype == 'filelists'
+ ext = 'FL'
+ elsif repomdtype == 'deltainfo'
+ ext = 'DL'
+ else
+ return false
+ end
+ print "[#{@name}:#{ext}: "
+ STDOUT.flush
+ if usecachedrepo(ext)
+ puts "cached]\n"
+ return true
+ end
+ puts "fetching]\n"
+ filename = repodata.lookup_str(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_LOCATION)
+ filechksum = repodata.lookup_checksum(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_CHECKSUM)
+ f = download(filename, true, filechksum)
+ return false unless f
+ if ext == 'FL'
+ @handle.add_rpmmd(f, 'FL', Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES|Solv::Repo::REPO_LOCALPOOL)
+ elsif ext == 'DL'
+ @handle.add_deltainfoxml(f, Solv::Repo::REPO_USE_LOADING)
+ end
+ f.close
+ writecachedrepo(ext, repodata)
+ return true
+ end
+
+end
+
+class Repo_susetags < Repo_generic
+
+ def find(what)
+ di = @handle.Dataiterator_meta(Solv::SUSETAGS_FILE_NAME, what, Solv::Dataiterator::SEARCH_STRING)
+ di.prepend_keyname(Solv::SUSETAGS_FILE)
+ for d in di
+ dp = d.parentpos()
+ checksum = dp.lookup_checksum(Solv::SUSETAGS_FILE_CHECKSUM)
+ return what, checksum
+ end
+ return nil, nil
+ end
+
+ def load(pool)
+ return true if super(pool)
+ print "susetags repo '#{@name}: "
+ f = download("content", false, nil, nil)
+ if !f
+ puts "no content file, skipped"
+ @handle.free(true)
+ @handle = nil
+ return false
+ end
+ @cookie = calc_cookie_fp(f)
+ if usecachedrepo(nil, true)
+ puts "cached"
+ f.close
+ return true
+ end
+ @handle.add_content(f, 0)
+ f.close
+ puts "fetching"
+ defvendorid = @handle.meta.lookup_id(Solv::SUSETAGS_DEFAULTVENDOR)
+ descrdir = @handle.meta.lookup_str(Solv::SUSETAGS_DESCRDIR)
+ descrdir = "suse/setup/descr" unless descrdir
+ (filename, filechksum) = find('packages.gz')
+ (filename, filechksum) = find('packages') unless filename
+ if filename
+ f = download("#{descrdir}/#{filename}", true, filechksum, true)
+ if f
+ @handle.add_susetags(f, defvendorid, nil, Solv::Repo::REPO_NO_INTERNALIZE|Solv::Repo::SUSETAGS_RECORD_SHARES)
+ f.close
+ (filename, filechksum) = find('packages.en.gz')
+ (filename, filechksum) = find('packages.en') unless filename
+ if filename
+ f = download("#{descrdir}/#{filename}", true, filechksum, true)
+ if f
+ @handle.add_susetags(f, defvendorid, nil, Solv::Repo::REPO_NO_INTERNALIZE|Solv::Repo::REPO_REUSE_REPODATA|Solv::Repo::REPO_EXTEND_SOLVABLES)
+ f.close
+ end
+ end
+ @handle.internalize()
+ end
+ end
+ add_exts()
+ writecachedrepo(nil)
+ @handle.create_stubs()
+ return true
+ end
+
+ def add_ext(repodata, what, ext)
+ (filename, filechksum) = find(what)
+ h = repodata.new_handle()
+ repodata.set_str(h, Solv::SUSETAGS_FILE_NAME, filename)
+ repodata.set_checksum(h, Solv::SUSETAGS_FILE_CHECKSUM, filechksum)
+ add_ext_keys(ext, repodata, h)
+ repodata.add_flexarray(Solv::SOLVID_META, Solv::REPOSITORY_EXTERNAL, h)
+ end
+
+ def add_exts
+ repodata = @handle.add_repodata(0)
+ di = @handle.Dataiterator_meta(Solv::SUSETAGS_FILE_NAME, nil, 0)
+ di.prepend_keyname(Solv::SUSETAGS_FILE)
+ for d in di
+ filename = d.str
+ next unless filename && filename =~ /^packages\.(..)(?:\..*)$/
+ next if $1 == 'en' || $1 == 'gz'
+ add_ext(repodata, filename, $1)
+ end
+ repodata.internalize()
+ end
+
+ def load_ext(repodata)
+ filename = repodata.lookup_str(Solv::SOLVID_META, Solv::SUSETAGS_FILE_NAME)
+ ext = filename[9,2]
+ print "[#{@name}:#{ext}: "
+ STDOUT.flush
+ if usecachedrepo(ext)
+ puts "cached]\n"
+ return true
+ end
+ puts "fetching]\n"
+ defvendorid = @handle.meta.lookup_id(Solv::SUSETAGS_DEFAULTVENDOR)
+ descrdir = @handle.meta.lookup_str(Solv::SUSETAGS_DESCRDIR)
+ descrdir = "suse/setup/descr" unless descrdir
+ filechksum = repodata.lookup_checksum(Solv::SOLVID_META, Solv::SUSETAGS_FILE_CHECKSUM)
+ f = download("#{descrdir}/#{filename}", true, filechksum)
+ return false unless f
+ flags = Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES
+ flags |= Solv::Repo::REPO_LOCALPOOL if ext != 'DL'
+ @handle.add_susetags(f, defvendorid, ext, flags)
+ f.close
+ writecachedrepo(ext, repodata)
+ return true
+ end
+
+ def packagespath()
+ datadir = @handle.meta.lookup_str(Solv::SUSETAGS_DATADIR)
+ datadir = "suse" unless datadir
+ return datadir + '/'
+ end
+end
+
+class Repo_unknown < Repo_generic
+ def load(pool)
+ puts "unsupported repo '#{@name}: skipped"
+ return false
+ end
+end
+
+class Repo_system < Repo_generic
+ def load(pool)
+ @handle = pool.add_repo(@name)
+ @handle.appdata = self
+ pool.installed = @handle
+ print "rpm database: "
+ @cookie = calc_cookie_file("/var/lib/rpm/Packages")
+ if usecachedrepo(nil)
+ puts "cached"
+ return true
+ end
+ puts "reading"
+ if @handle.respond_to? :add_products
+ @handle.add_products("/etc/products.d", Solv::Repo::REPO_NO_INTERNALIZE)
+ end
+ f = Solv::xfopen(cachepath())
+ @handle.add_rpmdb_reffp(f, Solv::Repo::REPO_REUSE_REPODATA)
+ f.close if f
+ writecachedrepo(nil)
+ return true
+ end
+end
+
+args = ARGV
+cmd = args.shift
+
+cmdabbrev = { 'ls' => 'list', 'in' => 'install', 'rm' => 'erase',
+ 've' => 'verify', 'se' => 'search' }
+cmd = cmdabbrev[cmd] if cmdabbrev.has_key?(cmd)
+
+cmdactionmap = {
+ 'install' => Solv::Job::SOLVER_INSTALL,
+ 'erase' => Solv::Job::SOLVER_ERASE,
+ 'up' => Solv::Job::SOLVER_UPDATE,
+ 'dup' => Solv::Job::SOLVER_DISTUPGRADE,
+ 'verify' => Solv::Job::SOLVER_VERIFY,
+ 'list' => 0,
+ 'info' => 0,
+}
+
+repos = []
+reposdirs = []
+if FileTest.directory?('/etc/zypp/repos.d')
+ reposdirs = [ '/etc/zypp/repos.d' ]
+else
+ reposdirs = [ '/etc/yum/repos.d' ]
+end
+for reposdir in reposdirs do
+ next unless FileTest.directory?(reposdir)
+ for reponame in Dir["#{reposdir}/*.repo"].sort do
+ cfg = IniFile.load(reponame)
+ cfg.each_section do |ali|
+ repoattr = { 'alias' => ali, 'enabled' => 0, 'priority' => 99, 'autorefresh' => 1, 'type' => 'rpm-md', 'metadata_expire' => 900}
+ repoattr.update(cfg[ali])
+ if repoattr['type'] == 'rpm-md'
+ repo = Repo_rpmmd.new(ali, 'repomd', repoattr)
+ elsif repoattr['type'] == 'yast2'
+ repo = Repo_susetags.new(ali, 'susetags', repoattr)
+ else
+ repo = Repo_unknown.new(ali, 'unknown', repoattr)
+ end
+ repos.push(repo)
+ end
+ end
+end
+
+pool = Solv::Pool.new()
+pool.setarch()
+
+pool.set_loadcallback { |repodata|
+ repo = repodata.repo.appdata
+ repo ? repo.load_ext(repodata) : false
+}
+
+sysrepo = Repo_system.new('@System', 'system')
+sysrepo.load(pool)
+for repo in repos
+ repo.load(pool) if repo.enabled?
+end
+
+if cmd == 'search'
+ pool.createwhatprovides()
+ sel = pool.Selection
+ for di in pool.Dataiterator(Solv::SOLVABLE_NAME, args[0], Solv::Dataiterator::SEARCH_SUBSTRING | Solv::Dataiterator::SEARCH_NOCASE)
+ sel.add_raw(Solv::Job::SOLVER_SOLVABLE, di.solvid)
+ end
+ for s in sel.solvables
+ puts "- #{s.str} [#{s.repo.name}]: #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
+ end
+ exit
+end
+
+abort("unknown command '#{cmd}'\n") unless cmdactionmap.has_key?(cmd)
+
+addedprovides = pool.addfileprovides_queue()
+if !addedprovides.empty?
+ sysrepo.updateaddedprovides(addedprovides)
+ for repo in repos
+ repo.updateaddedprovides(addedprovides)
+ end
+end
+pool.createwhatprovides()
+
+jobs = []
+for arg in args
+ flags = Solv::Selection::SELECTION_NAME | Solv::Selection::SELECTION_PROVIDES | Solv::Selection::SELECTION_GLOB
+ flags |= Solv::Selection::SELECTION_CANON | Solv::Selection::SELECTION_DOTARCH | Solv::Selection::SELECTION_REL
+ if arg =~ /^\//
+ flags |= Solv::Selection::SELECTION_FILELIST
+ flags |= Solv::Selection::SELECTION_INSTALLED_ONLY if cmd == 'erase'
+ end
+ sel = pool.select(arg, flags)
+ if sel.isempty?
+ sel = pool.select(arg, flags | Solv::Selection::SELECTION_NOCASE)
+ puts "[ignoring case for '#{arg}']" unless sel.isempty?
+ end
+ puts "[using file list match for '#{arg}']" if sel.flags & Solv::Selection::SELECTION_FILELIST != 0
+ puts "[using capability match for '#{arg}']" if sel.flags & Solv::Selection::SELECTION_PROVIDES != 0
+ jobs += sel.jobs(cmdactionmap[cmd])
+end
+
+if jobs.empty? && (cmd == 'up' || cmd == 'dup' || cmd == 'verify')
+ sel = pool.Selection_all()
+ jobs += sel.jobs(cmdactionmap[cmd])
+end
+
+abort("no package matched.") if jobs.empty?
+
+if cmd == 'list' || cmd == 'info'
+ for job in jobs
+ for s in job.solvables()
+ if cmd == 'info'
+ puts "Name: #{s.str}"
+ puts "Repo: #{s.repo.name}"
+ puts "Summary: #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
+ str = s.lookup_str(Solv::SOLVABLE_URL)
+ puts "Url: #{str}" if str
+ str = s.lookup_str(Solv::SOLVABLE_LICENSE)
+ puts "License: #{str}" if str
+ puts "Description:\n#{s.lookup_str(Solv::SOLVABLE_DESCRIPTION)}"
+ puts
+ else
+ puts " - #{s.str} [#{s.repo.name}]"
+ puts " #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
+ end
+ end
+ end
+ exit
+end
+
+for job in jobs
+ job.how ^= Solv::Job::SOLVER_UPDATE ^ Solv::Job::SOLVER_INSTALL if cmd == 'up' and job.isemptyupdate?
+end
+
+solver = pool.Solver
+solver.set_flag(Solv::Solver::SOLVER_FLAG_SPLITPROVIDES, 1)
+solver.set_flag(Solv::Solver::SOLVER_FLAG_ALLOW_UNINSTALL, 1) if cmd == 'erase'
+#pool.set_debuglevel(1)
+
+while true
+ problems = solver.solve(jobs)
+ break if problems.empty?
+ for problem in problems
+ puts "Problem #{problem.id}/#{problems.count}:"
+ puts problem
+ solutions = problem.solutions
+ for solution in solutions
+ puts " Solution #{solution.id}:"
+ elements = solution.elements(true)
+ for element in elements
+ puts " - #{element.str}"
+ end
+ puts
+ end
+ sol = nil
+ while true
+ print "Please choose a solution: "
+ STDOUT.flush
+ sol = STDIN.gets.strip
+ break if sol == 's' || sol == 'q'
+ break if sol =~ /^\d+$/ && sol.to_i >= 1 && sol.to_i <= solutions.length
+ end
+ next if sol == 's'
+ abort if sol == 'q'
+ solution = solutions[sol.to_i - 1]
+ for element in solution.elements
+ newjob = element.Job()
+ if element.type == Solv::Solver::SOLVER_SOLUTION_JOB
+ jobs[element.jobidx] = newjob
+ else
+ jobs.push(newjob) if newjob && !jobs.include?(newjob)
+ end
+ end
+ end
+end
+
+trans = solver.transaction
+solver = nil
+if trans.isempty?
+ puts "Nothing to do."
+ exit
+end
+
+puts "\nTransaction summary:\n"
+for cl in trans.classify(Solv::Transaction::SOLVER_TRANSACTION_SHOW_OBSOLETES | Solv::Transaction::SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE)
+ if cl.type == Solv::Transaction::SOLVER_TRANSACTION_ERASE
+ puts "#{cl.count} erased packages:"
+ elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_INSTALL
+ puts "#{cl.count} installed packages:"
+ elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_REINSTALLED
+ puts "#{cl.count} reinstalled packages:"
+ elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED
+ puts "#{cl.count} downgraded packages:"
+ elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_CHANGED
+ puts "#{cl.count} changed packages:"
+ elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_UPGRADED
+ puts "#{cl.count} upgraded packages:"
+ elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_VENDORCHANGE
+ puts "#{cl.count} vendor changes from '#{cl.fromstr}' to '#{cl.tostr}':"
+ elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_ARCHCHANGE
+ puts "#{cl.count} arch changes from '#{cl.fromstr}' to '#{cl.tostr}':"
+ else
+ next
+ end
+ for p in cl.solvables
+ if cl.type == Solv::Transaction::SOLVER_TRANSACTION_UPGRADED || cl.type == Solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED
+ puts " - #{p.str} -> #{trans.othersolvable(p).str}"
+ else
+ puts " - #{p.str}"
+ end
+ end
+ puts
+end
+puts "install size change: #{trans.calc_installsizechange()} K\n\n"
+
+while true
+ print("OK to continue (y/n)? ")
+ STDOUT.flush
+ yn = STDIN.gets.strip
+ break if yn == 'y'
+ abort if yn == 'n' || yn == 'q'
+end
+
+newpkgs = trans.newsolvables()
+newpkgsfp = {}
+if !newpkgs.empty?
+ downloadsize = 0
+ for p in newpkgs
+ downloadsize += p.lookup_num(Solv::SOLVABLE_DOWNLOADSIZE)
+ end
+ puts "Downloading #{newpkgs.length} packages, #{downloadsize / 1024} K"
+ for p in newpkgs
+ repo = p.repo.appdata
+ location, medianr = p.lookup_location()
+ next unless location
+ location = repo.packagespath + location
+ chksum = p.lookup_checksum(Solv::SOLVABLE_CHECKSUM)
+ f = repo.download(location, false, chksum)
+ abort("\n#{@name}: #{location} not found in repository\n") unless f
+ newpkgsfp[p.id] = f
+ print "."
+ STDOUT.flush()
+ end
+ puts
+end
+
+puts "Committing transaction:"
+puts
+trans.order()
+for p in trans.steps
+ steptype = trans.steptype(p, Solv::Transaction::SOLVER_TRANSACTION_RPM_ONLY)
+ if steptype == Solv::Transaction::SOLVER_TRANSACTION_ERASE
+ puts "erase #{p.str}"
+ next unless p.lookup_num(Solv::RPM_RPMDBID)
+ evr = p.evr.sub(/^[0-9]+:/, '')
+ system('rpm', '-e', '--nodeps', '--nodigest', '--nosignature', "#{p.name}-#{evr}.#{p.arch}") || abort("rpm failed: #{$? >> 8}")
+ elsif (steptype == Solv::Transaction::SOLVER_TRANSACTION_INSTALL || steptype == Solv::Transaction::SOLVER_TRANSACTION_MULTIINSTALL)
+ puts "install #{p.str}"
+ f = newpkgsfp.delete(p.id)
+ next unless f
+ mode = steptype == Solv::Transaction::SOLVER_TRANSACTION_INSTALL ? '-U' : '-i'
+ f.cloexec(0)
+ system('rpm', mode, '--force', '--nodeps', '--nodigest', '--nosignature', "/dev/fd/#{f.fileno().to_s}") || abort("rpm failed: #{$? >> 8}")
+ f.close
+ end
+end
--- /dev/null
+
+ADD_EXECUTABLE (solv solv.c
+checksig.c
+deltarpm.c
+fastestmirror.c
+fileconflicts.c
+fileprovides.c
+mirror.c
+patchjobs.c
+repoinfo.c
+repoinfo_cache.c
+repoinfo_config_debian.c
+repoinfo_config_yum.c
+repoinfo_config_urpmi.c
+repoinfo_download.c
+repoinfo_system_debian.c
+repoinfo_system_rpm.c
+repoinfo_type_debian.c
+repoinfo_type_mdk.c
+repoinfo_type_rpmmd.c
+repoinfo_type_susetags.c
+)
+
+TARGET_LINK_LIBRARIES (solv libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+INSTALL(TARGETS
+ solv
+ DESTINATION ${BIN_INSTALL_DIR})
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "pool.h"
+#include "repo.h"
+#ifdef ENABLE_PUBKEY
+#include "repo_pubkey.h"
+#endif
+
+#include "checksig.h"
+
+#ifndef DEBIAN
+
+static void
+cleanupgpg(char *gpgdir)
+{
+ char cmd[256];
+ snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
+ unlink(cmd);
+ snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
+ unlink(cmd);
+ snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
+ unlink(cmd);
+ snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
+ unlink(cmd);
+ snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
+ unlink(cmd);
+ rmdir(gpgdir);
+}
+
+int
+checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
+{
+ char *gpgdir;
+ char *keysfile;
+ const char *pubkey;
+ char cmd[256];
+ FILE *kfp;
+ Solvable *s;
+ Id p;
+ off_t posfp, possigfp;
+ int r, nkeys;
+
+ gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
+ if (!gpgdir)
+ return 0;
+ keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
+ if (!(kfp = fopen(keysfile, "w")) )
+ {
+ cleanupgpg(gpgdir);
+ return 0;
+ }
+ nkeys = 0;
+ for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
+ {
+ if (!s->repo)
+ continue;
+ pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
+ if (!pubkey || !*pubkey)
+ continue;
+ if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
+ break;
+ if (fputc('\n', kfp) == EOF) /* Just in case... */
+ break;
+ nkeys++;
+ }
+ if (fclose(kfp) || !nkeys || p < sigpool->nsolvables)
+ {
+ cleanupgpg(gpgdir);
+ return 0;
+ }
+ snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
+ if (system(cmd))
+ {
+ fprintf(stderr, "key import error\n");
+ cleanupgpg(gpgdir);
+ return 0;
+ }
+ unlink(keysfile);
+ posfp = lseek(fileno(fp), 0, SEEK_CUR);
+ lseek(fileno(fp), 0, SEEK_SET);
+ possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
+ lseek(fileno(sigfp), 0, SEEK_SET);
+ snprintf(cmd, sizeof(cmd), "gpgv -q --homedir %s --keyring %s/pubring.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, gpgdir, fileno(sigfp), fileno(fp));
+ fcntl(fileno(fp), F_SETFD, 0); /* clear CLOEXEC */
+ fcntl(fileno(sigfp), F_SETFD, 0); /* clear CLOEXEC */
+ r = system(cmd);
+ lseek(fileno(sigfp), possigfp, SEEK_SET);
+ lseek(fileno(fp), posfp, SEEK_SET);
+ fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
+ cleanupgpg(gpgdir);
+ return r == 0 ? 1 : 0;
+}
+
+#else
+
+int
+checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
+{
+ char cmd[256];
+ int r;
+
+ snprintf(cmd, sizeof(cmd), "gpgv -q --keyring /etc/apt/trusted.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", fileno(sigfp), fileno(fp));
+ fcntl(fileno(fp), F_SETFD, 0); /* clear CLOEXEC */
+ fcntl(fileno(sigfp), F_SETFD, 0); /* clear CLOEXEC */
+ r = system(cmd);
+ fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
+ return r == 0 ? 1 : 0;
+}
+
+#endif
+
+Pool *
+read_sigs()
+{
+ Pool *sigpool = pool_create();
+#if defined(ENABLE_PUBKEY) && defined(ENABLE_RPMDB)
+ Repo *repo = repo_create(sigpool, "pubkeys");
+ repo_add_rpmdb_pubkeys(repo, 0);
+#endif
+ return sigpool;
+}
--- /dev/null
+extern int checksig(Pool *sigpool, FILE *fp, FILE *sigfp);
+extern Pool *read_sigs();
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repoinfo.h"
+#include "repoinfo_download.h"
+
+#include "deltarpm.h"
+
+static inline int
+opentmpfile()
+{
+ char tmpl[100];
+ int fd;
+
+ strcpy(tmpl, "/var/tmp/solvXXXXXX");
+ fd = mkstemp(tmpl);
+ if (fd < 0)
+ {
+ perror("mkstemp");
+ exit(1);
+ }
+ unlink(tmpl);
+ return fd;
+}
+
+FILE *
+trydeltadownload(Solvable *s, const char *loc)
+{
+ Repo *repo = s->repo;
+ Pool *pool = repo->pool;
+ struct repoinfo *cinfo = repo->appdata;
+ Dataiterator di;
+ Id pp;
+ const unsigned char *chksum;
+ Id chksumtype;
+ FILE *retfp = 0;
+ char *matchname = strdup(pool_id2str(pool, s->name));
+
+ dataiterator_init(&di, pool, repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
+ dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
+ while (dataiterator_step(&di))
+ {
+ Id baseevr, op;
+
+ dataiterator_setpos_parent(&di);
+ if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
+ pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
+ continue;
+ baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
+ FOR_PROVIDES(op, pp, s->name)
+ {
+ Solvable *os = pool->solvables + op;
+ if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
+ break;
+ }
+ if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
+ {
+ /* base is installed, run sequence check */
+ const char *seq;
+ const char *dloc;
+ const char *archstr;
+ FILE *fp;
+ char cmd[128];
+ int newfd;
+
+ archstr = pool_id2str(pool, s->arch);
+ if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
+ continue;
+
+ seq = pool_tmpjoin(pool, pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME), "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
+ seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
+ if (strchr(seq, '\'') != 0)
+ continue;
+#ifdef FEDORA
+ sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
+#else
+ sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
+#endif
+ if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
+ continue; /* didn't match */
+ /* looks good, download delta */
+ chksumtype = 0;
+ chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
+ if (!chksumtype)
+ continue; /* no way! */
+ dloc = pool_lookup_deltalocation(pool, SOLVID_POS, 0);
+ if (!dloc)
+ continue;
+#ifdef ENABLE_SUSEREPO
+ if (cinfo->type == TYPE_SUSETAGS)
+ {
+ const char *datadir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DATADIR);
+ dloc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", dloc);
+ }
+#endif
+ if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
+ continue;
+ /* got it, now reconstruct */
+ newfd = opentmpfile();
+#ifdef FEDORA
+ sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
+#else
+ sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
+#endif
+ fcntl(fileno(fp), F_SETFD, 0);
+ if (system(cmd))
+ {
+ close(newfd);
+ fclose(fp);
+ continue;
+ }
+ lseek(newfd, 0, SEEK_SET);
+ chksumtype = 0;
+ chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
+ if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
+ {
+ close(newfd);
+ fclose(fp);
+ continue;
+ }
+ retfp = fdopen(newfd, "r");
+ fclose(fp);
+ break;
+ }
+ }
+ dataiterator_free(&di);
+ solv_free(matchname);
+ return retfp;
+}
--- /dev/null
+extern FILE *trydeltadownload(Solvable *s, const char *loc);
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <poll.h>
+#include <errno.h>
+
+#include "util.h"
+
+#include "fastestmirror.h"
+
+void
+findfastest(char **urls, int nurls)
+{
+ int i, j, port;
+ int *socks, qc;
+ struct pollfd *fds;
+ char *p, *p2, *q;
+ char portstr[16];
+ struct addrinfo hints, *result;;
+
+ fds = solv_calloc(nurls, sizeof(*fds));
+ socks = solv_calloc(nurls, sizeof(*socks));
+ for (i = 0; i < nurls; i++)
+ {
+ socks[i] = -1;
+ p = strchr(urls[i], '/');
+ if (!p)
+ continue;
+ if (p[1] != '/')
+ continue;
+ p += 2;
+ q = strchr(p, '/');
+ qc = 0;
+ if (q)
+ {
+ qc = *q;
+ *q = 0;
+ }
+ if ((p2 = strchr(p, '@')) != 0)
+ p = p2 + 1;
+ port = 80;
+ if (!strncmp("https:", urls[i], 6))
+ port = 443;
+ else if (!strncmp("ftp:", urls[i], 4))
+ port = 21;
+ if ((p2 = strrchr(p, ':')) != 0)
+ {
+ port = atoi(p2 + 1);
+ if (q)
+ *q = qc;
+ q = p2;
+ qc = *q;
+ *q = 0;
+ }
+ sprintf(portstr, "%d", port);
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICSERV;
+ result = 0;
+ if (!getaddrinfo(p, portstr, &hints, &result))
+ {
+ socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
+ if (socks[i] >= 0)
+ {
+ fcntl(socks[i], F_SETFL, O_NONBLOCK);
+ if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
+ {
+ if (errno != EINPROGRESS)
+ {
+ close(socks[i]);
+ socks[i] = -1;
+ }
+ }
+ }
+ freeaddrinfo(result);
+ }
+ if (q)
+ *q = qc;
+ }
+ for (;;)
+ {
+ for (i = j = 0; i < nurls; i++)
+ {
+ if (socks[i] < 0)
+ continue;
+ fds[j].fd = socks[i];
+ fds[j].events = POLLOUT;
+ j++;
+ }
+ if (j < 2)
+ {
+ i = j - 1;
+ break;
+ }
+ if (poll(fds, j, 10000) <= 0)
+ {
+ i = -1; /* something is wrong */
+ break;
+ }
+ for (i = 0; i < j; i++)
+ if ((fds[i].revents & POLLOUT) != 0)
+ {
+ int soe = 0;
+ socklen_t soel = sizeof(int);
+ if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
+ {
+ /* connect failed, kill socket */
+ for (j = 0; j < nurls; j++)
+ if (socks[j] == fds[i].fd)
+ {
+ close(socks[j]);
+ socks[j] = -1;
+ }
+ i = j + 1;
+ break;
+ }
+ break; /* horray! */
+ }
+ if (i == j + 1)
+ continue;
+ if (i == j)
+ i = -1; /* something is wrong, no bit was set */
+ break;
+ }
+ /* now i contains the fastest fd index */
+ if (i >= 0)
+ {
+ for (j = 0; j < nurls; j++)
+ if (socks[j] == fds[i].fd)
+ break;
+ if (j != 0)
+ {
+ char *url0 = urls[0];
+ urls[0] = urls[j];
+ urls[j] = url0;
+ }
+ }
+ for (i = j = 0; i < nurls; i++)
+ if (socks[i] >= 0)
+ close(socks[i]);
+ free(socks);
+ free(fds);
+}
--- /dev/null
+extern void findfastest(char **urls, int nurls);
+
--- /dev/null
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+#include "pool_fileconflicts.h"
+
+#include "fileconflicts.h"
+
+struct fcstate {
+ FILE **newpkgsfps;
+ Queue *checkq;
+ int newpkgscnt;
+ void *rpmstate;
+};
+
+static void *
+fileconflict_cb(Pool *pool, Id p, void *cbdata)
+{
+ struct fcstate *fcstate = cbdata;
+ Solvable *s;
+ Id rpmdbid;
+ int i;
+ FILE *fp;
+
+ s = pool_id2solvable(pool, p);
+ if (pool->installed && s->repo == pool->installed)
+ {
+ if (!s->repo->rpmdbid)
+ return 0;
+ rpmdbid = s->repo->rpmdbid[p - s->repo->start];
+ if (!rpmdbid)
+ return 0;
+ return rpm_byrpmdbid(fcstate->rpmstate, rpmdbid);
+ }
+ for (i = 0; i < fcstate->newpkgscnt; i++)
+ if (fcstate->checkq->elements[i] == p)
+ break;
+ if (i == fcstate->newpkgscnt)
+ return 0;
+ fp = fcstate->newpkgsfps[i];
+ if (!fp)
+ return 0;
+ rewind(fp);
+ return rpm_byfp(fcstate->rpmstate, fp, pool_solvable2str(pool, s));
+}
+
+int
+checkfileconflicts(Pool *pool, Queue *checkq, int newpkgs, FILE **newpkgsfps, Queue *conflicts)
+{
+ struct fcstate fcstate;
+ int i;
+
+ printf("Searching for file conflicts\n");
+ queue_init(conflicts);
+ fcstate.rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
+ fcstate.newpkgscnt = newpkgs;
+ fcstate.checkq = checkq;
+ fcstate.newpkgsfps = newpkgsfps;
+ pool_findfileconflicts(pool, checkq, newpkgs, conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &fileconflict_cb, &fcstate);
+ fcstate.rpmstate = rpm_state_free(fcstate.rpmstate);
+ if (conflicts->count)
+ {
+ printf("\n");
+ for (i = 0; i < conflicts->count; i += 6)
+ printf("file %s of package %s conflicts with package %s\n", pool_id2str(pool, conflicts->elements[i]), pool_solvid2str(pool, conflicts->elements[i + 1]), pool_solvid2str(pool, conflicts->elements[i + 4]));
+ printf("\n");
+ }
+ return conflicts->count;
+}
+
+#endif
--- /dev/null
+extern int checkfileconflicts(Pool *pool, Queue *checkq, int newpkgs, FILE **newpkgsfps, Queue *conflicts);
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#include "fileprovides.h"
+
+static void
+rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
+{
+ Repo *repo;
+ Repodata *data;
+ Map providedids;
+ Queue fileprovidesq;
+ int i, j, n;
+ struct repoinfo *cinfo;
+
+ map_init(&providedids, pool->ss.nstrings);
+ queue_init(&fileprovidesq);
+ for (i = 0; i < addedfileprovides->count; i++)
+ MAPSET(&providedids, addedfileprovides->elements[i]);
+ FOR_REPOS(i, repo)
+ {
+ /* make sure all repodatas but the first are extensions */
+ if (repo->nrepodata < 2)
+ continue;
+ cinfo = repo->appdata;
+ if (!cinfo)
+ continue; /* cmdline */
+ if (cinfo->incomplete)
+ continue;
+ data = repo_id2repodata(repo, 1);
+ if (data->loadcallback)
+ continue;
+ for (j = 2; j < repo->nrepodata; j++)
+ {
+ Repodata *edata = repo_id2repodata(repo, j);
+ if (!edata->loadcallback)
+ break;
+ }
+ if (j < repo->nrepodata)
+ continue; /* found a non-extension repodata, can't rewrite */
+ if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
+ {
+ if (repo == pool->installed && addedfileprovides_inst)
+ {
+ for (j = 0; j < addedfileprovides->count; j++)
+ MAPCLR(&providedids, addedfileprovides->elements[j]);
+ for (j = 0; j < addedfileprovides_inst->count; j++)
+ MAPSET(&providedids, addedfileprovides_inst->elements[j]);
+ }
+ n = 0;
+ for (j = 0; j < fileprovidesq.count; j++)
+ if (MAPTST(&providedids, fileprovidesq.elements[j]))
+ n++;
+ if (repo == pool->installed && addedfileprovides_inst)
+ {
+ for (j = 0; j < addedfileprovides_inst->count; j++)
+ MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
+ for (j = 0; j < addedfileprovides->count; j++)
+ MAPSET(&providedids, addedfileprovides->elements[j]);
+ if (n == addedfileprovides_inst->count)
+ continue; /* nothing new added */
+ }
+ else if (n == addedfileprovides->count)
+ continue; /* nothing new added */
+ }
+ repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
+ repodata_internalize(data);
+ writecachedrepo(cinfo, 0, data);
+ }
+ queue_free(&fileprovidesq);
+ map_free(&providedids);
+}
+
+void
+addfileprovides(Pool *pool)
+{
+ Queue addedfileprovides;
+ Queue addedfileprovides_inst;
+
+ queue_init(&addedfileprovides);
+ queue_init(&addedfileprovides_inst);
+ pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
+ if (addedfileprovides.count || addedfileprovides_inst.count)
+ rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
+ queue_free(&addedfileprovides);
+ queue_free(&addedfileprovides_inst);
+}
--- /dev/null
+void addfileprovides(Pool *pool);
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pool.h"
+#include "util.h"
+#include "fastestmirror.h"
+
+#include "mirror.h"
+
+char *
+findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
+{
+ char buf[4096], *bp, *ep;
+ char **urls = 0;
+ int nurls = 0;
+ int i;
+
+ if (chksumtypep)
+ *chksumtypep = 0;
+ while((bp = fgets(buf, sizeof(buf), fp)) != 0)
+ {
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
+ {
+ bp += 20;
+ if (solv_hex2bin((const char **)&bp, chksump, 32) == 32)
+ *chksumtypep = REPOKEY_TYPE_SHA256;
+ continue;
+ }
+ if (strncmp(bp, "<url", 4))
+ continue;
+ bp = strchr(bp, '>');
+ if (!bp)
+ continue;
+ bp++;
+ ep = strstr(bp, "repodata/repomd.xml</url>");
+ if (!ep)
+ continue;
+ *ep = 0;
+ if (strncmp(bp, "http", 4))
+ continue;
+ urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
+ urls[nurls++] = strdup(bp);
+ }
+ if (nurls)
+ {
+ if (nurls > 1)
+ findfastest(urls, nurls > 5 ? 5 : nurls);
+ bp = urls[0];
+ urls[0] = 0;
+ for (i = 0; i < nurls; i++)
+ solv_free(urls[i]);
+ solv_free(urls);
+ ep = strchr(bp, '/');
+ if ((ep = strchr(ep + 2, '/')) != 0)
+ {
+ *ep = 0;
+ printf("[using mirror %s]\n", bp);
+ *ep = '/';
+ }
+ return bp;
+ }
+ return 0;
+}
+
+char *
+findmirrorlisturl(FILE *fp)
+{
+ char buf[4096], *bp, *ep;
+ int i, l;
+ char **urls = 0;
+ int nurls = 0;
+
+ while((bp = fgets(buf, sizeof(buf), fp)) != 0)
+ {
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ if (!*bp || *bp == '#')
+ continue;
+ l = strlen(bp);
+ while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
+ bp[--l] = 0;
+ if ((ep = strstr(bp, "url=")) != 0)
+ bp = ep + 4;
+ urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
+ urls[nurls++] = strdup(bp);
+ }
+ if (nurls)
+ {
+ if (nurls > 1)
+ findfastest(urls, nurls > 5 ? 5 : nurls);
+ bp = urls[0];
+ urls[0] = 0;
+ for (i = 0; i < nurls; i++)
+ solv_free(urls[i]);
+ solv_free(urls);
+ ep = strchr(bp, '/');
+ if ((ep = strchr(ep + 2, '/')) != 0)
+ {
+ *ep = 0;
+ printf("[using mirror %s]\n", bp);
+ *ep = '/';
+ }
+ return bp;
+ }
+ return 0;
+}
--- /dev/null
+char *findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep);
+char *findmirrorlisturl(FILE *fp);
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "evr.h"
+#include "solver.h"
+
+void
+add_patchjobs(Pool *pool, Queue *job)
+{
+ Id p, pp;
+ int pruneyou = 0;
+ Map installedmap, multiversionmap;
+ Solvable *s;
+
+ map_init(&multiversionmap, 0);
+ map_init(&installedmap, pool->nsolvables);
+ solver_calculate_multiversionmap(pool, job, &multiversionmap);
+ if (pool->installed)
+ FOR_REPO_SOLVABLES(pool->installed, p, s)
+ MAPSET(&installedmap, p);
+
+ /* install all patches */
+ for (p = 1; p < pool->nsolvables; p++)
+ {
+ const char *type;
+ int r;
+ Id p2;
+
+ s = pool->solvables + p;
+ if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
+ continue;
+ FOR_PROVIDES(p2, pp, s->name)
+ {
+ Solvable *s2 = pool->solvables + p2;
+ if (s2->name != s->name)
+ continue;
+ r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
+ if (r < 0 || (r == 0 && p > p2))
+ break;
+ }
+ if (p2)
+ continue;
+ type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
+ if (type && !strcmp(type, "optional"))
+ continue;
+ r = solvable_trivial_installable_map(s, &installedmap, 0, &multiversionmap);
+ if (r == -1)
+ continue;
+ if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
+ {
+ if (!pruneyou++)
+ queue_empty(job);
+ }
+ else if (pruneyou)
+ continue;
+ queue_push2(job, SOLVER_SOLVABLE, p);
+ }
+ map_free(&installedmap);
+ map_free(&multiversionmap);
+}
--- /dev/null
+extern void add_patchjobs(Pool *pool, Queue *job);
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+#include "repo_rpmdb.h"
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+#include "repo_deb.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#if defined(SUSE) || defined(FEDORA)
+#include "repoinfo_config_yum.h"
+#endif
+#if defined(DEBIAN)
+#include "repoinfo_config_debian.h"
+#endif
+#if defined(MANDRIVA) || defined(MAGEIA)
+#include "repoinfo_config_urpmi.h"
+#endif
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+#include "repoinfo_system_rpm.h"
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+#include "repoinfo_system_debian.h"
+#endif
+
+#ifdef ENABLE_RPMMD
+#include "repoinfo_type_rpmmd.h"
+#endif
+#ifdef ENABLE_SUSEREPO
+#include "repoinfo_type_susetags.h"
+#endif
+#ifdef ENABLE_DEBIAN
+#include "repoinfo_type_debian.h"
+#endif
+#ifdef ENABLE_MDKREPO
+#include "repoinfo_type_mdk.h"
+#endif
+
+static int
+repoinfos_sort_cmp(const void *ap, const void *bp)
+{
+ const struct repoinfo *a = ap;
+ const struct repoinfo *b = bp;
+ return strcmp(a->alias, b->alias);
+}
+
+void
+sort_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
+{
+ qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), repoinfos_sort_cmp);
+}
+
+void
+free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
+{
+ int i, j;
+ for (i = 0; i < nrepoinfos; i++)
+ {
+ struct repoinfo *cinfo = repoinfos + i;
+ solv_free(cinfo->name);
+ solv_free(cinfo->alias);
+ solv_free(cinfo->path);
+ solv_free(cinfo->metalink);
+ solv_free(cinfo->mirrorlist);
+ solv_free(cinfo->baseurl);
+ for (j = 0; j < cinfo->ncomponents; j++)
+ solv_free(cinfo->components[j]);
+ solv_free(cinfo->components);
+ }
+ solv_free(repoinfos);
+#if defined(SUSE) || defined(FEDORA)
+ yum_substitute((Pool *)0, 0); /* free data */
+#endif
+}
+
+struct repoinfo *
+read_repoinfos(Pool *pool, int *nrepoinfosp)
+{
+ struct repoinfo *repoinfos = 0;
+#if defined(SUSE) || defined(FEDORA)
+ repoinfos = read_repoinfos_yum(pool, nrepoinfosp);
+#endif
+#if defined(MANDRIVA) || defined(MAGEIA)
+ repoinfos = read_repoinfos_urpmi(pool, nrepoinfosp);
+#endif
+#if defined(DEBIAN)
+ repoinfos = read_repoinfos_debian(pool, nrepoinfosp);
+#endif
+ return repoinfos;
+}
+
+int
+read_installed_repo(struct repoinfo *cinfo, Pool *pool)
+{
+ int r = 1;
+ cinfo->type = TYPE_INSTALLED;
+ cinfo->repo = repo_create(pool, "@System");
+ cinfo->repo->appdata = cinfo;
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ r = read_installed_rpm(cinfo);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+ r = read_installed_debian(cinfo);
+#endif
+ pool_set_installed(pool, cinfo->repo);
+ return r;
+}
+
+int
+is_cmdline_package(const char *filename)
+{
+ int l = strlen(filename);
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ if (l > 4 && !strcmp(filename + l - 4, ".rpm"))
+ return 1;
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+ if (l > 4 && !strcmp(filename + l - 4, ".deb"))
+ return 1;
+#endif
+ return 0;
+}
+
+Id
+add_cmdline_package(Repo *repo, const char *filename)
+{
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ return repo_add_rpm(repo, filename, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+ return repo_add_deb(repo, filename, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
+#endif
+ return 0;
+}
+
+void
+commit_transactionelement(Pool *pool, Id type, Id p, FILE *fp)
+{
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ commit_transactionelement_rpm(pool, type, p, fp);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+ commit_transactionelement_debian(pool, type, p, fp);
+#endif
+}
+
+void
+add_ext_keys(Repodata *data, Id handle, const char *ext)
+{
+ static Id langtags[] = {
+ SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
+ SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
+ SOLVABLE_EULA, REPOKEY_TYPE_STR,
+ SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
+ SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
+ SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
+ 0, 0
+ };
+ if (!strcmp(ext, "DL"))
+ {
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
+ }
+ else if (!strcmp(ext, "FL"))
+ {
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
+ }
+ else if (!strcmp(ext, "DU"))
+ {
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
+ }
+ else
+ {
+ Pool *pool = data->repo->pool;
+ int i;
+ for (i = 0; langtags[i]; i += 2)
+ {
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, langtags[i], ext, 1));
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, langtags[i + 1]);
+ }
+ }
+}
+
+int
+load_stub(Pool *pool, Repodata *data, void *dp)
+{
+ struct repoinfo *cinfo = data->repo->appdata;
+ switch (cinfo->type)
+ {
+#ifdef ENABLE_SUSEREPO
+ case TYPE_SUSETAGS:
+ return susetags_load_ext(data->repo, data);
+#endif
+#ifdef ENABLE_RPMMD
+ case TYPE_RPMMD:
+ return repomd_load_ext(data->repo, data);
+#endif
+#ifdef ENABLE_MDKREPO
+ case TYPE_MDK:
+ return mdk_load_ext(data->repo, data);
+#endif
+ default:
+ /* debian does not have any ext data yet */
+ return 0;
+ }
+}
+
+void
+read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
+{
+ Repo *repo;
+ int i;
+ Pool *sigpool = 0;
+
+ for (i = 0; i < nrepoinfos; i++)
+ {
+ struct repoinfo *cinfo = repoinfos + i;
+ if (!cinfo->enabled)
+ continue;
+
+ repo = repo_create(pool, cinfo->alias);
+ cinfo->repo = repo;
+ repo->appdata = cinfo;
+ repo->priority = 99 - cinfo->priority;
+
+ if ((!cinfo->autorefresh || cinfo->metadata_expire) && usecachedrepo(cinfo, 0, 0))
+ {
+ printf("repo '%s':", cinfo->alias);
+ printf(" cached\n");
+ continue;
+ }
+
+ switch (cinfo->type)
+ {
+#ifdef ENABLE_RPMMD
+ case TYPE_RPMMD:
+ repomd_load(cinfo, &sigpool);
+ break;
+#endif
+#ifdef ENABLE_SUSEREPO
+ case TYPE_SUSETAGS:
+ susetags_load(cinfo, &sigpool);
+ break;
+#endif
+#ifdef ENABLE_DEBIAN
+ case TYPE_DEBIAN:
+ debian_load(cinfo, &sigpool);
+ break;
+#endif
+#ifdef ENABLE_MDKREPO
+ case TYPE_MDK:
+ mdk_load(cinfo, &sigpool);
+ break;
+#endif
+ default:
+ printf("unsupported repo '%s': skipped\n", cinfo->alias);
+ repo_free(repo, 1);
+ cinfo->repo = 0;
+ break;
+ }
+ }
+ if (sigpool)
+ pool_free(sigpool);
+}
+
--- /dev/null
+struct repoinfo {
+ Repo *repo;
+
+ int type;
+ char *alias;
+ char *name;
+ int enabled;
+ int autorefresh;
+ char *baseurl;
+ char *metalink;
+ char *mirrorlist;
+ char *path;
+ int pkgs_gpgcheck;
+ int repo_gpgcheck;
+ int priority;
+ int keeppackages;
+ int metadata_expire;
+ char **components;
+ int ncomponents;
+ int cookieset;
+ unsigned char cookie[32];
+ int extcookieset;
+ unsigned char extcookie[32];
+ int incomplete;
+};
+
+#define TYPE_UNKNOWN 0
+#define TYPE_SUSETAGS 1
+#define TYPE_RPMMD 2
+#define TYPE_PLAINDIR 3
+#define TYPE_DEBIAN 4
+#define TYPE_MDK 5
+
+#define TYPE_INSTALLED 16
+#define TYPE_CMDLINE 17
+
+#define METADATA_EXPIRE (60 * 15)
+
+extern void sort_repoinfos(struct repoinfo *repoinfos, int nrepoinfos);
+extern void free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos);
+extern void read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos);
+extern struct repoinfo *read_repoinfos(Pool *pool, int *nrepoinfosp);
+
+extern int read_installed_repo(struct repoinfo *cinfo, Pool *pool);
+
+extern int is_cmdline_package(const char *filename);
+extern Id add_cmdline_package(Repo *repo, const char *filename);
+
+extern void commit_transactionelement(Pool *pool, Id type, Id p, FILE *fp);
+
+extern void add_ext_keys(Repodata *data, Id handle, const char *ext);
+extern int load_stub(Pool *pool, Repodata *data, void *dp);
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_solv.h"
+#include "repo_write.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#define COOKIE_IDENT "1.1"
+
+#define SOLVCACHE_PATH "/var/cache/solv"
+
+static char *userhome;
+
+void
+set_userhome()
+{
+ userhome = getenv("HOME");
+ if (userhome && userhome[0] != '/')
+ userhome = 0;
+}
+
+void
+calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out)
+{
+ char buf[4096];
+ Chksum *h = solv_chksum_create(chktype);
+ int l;
+
+ solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
+ while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
+ solv_chksum_add(h, buf, l);
+ rewind(fp);
+ solv_chksum_free(h, out);
+}
+
+void
+calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out)
+{
+ Chksum *h = solv_chksum_create(chktype);
+ solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
+ if (cookie)
+ solv_chksum_add(h, cookie, 32);
+ solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
+ solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
+ solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
+ solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
+ solv_chksum_free(h, out);
+}
+
+char *
+calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc)
+{
+ char *q, *p;
+ int l;
+ if (!forcesystemloc && userhome && getuid())
+ p = pool_tmpjoin(repo->pool, userhome, "/.solvcache/", 0);
+ else
+ p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", 0);
+ l = strlen(p);
+ p = pool_tmpappend(repo->pool, p, repo->name, 0);
+ if (repoext)
+ {
+ p = pool_tmpappend(repo->pool, p, "_", repoext);
+ p = pool_tmpappend(repo->pool, p, ".solvx", 0);
+ }
+ else
+ p = pool_tmpappend(repo->pool, p, ".solv", 0);
+ q = p + l;
+ if (*q == '.')
+ *q = '_';
+ for (; *q; q++)
+ if (*q == '/')
+ *q = '_';
+ return p;
+}
+
+int
+usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark)
+{
+ Repo *repo = cinfo->repo;
+ FILE *fp;
+ unsigned char *cookie = repoext ? cinfo->extcookie : (cinfo->cookieset ? cinfo->cookie : 0);
+ unsigned char mycookie[32];
+ unsigned char myextcookie[32];
+ int flags;
+ int forcesystemloc;
+
+ if (repoext && !cinfo->extcookieset)
+ return 0; /* huh? */
+ forcesystemloc = mark & 2 ? 0 : 1;
+ if (mark < 2 && userhome && getuid())
+ {
+ /* first try home location */
+ int res = usecachedrepo(cinfo, repoext, mark | 2);
+ if (res)
+ return res;
+ }
+ mark &= 1;
+ if (!(fp = fopen(calc_cachepath(repo, repoext, forcesystemloc), "r")))
+ return 0;
+ if (!repoext && !cinfo->cookieset && cinfo->autorefresh && cinfo->metadata_expire != -1)
+ {
+ struct stat stb; /* no cookie set yet, check cache expiry time */
+ if (fstat(fileno(fp), &stb) || time(0) - stb.st_mtime >= cinfo->metadata_expire)
+ {
+ fclose(fp);
+ return 0;
+ }
+ }
+ if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
+ {
+ fclose(fp);
+ return 0;
+ }
+ if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)) != 0)
+ {
+ fclose(fp);
+ return 0;
+ }
+ if (cinfo->type != TYPE_INSTALLED && !repoext)
+ {
+ if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
+ {
+ fclose(fp);
+ return 0;
+ }
+ }
+ rewind(fp);
+
+ flags = 0;
+ if (repoext)
+ {
+ flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+ if (strcmp(repoext, "DL") != 0)
+ flags |= REPO_LOCALPOOL; /* no local pool for DL so that we can compare IDs */
+ }
+ if (repo_add_solv(repo, fp, flags))
+ {
+ fclose(fp);
+ return 0;
+ }
+ if (cinfo->type != TYPE_INSTALLED && !repoext)
+ {
+ memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
+ cinfo->cookieset = 1;
+ memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
+ cinfo->extcookieset = 1;
+ }
+ if (mark)
+ futimens(fileno(fp), 0); /* try to set modification time */
+ fclose(fp);
+ return 1;
+}
+
+static void
+switchtowritten(struct repoinfo *cinfo, const char *repoext, Repodata *repodata, char *tmpl)
+{
+ Repo *repo = cinfo->repo;
+ FILE *fp;
+ int i;
+
+ if (!repoext && repodata)
+ return; /* rewrite case, don't bother for the added fileprovides */
+ for (i = repo->start; i < repo->end; i++)
+ if (repo->pool->solvables[i].repo != repo)
+ break;
+ if (i < repo->end)
+ return; /* not a simple block */
+ /* switch to just saved repo to activate paging and save memory */
+ fp = fopen(tmpl, "r");
+ if (!fp)
+ return;
+ if (!repoext)
+ {
+ /* main repo */
+ repo_empty(repo, 1);
+ if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
+ {
+ /* oops, no way to recover from here */
+ fprintf(stderr, "internal error\n");
+ exit(1);
+ }
+ }
+ else
+ {
+ int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+ /* make sure repodata contains complete repo */
+ /* (this is how repodata_write saves it) */
+ repodata_extend_block(repodata, repo->start, repo->end - repo->start);
+ repodata->state = REPODATA_LOADING;
+ if (strcmp(repoext, "DL") != 0)
+ flags |= REPO_LOCALPOOL;
+ repo_add_solv(repo, fp, flags);
+ repodata->state = REPODATA_AVAILABLE; /* in case the load failed */
+ }
+ fclose(fp);
+}
+
+void
+writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata)
+{
+ Repo *repo = cinfo->repo;
+ FILE *fp;
+ int fd;
+ char *tmpl, *cachedir;
+
+ if (cinfo->incomplete || (repoext && !cinfo->extcookieset) || (!repoext && !cinfo->cookieset))
+ return;
+ cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
+ if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
+ printf("[created %s]\n", cachedir);
+ /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
+ tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
+ fd = mkstemp(tmpl);
+ if (fd < 0)
+ {
+ free(tmpl);
+ return;
+ }
+ fchmod(fd, 0444);
+ if (!(fp = fdopen(fd, "w")))
+ {
+ close(fd);
+ unlink(tmpl);
+ free(tmpl);
+ return;
+ }
+
+ if (!repodata)
+ repo_write(repo, fp);
+ else if (repoext)
+ repodata_write(repodata, fp);
+ else
+ {
+ int oldnrepodata = repo->nrepodata;
+ repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata; /* XXX: do this right */
+ repo_write(repo, fp);
+ repo->nrepodata = oldnrepodata;
+ }
+
+ if (!repoext && cinfo->type != TYPE_INSTALLED)
+ {
+ if (!cinfo->extcookieset)
+ {
+ /* create the ext cookie and append it */
+ /* we just need some unique ID */
+ struct stat stb;
+ if (fstat(fileno(fp), &stb))
+ memset(&stb, 0, sizeof(stb));
+ calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->cookie, cinfo->extcookie);
+ cinfo->extcookieset = 1;
+ }
+ if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
+ {
+ fclose(fp);
+ unlink(tmpl);
+ free(tmpl);
+ return;
+ }
+ }
+ /* append our cookie describing the metadata state */
+ if (fwrite(repoext ? cinfo->extcookie : cinfo->cookie, 32, 1, fp) != 1)
+ {
+ fclose(fp);
+ unlink(tmpl);
+ free(tmpl);
+ return;
+ }
+ if (fclose(fp))
+ {
+ unlink(tmpl);
+ free(tmpl);
+ return;
+ }
+
+ switchtowritten(cinfo, repoext, repodata, tmpl);
+
+ if (!rename(tmpl, calc_cachepath(repo, repoext, 0)))
+ unlink(tmpl);
+ free(tmpl);
+}
+
--- /dev/null
+
+struct repoinfo;
+struct stat;
+
+extern void set_userhome(void);
+extern char *calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc);
+extern void calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out);
+extern void calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out);
+
+extern int usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark);
+extern void writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata);
+
--- /dev/null
+#ifdef DEBIAN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_debian.h"
+
+
+
+struct repoinfo *
+read_repoinfos_debian(Pool *pool, int *nrepoinfosp)
+{
+ FILE *fp;
+ char buf[4096];
+ char buf2[4096];
+ int l;
+ char *kp, *url, *distro;
+ struct repoinfo *repoinfos = 0, *cinfo;
+ int nrepoinfos = 0;
+ DIR *dir = 0;
+ struct dirent *ent;
+
+ fp = fopen("/etc/apt/sources.list", "r");
+ while (1)
+ {
+ if (!fp)
+ {
+ if (!dir)
+ {
+ dir = opendir("/etc/apt/sources.list.d");
+ if (!dir)
+ break;
+ }
+ if ((ent = readdir(dir)) == 0)
+ {
+ closedir(dir);
+ break;
+ }
+ if (ent->d_name[0] == '.')
+ continue;
+ l = strlen(ent->d_name);
+ if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
+ continue;
+ snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
+ if (!(fp = fopen(buf, "r")))
+ continue;
+ }
+ while(fgets(buf2, sizeof(buf2), fp))
+ {
+ l = strlen(buf2);
+ if (l == 0)
+ continue;
+ while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
+ buf2[--l] = 0;
+ kp = buf2;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp || *kp == '#')
+ continue;
+ if (strncmp(kp, "deb", 3) != 0)
+ continue;
+ kp += 3;
+ if (*kp != ' ' && *kp != '\t')
+ continue;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp)
+ continue;
+ url = kp;
+ while (*kp && *kp != ' ' && *kp != '\t')
+ kp++;
+ if (*kp)
+ *kp++ = 0;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp)
+ continue;
+ distro = kp;
+ while (*kp && *kp != ' ' && *kp != '\t')
+ kp++;
+ if (*kp)
+ *kp++ = 0;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp)
+ continue;
+ repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+ cinfo = repoinfos + nrepoinfos++;
+ memset(cinfo, 0, sizeof(*cinfo));
+ cinfo->baseurl = strdup(url);
+ cinfo->alias = solv_dupjoin(url, "/", distro);
+ cinfo->name = strdup(distro);
+ cinfo->type = TYPE_DEBIAN;
+ cinfo->enabled = 1;
+ cinfo->autorefresh = 1;
+ cinfo->repo_gpgcheck = 1;
+ cinfo->metadata_expire = METADATA_EXPIRE;
+ while (*kp)
+ {
+ char *compo;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp)
+ break;
+ compo = kp;
+ while (*kp && *kp != ' ' && *kp != '\t')
+ kp++;
+ if (*kp)
+ *kp++ = 0;
+ cinfo->components = solv_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
+ cinfo->components[cinfo->ncomponents++] = strdup(compo);
+ }
+ }
+ fclose(fp);
+ fp = 0;
+ }
+ *nrepoinfosp = nrepoinfos;
+ return repoinfos;
+}
+
+#endif
--- /dev/null
+extern struct repoinfo *read_repoinfos_debian(Pool *pool, int *nrepoinfosp);
--- /dev/null
+#if defined(MANDRIVA) || defined(MAGEIA)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_urpmi.h"
+
+
+#define URPMI_CFG "/etc/urpmi/urpmi.cfg"
+
+
+struct repoinfo *
+read_repoinfos_urpmi(Pool *pool, int *nrepoinfosp)
+{
+ char buf[4096], *bp, *arg;
+ FILE *fp;
+ int l, insect = 0;
+ struct repoinfo *cinfo = 0;
+ struct repoinfo *repoinfos = 0;
+ int nrepoinfos = 0;
+
+ if ((fp = fopen(URPMI_CFG, "r")) == 0)
+ {
+ *nrepoinfosp = 0;
+ return 0;
+ }
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ l = strlen(buf);
+ while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
+ buf[--l] = 0;
+ bp = buf;
+ while (l && (*bp == ' ' || *bp == '\t'))
+ {
+ l--;
+ bp++;
+ }
+ if (!l || *bp == '#')
+ continue;
+ if (!insect && bp[l - 1] == '{')
+ {
+ insect++;
+ bp[--l] = 0;
+ if (l > 0)
+ {
+ while (l && (bp[l - 1] == ' ' || bp[l - 1] == '\t'))
+ bp[--l] = 0;
+ }
+ if (l)
+ {
+ char *bbp = bp, *bbp2 = bp;
+ /* unescape */
+ while (*bbp)
+ {
+ if (*bbp == '\\' && bbp[1])
+ bbp++;
+ *bbp2++ = *bbp++;
+ }
+ *bbp2 = 0;
+ repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+ cinfo = repoinfos + nrepoinfos++;
+ memset(cinfo, 0, sizeof(*cinfo));
+ cinfo->alias = strdup(bp);
+ cinfo->type = TYPE_MDK;
+ cinfo->autorefresh = 1;
+ cinfo->priority = 99;
+ cinfo->enabled = 1;
+ cinfo->metadata_expire = METADATA_EXPIRE;
+ }
+ continue;
+ }
+ if (insect && *bp == '}')
+ {
+ insect--;
+ cinfo = 0;
+ continue;
+ }
+ if (!insect || !cinfo)
+ continue;
+ if ((arg = strchr(bp, ':')) != 0)
+ {
+ *arg++ = 0;
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+ if (!*arg)
+ arg = 0;
+ }
+ if (strcmp(bp, "ignore") == 0)
+ cinfo->enabled = 0;
+ if (strcmp(bp, "mirrorlist") == 0)
+ cinfo->mirrorlist = solv_strdup(arg);
+ if (strcmp(bp, "with-dir") == 0)
+ cinfo->path = solv_strdup(arg);
+ }
+ fclose(fp);
+ *nrepoinfosp = nrepoinfos;
+ return repoinfos;
+}
+
+#endif
--- /dev/null
+extern struct repoinfo *read_repoinfos_urpmi(Pool *pool, int *nrepoinfosp);
+
--- /dev/null
+#if defined(SUSE) || defined(FEDORA)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_yum.h"
+
+
+#ifdef FEDORA
+# define REPOINFO_PATH "/etc/yum.repos.d"
+#endif
+#ifdef SUSE
+# define REPOINFO_PATH "/etc/zypp/repos.d"
+#endif
+
+char *
+yum_substitute(Pool *pool, char *line)
+{
+ char *p, *p2;
+ static char *releaseevr;
+ static char *basearch;
+
+ if (!line)
+ {
+ solv_free(releaseevr);
+ releaseevr = 0;
+ solv_free(basearch);
+ basearch = 0;
+ return 0;
+ }
+ p = line;
+ while ((p2 = strchr(p, '$')) != 0)
+ {
+ if (!strncmp(p2, "$releasever", 11))
+ {
+ if (!releaseevr)
+ {
+ void *rpmstate;
+ Queue q;
+
+ queue_init(&q);
+ rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
+ rpm_installedrpmdbids(rpmstate, "Providename", "redhat-release", &q);
+ if (q.count)
+ {
+ void *handle;
+ char *p;
+ handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
+ releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
+ if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
+ *p = 0;
+ }
+ rpm_state_free(rpmstate);
+ queue_free(&q);
+ if (!releaseevr)
+ {
+ fprintf(stderr, "no installed package provides 'redhat-release', cannot determine $releasever\n");
+ exit(1);
+ }
+ }
+ *p2 = 0;
+ p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
+ p2 = p + (p2 - line);
+ line = p;
+ p = p2 + strlen(releaseevr);
+ continue;
+ }
+ if (!strncmp(p2, "$basearch", 9))
+ {
+ if (!basearch)
+ {
+ struct utsname un;
+ if (uname(&un))
+ {
+ perror("uname");
+ exit(1);
+ }
+ basearch = strdup(un.machine);
+ if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
+ basearch[1] = '3';
+ }
+ *p2 = 0;
+ p = pool_tmpjoin(pool, line, basearch, p2 + 9);
+ p2 = p + (p2 - line);
+ line = p;
+ p = p2 + strlen(basearch);
+ continue;
+ }
+ p = p2 + 1;
+ }
+ return line;
+}
+
+struct repoinfo *
+read_repoinfos_yum(Pool *pool, int *nrepoinfosp)
+{
+ const char *reposdir = REPOINFO_PATH;
+ char buf[4096];
+ char buf2[4096], *kp, *vp, *kpe;
+ DIR *dir;
+ FILE *fp;
+ struct dirent *ent;
+ int l, rdlen;
+ struct repoinfo *repoinfos = 0, *cinfo;
+ int nrepoinfos = 0;
+
+ rdlen = strlen(reposdir);
+ dir = opendir(reposdir);
+ if (!dir)
+ {
+ *nrepoinfosp = 0;
+ return 0;
+ }
+ while ((ent = readdir(dir)) != 0)
+ {
+ if (ent->d_name[0] == '.')
+ continue;
+ l = strlen(ent->d_name);
+ if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
+ continue;
+ snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
+ if ((fp = fopen(buf, "r")) == 0)
+ {
+ perror(buf);
+ continue;
+ }
+ cinfo = 0;
+ while(fgets(buf2, sizeof(buf2), fp))
+ {
+ l = strlen(buf2);
+ if (l == 0)
+ continue;
+ while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
+ buf2[--l] = 0;
+ kp = buf2;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp || *kp == '#')
+ continue;
+ if (strchr(kp, '$'))
+ kp = yum_substitute(pool, kp);
+ if (*kp == '[')
+ {
+ vp = strrchr(kp, ']');
+ if (!vp)
+ continue;
+ *vp = 0;
+ repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+ cinfo = repoinfos + nrepoinfos++;
+ memset(cinfo, 0, sizeof(*cinfo));
+ cinfo->alias = strdup(kp + 1);
+ cinfo->type = TYPE_RPMMD;
+ cinfo->autorefresh = 1;
+ cinfo->priority = 99;
+#ifndef FEDORA
+ cinfo->repo_gpgcheck = 1;
+#endif
+ cinfo->metadata_expire = METADATA_EXPIRE;
+ continue;
+ }
+ if (!cinfo)
+ continue;
+ vp = strchr(kp, '=');
+ if (!vp)
+ continue;
+ for (kpe = vp - 1; kpe >= kp; kpe--)
+ if (*kpe != ' ' && *kpe != '\t')
+ break;
+ if (kpe == kp)
+ continue;
+ vp++;
+ while (*vp == ' ' || *vp == '\t')
+ vp++;
+ kpe[1] = 0;
+ if (!strcmp(kp, "name"))
+ cinfo->name = strdup(vp);
+ else if (!strcmp(kp, "enabled"))
+ cinfo->enabled = *vp == '0' ? 0 : 1;
+ else if (!strcmp(kp, "autorefresh"))
+ cinfo->autorefresh = *vp == '0' ? 0 : 1;
+ else if (!strcmp(kp, "gpgcheck"))
+ cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
+ else if (!strcmp(kp, "repo_gpgcheck"))
+ cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
+ else if (!strcmp(kp, "baseurl"))
+ cinfo->baseurl = strdup(vp);
+ else if (!strcmp(kp, "mirrorlist"))
+ {
+ if (strstr(vp, "metalink"))
+ cinfo->metalink = strdup(vp);
+ else
+ cinfo->mirrorlist = strdup(vp);
+ }
+ else if (!strcmp(kp, "path"))
+ {
+ if (vp && strcmp(vp, "/") != 0)
+ cinfo->path = strdup(vp);
+ }
+ else if (!strcmp(kp, "type"))
+ {
+ if (!strcmp(vp, "yast2"))
+ cinfo->type = TYPE_SUSETAGS;
+ else if (!strcmp(vp, "rpm-md"))
+ cinfo->type = TYPE_RPMMD;
+ else if (!strcmp(vp, "plaindir"))
+ cinfo->type = TYPE_PLAINDIR;
+ else if (!strcmp(vp, "mdk"))
+ cinfo->type = TYPE_MDK;
+ else
+ cinfo->type = TYPE_UNKNOWN;
+ }
+ else if (!strcmp(kp, "priority"))
+ cinfo->priority = atoi(vp);
+ else if (!strcmp(kp, "keeppackages"))
+ cinfo->keeppackages = *vp == '0' ? 0 : 1;
+ }
+ fclose(fp);
+ cinfo = 0;
+ }
+ closedir(dir);
+ *nrepoinfosp = nrepoinfos;
+ return repoinfos;
+}
+
+#endif
--- /dev/null
+extern char *yum_substitute(Pool *pool, char *line);
+extern struct repoinfo *read_repoinfos_yum(Pool *pool, int *nrepoinfosp);
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "solv_xfopen.h"
+
+#include "repoinfo.h"
+#include "mirror.h"
+#include "checksig.h"
+#include "repoinfo_download.h"
+
+static inline int
+opentmpfile()
+{
+ char tmpl[100];
+ int fd;
+
+ strcpy(tmpl, "/var/tmp/solvXXXXXX");
+ fd = mkstemp(tmpl);
+ if (fd < 0)
+ {
+ perror("mkstemp");
+ exit(1);
+ }
+ unlink(tmpl);
+ return fd;
+}
+
+int
+verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
+{
+ char buf[1024];
+ const unsigned char *sum;
+ Chksum *h;
+ int l;
+
+ h = solv_chksum_create(chksumtype);
+ if (!h)
+ {
+ printf("%s: unknown checksum type\n", file);
+ return 0;
+ }
+ while ((l = read(fd, buf, sizeof(buf))) > 0)
+ solv_chksum_add(h, buf, l);
+ lseek(fd, 0, SEEK_SET);
+ l = 0;
+ sum = solv_chksum_get(h, &l);
+ if (memcmp(sum, chksum, l))
+ {
+ printf("%s: checksum mismatch\n", file);
+ solv_chksum_free(h, 0);
+ return 0;
+ }
+ solv_chksum_free(h, 0);
+ return 1;
+}
+
+FILE *
+curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete)
+{
+ FILE *fp;
+ pid_t pid;
+ int fd;
+ int status;
+ char url[4096];
+ const char *baseurl = cinfo->baseurl;
+
+ if (!baseurl)
+ {
+ if (!cinfo->metalink && !cinfo->mirrorlist)
+ return 0;
+ if (file != cinfo->metalink && file != cinfo->mirrorlist)
+ {
+ unsigned char mlchksum[32];
+ Id mlchksumtype = 0;
+ fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
+ if (!fp)
+ return 0;
+ if (cinfo->metalink)
+ cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
+ else
+ cinfo->baseurl = findmirrorlisturl(fp);
+ fclose(fp);
+ if (!cinfo->baseurl)
+ return 0;
+#ifdef FEDORA
+ if (strchr(cinfo->baseurl, '$'))
+ {
+ char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
+ free(cinfo->baseurl);
+ cinfo->baseurl = strdup(b);
+ }
+#endif
+ if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
+ {
+ chksumtype = mlchksumtype;
+ chksum = mlchksum;
+ }
+ return curlfopen(cinfo, file, uncompress, chksum, chksumtype, markincomplete);
+ }
+ snprintf(url, sizeof(url), "%s", file);
+ }
+ else
+ {
+ const char *path = cinfo->path && strcmp(cinfo->path, "/") != 0 ? cinfo->path : "";
+ int l = strlen(baseurl);
+ int pl = strlen(path);
+ const char *sep = l && baseurl[l - 1] == '/' ? "" : "/";
+ const char *psep = pl && cinfo->path[pl - 1] == '/' ? "" : "/";
+ snprintf(url, sizeof(url), "%s%s%s%s%s", baseurl, sep, path, psep, file);
+ }
+ fd = opentmpfile();
+ // printf("url: %s\n", url);
+ if ((pid = fork()) == (pid_t)-1)
+ {
+ perror("fork");
+ exit(1);
+ }
+ if (pid == 0)
+ {
+ if (fd != 1)
+ {
+ dup2(fd, 1);
+ close(fd);
+ }
+ execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
+ perror("curl");
+ _exit(0);
+ }
+ status = 0;
+ while (waitpid(pid, &status, 0) != pid)
+ ;
+ if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
+ {
+ /* empty file */
+ close(fd);
+ return 0;
+ }
+ lseek(fd, 0, SEEK_SET);
+ if (status)
+ {
+ printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
+ if (markincomplete)
+ cinfo->incomplete = 1;
+ close(fd);
+ return 0;
+ }
+ if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
+ {
+ if (markincomplete)
+ cinfo->incomplete = 1;
+ close(fd);
+ return 0;
+ }
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (uncompress)
+ {
+ if (solv_xfopen_iscompressed(file) < 0)
+ {
+ printf("%s: unsupported compression\n", file);
+ if (markincomplete)
+ cinfo->incomplete = 1;
+ close(fd);
+ return 0;
+ }
+ fp = solv_xfopen_fd(file, fd, "r");
+ }
+ else
+ fp = fdopen(fd, "r");
+ if (!fp)
+ close(fd);
+ return fp;
+}
+
+FILE *
+downloadpackage(Solvable *s, const char *loc)
+{
+ const unsigned char *chksum;
+ Id chksumtype;
+ struct repoinfo *cinfo = s->repo->appdata;
+
+#ifdef ENABLE_SUSEREPO
+ if (cinfo->type == TYPE_SUSETAGS)
+ {
+ const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
+ loc = pool_tmpjoin(s->repo->pool, datadir ? datadir : "suse", "/", loc);
+ }
+#endif
+ chksumtype = 0;
+ chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
+ return curlfopen(cinfo, loc, 0, chksum, chksumtype, 0);
+}
+
+int
+downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool)
+{
+ FILE *sigfp;
+ sigfp = curlfopen(cinfo, sigurl, 0, 0, 0, 0);
+ if (!sigfp)
+ {
+ printf(" unsigned, skipped\n");
+ return 0;
+ }
+ if (!*sigpool)
+ *sigpool = read_sigs();
+ if (!checksig(*sigpool, fp, sigfp))
+ {
+ printf(" checksig failed, skipped\n");
+ fclose(sigfp);
+ return 0;
+ }
+ fclose(sigfp);
+ return 1;
+}
+
--- /dev/null
+int verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype);
+
+FILE *curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete);
+
+FILE *downloadpackage(Solvable *s, const char *loc);
+int downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool);
+
--- /dev/null
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_deb.h"
+#include "transaction.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_system_debian.h"
+
+static void
+rundpkg(const char *arg, const char *name, int dupfd3, const char *rootdir)
+{
+ pid_t pid;
+ int status;
+
+ if ((pid = fork()) == (pid_t)-1)
+ {
+ perror("fork");
+ exit(1);
+ }
+ if (pid == 0)
+ {
+ if (!rootdir)
+ rootdir = "/";
+ if (dupfd3 != -1 && dupfd3 != 3)
+ {
+ dup2(dupfd3, 3);
+ close(dupfd3);
+ }
+ if (dupfd3 != -1)
+ fcntl(3, F_SETFD, 0); /* clear CLOEXEC */
+ if (strcmp(arg, "--install") == 0)
+ execlp("dpkg", "dpkg", "--install", "--root", rootdir, "--force", "all", name, (char *)0);
+ else
+ execlp("dpkg", "dpkg", "--remove", "--root", rootdir, "--force", "all", name, (char *)0);
+ perror("dpkg");
+ _exit(0);
+ }
+ while (waitpid(pid, &status, 0) != pid)
+ ;
+ if (status)
+ {
+ printf("dpkg failed\n");
+ exit(1);
+ }
+}
+
+int
+read_installed_debian(struct repoinfo *cinfo)
+{
+ struct stat stb;
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+
+ memset(&stb, 0, sizeof(stb));
+ printf("dpgk database:");
+ if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/dpkg/status"), &stb))
+ memset(&stb, 0, sizeof(stb));
+ calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, 0, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 0))
+ {
+ printf(" cached\n");
+ return 1;
+ }
+ if (repo_add_debdb(repo, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
+ return 0;
+ }
+ repo_internalize(repo);
+ writecachedrepo(cinfo, 0, 0);
+ return 1;
+}
+
+void
+commit_transactionelement_debian(Pool *pool, Id type, Id p, FILE *fp)
+{
+ Solvable *s = pool_id2solvable(pool, p);
+ const char *rootdir = pool_get_rootdir(pool);
+
+ switch(type)
+ {
+ case SOLVER_TRANSACTION_ERASE:
+ rundpkg("--remove", pool_id2str(pool, s->name), 0, rootdir);
+ break;
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ rewind(fp);
+ lseek(fileno(fp), 0, SEEK_SET);
+ rundpkg("--install", "/dev/fd/3", fileno(fp), rootdir);
+ break;
+ default:
+ break;
+ }
+}
+
+#endif
--- /dev/null
+int read_installed_debian(struct repoinfo *cinfo);
+void commit_transactionelement_debian(Pool *pool, Id type, Id p, FILE *fp);
--- /dev/null
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+#if defined(ENABLE_SUSEREPO) && defined(SUSE)
+#include "repo_products.h"
+#endif
+#if defined(ENABLE_APPDATA)
+#include "repo_appdata.h"
+#endif
+#include "transaction.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_system_rpm.h"
+
+#ifdef SUSE
+# define PRODUCTS_PATH "/etc/products.d"
+#endif
+#ifdef ENABLE_APPDATA
+# define APPDATA_PATH "/usr/share/appdata"
+#endif
+
+static void
+runrpm(const char *arg, const char *name, int dupfd3, const char *rootdir)
+{
+ pid_t pid;
+ int status;
+
+ if ((pid = fork()) == (pid_t)-1)
+ {
+ perror("fork");
+ exit(1);
+ }
+ if (pid == 0)
+ {
+ if (!rootdir)
+ rootdir = "/";
+ if (dupfd3 != -1 && dupfd3 != 3)
+ {
+ dup2(dupfd3, 3);
+ close(dupfd3);
+ }
+ if (dupfd3 != -1)
+ fcntl(3, F_SETFD, 0); /* clear CLOEXEC */
+ if (strcmp(arg, "-e") == 0)
+ execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
+ else
+ execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
+ perror("rpm");
+ _exit(0);
+ }
+ while (waitpid(pid, &status, 0) != pid)
+ ;
+ if (status)
+ {
+ printf("rpm failed\n");
+ exit(1);
+ }
+}
+
+int
+read_installed_rpm(struct repoinfo *cinfo)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ FILE *ofp = 0;
+ struct stat stb;
+
+ memset(&stb, 0, sizeof(stb));
+ printf("rpm database:");
+ if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/rpm/Packages"), &stb))
+ memset(&stb, 0, sizeof(stb));
+ calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, 0, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 0))
+ {
+ printf(" cached\n");
+ return 1;
+ }
+ printf(" reading\n");
+#if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
+ if (repo_add_products(repo, PRODUCTS_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "product reading failed: %s\n", pool_errstr(pool));
+ return 0;
+ }
+#endif
+#if defined(ENABLE_APPDATA) && defined(APPDATA_PATH)
+ if (repo_add_appdata_dir(repo, APPDATA_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "appdata reading failed: %s\n", pool_errstr(pool));
+ return 0;
+ }
+#endif
+ ofp = fopen(calc_cachepath(repo, 0, 0), "r");
+ if (repo_add_rpmdb_reffp(repo, ofp, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
+ return 0;
+ }
+ if (ofp)
+ fclose(ofp);
+ repo_internalize(repo);
+ writecachedrepo(cinfo, 0, 0);
+ return 1;
+}
+
+void
+commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp)
+{
+ Solvable *s = pool_id2solvable(pool, p);
+ const char *rootdir = pool_get_rootdir(pool);
+ const char *evr, *evrp, *nvra;
+
+ switch(type)
+ {
+ case SOLVER_TRANSACTION_ERASE:
+ if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
+ break;
+ /* strip epoch from evr */
+ evr = evrp = pool_id2str(pool, s->evr);
+ while (*evrp >= '0' && *evrp <= '9')
+ evrp++;
+ if (evrp > evr && evrp[0] == ':' && evrp[1])
+ evr = evrp + 1;
+ nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
+ nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
+ runrpm("-e", nvra, -1, rootdir); /* too bad that --querybynumber doesn't work */
+ break;
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ rewind(fp);
+ lseek(fileno(fp), 0, SEEK_SET);
+ runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp), rootdir);
+ break;
+ default:
+ break;
+ }
+}
+
+#endif
--- /dev/null
+int read_installed_rpm(struct repoinfo *cinfo);
+void commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp);
--- /dev/null
+#ifdef ENABLE_DEBIAN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_deb.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_debian.h"
+
+static const char *
+debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
+{
+ char buf[4096];
+ Id chksumtype;
+ unsigned char *chksum;
+ Id curchksumtype;
+ int l, compl;
+ char *ch, *fn, *bp;
+ char *filename;
+ static char *basearch;
+ char *binarydir;
+ int lbinarydir;
+
+ if (!basearch)
+ {
+ struct utsname un;
+ if (uname(&un))
+ {
+ perror("uname");
+ exit(1);
+ }
+ basearch = strdup(un.machine);
+ if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
+ basearch[1] = '3';
+ }
+ binarydir = solv_dupjoin("binary-", basearch, "/");
+ lbinarydir = strlen(binarydir);
+ compl = strlen(comp);
+ rewind(fp);
+ curchksumtype = 0;
+ filename = 0;
+ chksum = solv_malloc(32);
+ chksumtype = 0;
+ while(fgets(buf, sizeof(buf), fp))
+ {
+ l = strlen(buf);
+ if (l == 0)
+ continue;
+ while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
+ buf[--l] = 0;
+ if (!strncasecmp(buf, "MD5Sum:", 7))
+ {
+ curchksumtype = REPOKEY_TYPE_MD5;
+ continue;
+ }
+ if (!strncasecmp(buf, "SHA1:", 5))
+ {
+ curchksumtype = REPOKEY_TYPE_SHA1;
+ continue;
+ }
+ if (!strncasecmp(buf, "SHA256:", 7))
+ {
+ curchksumtype = REPOKEY_TYPE_SHA256;
+ continue;
+ }
+ if (!curchksumtype)
+ continue;
+ bp = buf;
+ if (*bp++ != ' ')
+ {
+ curchksumtype = 0;
+ continue;
+ }
+ ch = bp;
+ while (*bp && *bp != ' ' && *bp != '\t')
+ bp++;
+ if (!*bp)
+ continue;
+ *bp++ = 0;
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ while (*bp && *bp != ' ' && *bp != '\t')
+ bp++;
+ if (!*bp)
+ continue;
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ fn = bp;
+ if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
+ continue;
+ bp += compl + 1;
+ if (strncmp(bp, binarydir, lbinarydir))
+ continue;
+ bp += lbinarydir;
+ if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
+ {
+ unsigned char curchksum[32];
+ int curl;
+ if (filename && !strcmp(bp, "Packages"))
+ continue;
+ curl = solv_chksum_len(curchksumtype);
+ if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
+ continue;
+ if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
+ continue;
+ solv_free(filename);
+ filename = strdup(fn);
+ chksumtype = curchksumtype;
+ memcpy(chksum, curchksum, curl);
+ }
+ }
+ free(binarydir);
+ if (filename)
+ {
+ fn = solv_dupjoin("/", filename, 0);
+ solv_free(filename);
+ filename = solv_dupjoin("dists/", cinfo->name, fn);
+ solv_free(fn);
+ }
+ if (!chksumtype)
+ chksum = solv_free(chksum);
+ *chksump = chksum;
+ *chksumtypep = chksumtype;
+ return filename;
+}
+
+int
+debian_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ const char *filename;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ FILE *fp, *fpr;
+ int j;
+
+ printf("debian repo '%s':", cinfo->alias);
+ fflush(stdout);
+ filename = solv_dupjoin("dists/", cinfo->name, "/Release");
+ if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
+ {
+ printf(" no Release file\n");
+ free((char *)filename);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ solv_free((char *)filename);
+ if (cinfo->repo_gpgcheck)
+ {
+ filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
+ if (!downloadchecksig(cinfo, fpr, filename, sigpoolp))
+ {
+ fclose(fpr);
+ solv_free((char *)filename);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ solv_free((char *)filename);
+ }
+ calc_cookie_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 1))
+ {
+ printf(" cached\n");
+ fclose(fpr);
+ return 1;
+ }
+ printf(" fetching\n");
+ for (j = 0; j < cinfo->ncomponents; j++)
+ {
+ if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
+ {
+ printf("[component %s not found]\n", cinfo->components[j]);
+ continue;
+ }
+ if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_debpackages(repo, fp, 0))
+ {
+ printf("component %s: %s\n", cinfo->components[j], pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+ solv_free((char *)filechksum);
+ solv_free((char *)filename);
+ }
+ fclose(fpr);
+ writecachedrepo(cinfo, 0, 0);
+ return 1;
+}
+
+#endif
--- /dev/null
+extern int debian_load(struct repoinfo *cinfo, Pool **sigpoolp);
+
--- /dev/null
+#ifdef ENABLE_MDKREPO
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_mdk.h"
+#include "solv_xfopen.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_mdk.h"
+
+static int
+mdk_find(const char *md5sums, const char *what, unsigned char *chksum)
+{
+ const char *sp, *ep;
+ int wl = strlen(what);
+ for (sp = md5sums; (ep = strchr(sp, '\n')) != 0; sp = ep + 1)
+ {
+ int l = ep - sp;
+ if (l <= 34)
+ continue;
+ if (sp[32] != ' ' || sp[33] != ' ')
+ continue;
+ if (wl != l - 34 || strncmp(what, sp + 34, wl) != 0)
+ continue;
+ if (solv_hex2bin(&sp, chksum, 16) != 16)
+ continue;
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+slurp(FILE *fp)
+{
+ int l, ll;
+ char *buf = 0;
+ int bufl = 0;
+
+ for (l = 0; ; l += ll)
+ {
+ if (bufl - l < 4096)
+ {
+ bufl += 4096;
+ buf = solv_realloc(buf, bufl);
+ }
+ ll = fread(buf + l, 1, bufl - l, fp);
+ if (ll < 0)
+ {
+ buf = solv_free(buf);
+ l = 0;
+ break;
+ }
+ if (ll == 0)
+ {
+ buf[l] = 0;
+ break;
+ }
+ }
+ return buf;
+}
+
+int
+mdk_load_ext(Repo *repo, Repodata *data)
+{
+ struct repoinfo *cinfo = repo->appdata;
+ const char *type, *ext, *filename;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ int r = 0;
+ FILE *fp;
+
+ type = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
+ if (strcmp(type, "filelists") != 0)
+ return 0;
+ ext = "FL";
+ printf("[%s:%s", repo->name, ext);
+ if (usecachedrepo(cinfo, ext, 0))
+ {
+ printf(" cached]\n"); fflush(stdout);
+ return 1;
+ }
+ printf(" fetching]\n"); fflush(stdout);
+ filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
+ filechksumtype = 0;
+ filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
+ if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 0)) == 0)
+ return 0;
+ r = repo_add_mdk_info(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+ fclose(fp);
+ if (r)
+ {
+ printf("%s\n", pool_errstr(repo->pool));
+ return 0;
+ }
+ writecachedrepo(cinfo, ext, data);
+ return 1;
+}
+
+int
+mdk_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ Repodata *data;
+ const char *compression;
+ FILE *fp, *cfp;
+ char *md5sums;
+ unsigned char probe[5];
+ unsigned char md5[16];
+
+ printf("mdk repo '%s':", cinfo->alias);
+ fflush(stdout);
+ if ((fp = curlfopen(cinfo, "media_info/MD5SUM", 0, 0, 0, 0)) == 0)
+ {
+ printf(" no media_info/MD5SUM file\n");
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 1))
+ {
+ printf(" cached\n");
+ fclose(fp);
+ return 1;
+ }
+ md5sums = slurp(fp);
+ fclose(fp);
+ printf(" fetching\n");
+ if (!mdk_find(md5sums, "synthesis.hdlist.cz", md5))
+ {
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ if ((fp = curlfopen(cinfo, "media_info/synthesis.hdlist.cz", 0, md5, REPOKEY_TYPE_MD5, 1)) == 0)
+ {
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ /* probe compression */
+ if (fread(probe, 5, 1, fp) != 1)
+ {
+ fclose(fp);
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ if (probe[0] == 0xfd && memcmp(probe + 1, "7zXZ", 4) == 0)
+ compression = "synthesis.hdlist.xz";
+ else
+ compression = "synthesis.hdlist.gz";
+ lseek(fileno(fp), 0, SEEK_SET);
+ cfp = solv_xfopen_fd(compression, dup(fileno(fp)), "r");
+ fclose(fp);
+ fp = cfp;
+ if (!fp)
+ {
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ if (repo_add_mdk(repo, fp, REPO_NO_INTERNALIZE))
+ {
+ printf("synthesis.hdlist.cz: %s\n", pool_errstr(pool));
+ fclose(fp);
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ fclose(fp);
+ /* add info, could do this on demand, but always having the summary is nice */
+ if (mdk_find(md5sums, "info.xml.lzma", md5))
+ {
+ if ((fp = curlfopen(cinfo, "media_info/info.xml.lzma", 1, md5, REPOKEY_TYPE_MD5, 1)) != 0)
+ {
+ if (repo_add_mdk_info(repo, fp, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
+ {
+ printf("info.xml.lzma: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+ }
+ repo_internalize(repo);
+ data = repo_add_repodata(repo, 0);
+ /* setup on-demand loading of filelist data */
+ if (mdk_find(md5sums, "files.xml.lzma", md5))
+ {
+ Id handle = repodata_new_handle(data);
+ /* we mis-use the repomd ids here... need something generic in the future */
+ repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, "filelists");
+ repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, "media_info/files.xml.lzma");
+ repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, REPOKEY_TYPE_MD5, md5);
+ add_ext_keys(data, handle, "FL");
+ repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+ }
+ solv_free(md5sums);
+ repodata_internalize(data);
+ writecachedrepo(cinfo, 0, 0);
+ repodata_create_stubs(repo_last_repodata(repo));
+ return 1;
+}
+
+#endif
--- /dev/null
+extern int mdk_load(struct repoinfo *cinfo, Pool **sigpoolp);
+extern int mdk_load_ext(Repo *repo, Repodata *data);
+
--- /dev/null
+#ifdef ENABLE_RPMMD
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_rpmmd.h"
+#include "repo_deltainfoxml.h"
+#include "repo_updateinfoxml.h"
+#include "repo_repomdxml.h"
+#ifdef ENABLE_APPDATA
+#include "repo_appdata.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_rpmmd.h"
+
+
+
+static const char *
+repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
+{
+ Pool *pool = repo->pool;
+ Dataiterator di;
+ const char *filename;
+
+ filename = 0;
+ *chksump = 0;
+ *chksumtypep = 0;
+ dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
+ dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
+ if (dataiterator_step(&di))
+ {
+ dataiterator_setpos_parent(&di);
+ filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
+ *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
+ }
+ dataiterator_free(&di);
+ if (filename && !*chksumtypep)
+ {
+ printf("no %s file checksum!\n", what);
+ filename = 0;
+ }
+ return filename;
+}
+
+static void
+repomd_add_ext(Repo *repo, Repodata *data, const char *what, const char *ext)
+{
+ Id chksumtype, handle;
+ const unsigned char *chksum;
+ const char *filename;
+
+ filename = repomd_find(repo, what, &chksum, &chksumtype);
+ if (!filename && !strcmp(what, "deltainfo"))
+ filename = repomd_find(repo, "prestodelta", &chksum, &chksumtype);
+ if (!filename)
+ return;
+ handle = repodata_new_handle(data);
+ repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
+ repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
+ repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
+ add_ext_keys(data, handle, ext);
+ repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+}
+
+int
+repomd_load_ext(Repo *repo, Repodata *data)
+{
+ const char *filename, *repomdtype;
+ char ext[3];
+ FILE *fp;
+ struct repoinfo *cinfo;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ int r = 0;
+
+ cinfo = repo->appdata;
+ repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
+ if (!repomdtype)
+ return 0;
+ if (!strcmp(repomdtype, "filelists"))
+ strcpy(ext, "FL");
+ else if (!strcmp(repomdtype, "deltainfo"))
+ strcpy(ext, "DL");
+ else
+ return 0;
+ printf("[%s:%s", repo->name, ext);
+ if (usecachedrepo(cinfo, ext, 0))
+ {
+ printf(" cached]\n"); fflush(stdout);
+ return 1;
+ }
+ printf(" fetching]\n"); fflush(stdout);
+ filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
+ filechksumtype = 0;
+ filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
+ if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 0)) == 0)
+ return 0;
+ if (!strcmp(ext, "FL"))
+ r = repo_add_rpmmd(repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+ else if (!strcmp(ext, "DL"))
+ r = repo_add_deltainfoxml(repo, fp, REPO_USE_LOADING);
+ fclose(fp);
+ if (r)
+ {
+ printf("%s\n", pool_errstr(repo->pool));
+ return 0;
+ }
+ if (cinfo->extcookieset)
+ writecachedrepo(cinfo, ext, data);
+ return 1;
+}
+
+int
+repomd_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ Repodata *data;
+ const char *filename;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ FILE *fp;
+
+ printf("rpmmd repo '%s':", cinfo->alias);
+ fflush(stdout);
+ if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
+ {
+ printf(" no repomd.xml file\n");
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 1))
+ {
+ printf(" cached\n");
+ fclose(fp);
+ return 1;
+ }
+ if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "repodata/repomd.xml.asc", sigpoolp))
+ {
+ fclose(fp);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ if (repo_add_repomdxml(repo, fp, 0))
+ {
+ printf("repomd.xml: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+ printf(" fetching\n");
+ filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
+ if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_rpmmd(repo, fp, 0, 0))
+ {
+ printf("primary: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+ if (cinfo->incomplete)
+ return 0; /* hopeless */
+
+ filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
+ if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_updateinfoxml(repo, fp, 0))
+ {
+ printf("updateinfo: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+
+#ifdef ENABLE_APPDATA
+ filename = repomd_find(repo, "appdata", &filechksum, &filechksumtype);
+ if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_appdata(repo, fp, 0))
+ {
+ printf("appdata: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+#endif
+ data = repo_add_repodata(repo, 0);
+ repomd_add_ext(repo, data, "deltainfo", "DL");
+ repomd_add_ext(repo, data, "filelists", "FL");
+ repodata_internalize(data);
+ writecachedrepo(cinfo, 0, 0);
+ repodata_create_stubs(repo_last_repodata(repo));
+ return 1;
+}
+
+#endif
--- /dev/null
+extern int repomd_load_ext(Repo *repo, Repodata *data);
+extern int repomd_load(struct repoinfo *cinfo, Pool **sigpoolp);
--- /dev/null
+#ifdef ENABLE_SUSEREPO
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_content.h"
+#include "repo_susetags.h"
+#ifdef ENABLE_APPDATA
+#include "repo_appdata.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_susetags.h"
+
+/* susetags helpers */
+
+static const char *
+susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
+{
+ Pool *pool = repo->pool;
+ Dataiterator di;
+ const char *filename;
+
+ filename = 0;
+ *chksump = 0;
+ *chksumtypep = 0;
+ dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
+ dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
+ if (dataiterator_step(&di))
+ {
+ dataiterator_setpos_parent(&di);
+ *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
+ filename = what;
+ }
+ dataiterator_free(&di);
+ if (filename && !*chksumtypep)
+ {
+ printf("no %s file checksum!\n", what);
+ filename = 0;
+ }
+ return filename;
+}
+
+void
+susetags_add_ext(Repo *repo, Repodata *data)
+{
+ Pool *pool = repo->pool;
+ Dataiterator di;
+ char ext[3];
+ Id handle, filechksumtype;
+ const unsigned char *filechksum;
+
+ dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
+ dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
+ while (dataiterator_step(&di))
+ {
+ if (strncmp(di.kv.str, "packages.", 9) != 0)
+ continue;
+ if (!strcmp(di.kv.str + 9, "gz"))
+ continue;
+ if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
+ continue;
+ ext[0] = di.kv.str[9];
+ ext[1] = di.kv.str[10];
+ ext[2] = 0;
+ if (!strcmp(ext, "en"))
+ continue;
+ if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
+ continue;
+ handle = repodata_new_handle(data);
+ repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
+ if (filechksumtype)
+ repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
+ add_ext_keys(data, handle, ext);
+ repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+ }
+ dataiterator_free(&di);
+}
+
+int
+susetags_load_ext(Repo *repo, Repodata *data)
+{
+ const char *filename, *descrdir;
+ Id defvendor;
+ char ext[3];
+ FILE *fp;
+ struct repoinfo *cinfo;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ int flags;
+
+ cinfo = repo->appdata;
+ filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
+ if (!filename)
+ return 0;
+ /* susetags load */
+ ext[0] = filename[9];
+ ext[1] = filename[10];
+ ext[2] = 0;
+ printf("[%s:%s", repo->name, ext);
+ if (usecachedrepo(cinfo, ext, 0))
+ {
+ printf(" cached]\n"); fflush(stdout);
+ return 1;
+ }
+ printf(" fetching]\n"); fflush(stdout);
+ defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
+ descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
+ if (!descrdir)
+ descrdir = "suse/setup/descr";
+ filechksumtype = 0;
+ filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
+ if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 0)) == 0)
+ return 0;
+ flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+ if (strcmp(ext, "DL") != 0)
+ flags |= REPO_LOCALPOOL;
+ if (repo_add_susetags(repo, fp, defvendor, ext, flags))
+ {
+ fclose(fp);
+ printf("%s\n", pool_errstr(repo->pool));
+ return 0;
+ }
+ fclose(fp);
+ writecachedrepo(cinfo, ext, data);
+ return 1;
+}
+
+int
+susetags_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ Repodata *data;
+ const char *filename;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ FILE *fp;
+ const char *descrdir;
+ int defvendor;
+
+ printf("susetags repo '%s':", cinfo->alias);
+ fflush(stdout);
+ descrdir = 0;
+ defvendor = 0;
+ if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
+ {
+ printf(" no content file\n");
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 1))
+ {
+ printf(" cached\n");
+ fclose(fp);
+ return 1;
+ }
+ if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "content.asc", sigpoolp))
+ {
+ fclose(fp);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ if (repo_add_content(repo, fp, 0))
+ {
+ printf("content: %s\n", pool_errstr(pool));
+ fclose(fp);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ fclose(fp);
+ defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
+ descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
+ if (!descrdir)
+ descrdir = "suse/setup/descr";
+ filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
+ if (!filename)
+ filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
+ if (!filename)
+ {
+ printf(" no packages file entry, skipped\n");
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ printf(" fetching\n");
+ if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) == 0)
+ {
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES))
+ {
+ printf("packages: %s\n", pool_errstr(pool));
+ fclose(fp);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ fclose(fp);
+ /* add default language */
+ filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
+ if (!filename)
+ filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
+ if (filename)
+ {
+ if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
+ {
+ printf("packages.en: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+ }
+ filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
+ if (filename)
+ {
+ if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ char pbuf[256];
+ while (fgets(pbuf, sizeof(pbuf), fp))
+ {
+ int l = strlen(pbuf);
+ FILE *fp2;
+ if (l && pbuf[l - 1] == '\n')
+ pbuf[--l] = 0;
+ if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
+ continue;
+ filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
+ if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE))
+ {
+ printf("%s: %s\n", pbuf, pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp2);
+ }
+ }
+ fclose(fp);
+ }
+ }
+#ifdef ENABLE_APPDATA
+ filename = susetags_find(repo, "appdata.xml.gz", &filechksum, &filechksumtype);
+ if (!filename)
+ filename = susetags_find(repo, "appdata.xml", &filechksum, &filechksumtype);
+ if (filename && (fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_appdata(repo, fp, 0))
+ {
+ printf("appdata: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+#endif
+ repo_internalize(repo);
+ data = repo_add_repodata(repo, 0);
+ susetags_add_ext(repo, data);
+ repodata_internalize(data);
+ writecachedrepo(cinfo, 0, 0);
+ repodata_create_stubs(repo_last_repodata(repo));
+ return 1;
+}
+
+#endif
--- /dev/null
+extern int susetags_load_ext(Repo *repo, Repodata *data);
+extern int susetags_load(struct repoinfo *cinfo, Pool **sigpoolp);
--- /dev/null
+/*
+ * Copyright (c) 2009-2015, SUSE LLC.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* solv, a little software installer demoing the sat solver library */
+
+/* things it does:
+ * - understands globs for package names / dependencies
+ * - understands .arch suffix
+ * - installation of commandline packages
+ * - repository data caching
+ * - on demand loading of secondary repository data
+ * - gpg and checksum verification
+ * - file conflicts
+ * - deltarpm support
+ * - fastestmirror implementation
+ *
+ * things available in the library but missing from solv:
+ * - vendor policy loading
+ * - multi version handling
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "poolarch.h"
+#include "evr.h"
+#include "selection.h"
+#include "repo.h"
+#include "solver.h"
+#include "solverdebug.h"
+#include "transaction.h"
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+
+#if defined(ENABLE_RPMDB)
+#include "fileprovides.h"
+#include "fileconflicts.h"
+#include "deltarpm.h"
+#endif
+#if defined(SUSE) || defined(FEDORA)
+#include "patchjobs.h"
+#endif
+
+void
+setarch(Pool *pool)
+{
+ struct utsname un;
+ if (uname(&un))
+ {
+ perror("uname");
+ exit(1);
+ }
+ pool_setarch(pool, un.machine);
+}
+
+
+int
+yesno(const char *str)
+{
+ char inbuf[128], *ip;
+
+ for (;;)
+ {
+ printf("%s", str);
+ fflush(stdout);
+ *inbuf = 0;
+ if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
+ {
+ printf("Abort.\n");
+ exit(1);
+ }
+ while (*ip == ' ' || *ip == '\t')
+ ip++;
+ if (*ip == 'q')
+ {
+ printf("Abort.\n");
+ exit(1);
+ }
+ if (*ip == 'y' || *ip == 'n')
+ return *ip == 'y' ? 1 : 0;
+ }
+}
+
+#ifdef SUSE
+static Id
+nscallback(Pool *pool, void *data, Id name, Id evr)
+{
+#if 0
+ if (name == NAMESPACE_LANGUAGE)
+ {
+ if (!strcmp(pool_id2str(pool, evr), "ja"))
+ return 1;
+ if (!strcmp(pool_id2str(pool, evr), "de"))
+ return 1;
+ if (!strcmp(pool_id2str(pool, evr), "en"))
+ return 1;
+ if (!strcmp(pool_id2str(pool, evr), "en_US"))
+ return 1;
+ }
+#endif
+ return 0;
+}
+#endif
+
+#ifdef SUSE
+static void
+add_autopackages(Pool *pool)
+{
+ int i;
+ Repo *repo;
+ FOR_REPOS(i, repo)
+ repo_add_autopattern(repo, 0);
+}
+#endif
+
+#ifdef SUSE
+static void
+showdiskusagechanges(Transaction *trans)
+{
+ DUChanges duc[4];
+ int i;
+
+ /* XXX: use mountpoints here */
+ memset(duc, 0, sizeof(duc));
+ duc[0].path = "/";
+ duc[1].path = "/usr/share/man";
+ duc[2].path = "/sbin";
+ duc[3].path = "/etc";
+ transaction_calc_duchanges(trans, duc, 4);
+ for (i = 0; i < 4; i++)
+ printf("duchanges %s: %d K %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
+}
+#endif
+
+static Id
+find_repo(const char *name, Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
+{
+ const char *rp;
+ int i;
+
+ for (rp = name; *rp; rp++)
+ if (*rp <= '0' || *rp >= '9')
+ break;
+ if (!*rp)
+ {
+ /* repo specified by number */
+ int rnum = atoi(name);
+ for (i = 0; i < nrepoinfos; i++)
+ {
+ struct repoinfo *cinfo = repoinfos + i;
+ if (!cinfo->enabled || !cinfo->repo)
+ continue;
+ if (--rnum == 0)
+ return cinfo->repo->repoid;
+ }
+ }
+ else
+ {
+ /* repo specified by alias */
+ Repo *repo;
+ FOR_REPOS(i, repo)
+ {
+ if (!strcasecmp(name, repo->name))
+ return repo->repoid;
+ }
+ }
+ return 0;
+}
+
+
+#define MODE_LIST 0
+#define MODE_INSTALL 1
+#define MODE_ERASE 2
+#define MODE_UPDATE 3
+#define MODE_DISTUPGRADE 4
+#define MODE_VERIFY 5
+#define MODE_PATCH 6
+#define MODE_INFO 7
+#define MODE_REPOLIST 8
+#define MODE_SEARCH 9
+
+void
+usage(int r)
+{
+ fprintf(stderr, "Usage: solv COMMAND <select>\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " dist-upgrade: replace installed packages with\n");
+ fprintf(stderr, " versions from the repositories\n");
+ fprintf(stderr, " erase: erase installed packages\n");
+ fprintf(stderr, " info: display package information\n");
+ fprintf(stderr, " install: install packages\n");
+ fprintf(stderr, " list: list packages\n");
+ fprintf(stderr, " repos: list enabled repositories\n");
+ fprintf(stderr, " search: search name/summary/description\n");
+ fprintf(stderr, " update: update installed packages\n");
+ fprintf(stderr, " verify: check dependencies of installed packages\n");
+#if defined(SUSE) || defined(FEDORA)
+ fprintf(stderr, " patch: install newest maintenance updates\n");
+#endif
+ fprintf(stderr, "\n");
+ exit(r);
+}
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool;
+ Repo *commandlinerepo = 0;
+ Id *commandlinepkgs = 0;
+ Id p;
+ struct repoinfo *repoinfos, installedrepoinfo;
+ int nrepoinfos = 0;
+ int mainmode = 0, mode = 0;
+ int i, newpkgs;
+ Queue job, checkq;
+ Solver *solv = 0;
+ Transaction *trans;
+ FILE **newpkgsfps;
+ Queue repofilter;
+ Queue kindfilter;
+ Queue archfilter;
+ int archfilter_src = 0;
+ int cleandeps = 0;
+ int forcebest = 0;
+ char *rootdir = 0;
+ char *keyname = 0;
+ int debuglevel = 0;
+
+ argc--;
+ argv++;
+ while (argc && !strcmp(argv[0], "-d"))
+ {
+ debuglevel++;
+ argc--;
+ argv++;
+ }
+ if (!argv[0])
+ usage(1);
+ if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
+ {
+ mainmode = MODE_INSTALL;
+ mode = SOLVER_INSTALL;
+ }
+#if defined(SUSE) || defined(FEDORA)
+ else if (!strcmp(argv[0], "patch"))
+ {
+ mainmode = MODE_PATCH;
+ mode = SOLVER_INSTALL;
+ }
+#endif
+ else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
+ {
+ mainmode = MODE_ERASE;
+ mode = SOLVER_ERASE;
+ }
+ else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
+ {
+ mainmode = MODE_LIST;
+ mode = 0;
+ }
+ else if (!strcmp(argv[0], "info"))
+ {
+ mainmode = MODE_INFO;
+ mode = 0;
+ }
+ else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
+ {
+ mainmode = MODE_SEARCH;
+ mode = 0;
+ }
+ else if (!strcmp(argv[0], "verify"))
+ {
+ mainmode = MODE_VERIFY;
+ mode = SOLVER_VERIFY;
+ }
+ else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
+ {
+ mainmode = MODE_UPDATE;
+ mode = SOLVER_UPDATE;
+ }
+ else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
+ {
+ mainmode = MODE_DISTUPGRADE;
+ mode = SOLVER_DISTUPGRADE;
+ }
+ else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
+ {
+ mainmode = MODE_REPOLIST;
+ mode = 0;
+ }
+ else
+ usage(1);
+
+ for (;;)
+ {
+ if (argc > 2 && !strcmp(argv[1], "--root"))
+ {
+ rootdir = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc > 1 && !strcmp(argv[1], "--clean"))
+ {
+ cleandeps = 1;
+ argc--;
+ argv++;
+ }
+ else if (argc > 1 && !strcmp(argv[1], "--best"))
+ {
+ forcebest = 1;
+ argc--;
+ argv++;
+ }
+ if (argc > 2 && !strcmp(argv[1], "--keyname"))
+ {
+ keyname = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+ else
+ break;
+ }
+
+ set_userhome();
+ pool = pool_create();
+ pool_set_rootdir(pool, rootdir);
+
+#if 0
+ {
+ const char *langs[] = {"de_DE", "de", "en"};
+ pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
+ }
+#endif
+
+ pool_setloadcallback(pool, load_stub, 0);
+#ifdef SUSE
+ pool->nscallback = nscallback;
+#endif
+ if (debuglevel)
+ pool_setdebuglevel(pool, debuglevel);
+ setarch(pool);
+ pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
+ repoinfos = read_repoinfos(pool, &nrepoinfos);
+ sort_repoinfos(repoinfos, nrepoinfos);
+
+ if (mainmode == MODE_REPOLIST)
+ {
+ int j = 1;
+ for (i = 0; i < nrepoinfos; i++)
+ {
+ struct repoinfo *cinfo = repoinfos + i;
+ if (!cinfo->enabled)
+ continue;
+ printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
+ }
+ exit(0);
+ }
+ memset(&installedrepoinfo, 0, sizeof(installedrepoinfo));
+ if (!read_installed_repo(&installedrepoinfo, pool))
+ exit(1);
+ read_repos(pool, repoinfos, nrepoinfos);
+
+ /* setup filters */
+ queue_init(&repofilter);
+ queue_init(&kindfilter);
+ queue_init(&archfilter);
+ while (argc > 1)
+ {
+ if (!strcmp(argv[1], "-i"))
+ {
+ queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
+ argc--;
+ argv++;
+ }
+ else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
+ {
+ Id repoid = find_repo(argv[2], pool, repoinfos, nrepoinfos);
+ if (!repoid)
+ {
+ fprintf(stderr, "%s: no such repo\n", argv[2]);
+ exit(1);
+ }
+ /* SETVENDOR is actually wrong but useful */
+ queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc > 2 && !strcmp(argv[1], "--arch"))
+ {
+ if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
+ archfilter_src = 1;
+ queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
+ {
+ const char *kind = argv[2];
+ if (!strcmp(kind, "srcpackage"))
+ {
+ /* hey! should use --arch! */
+ queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
+ archfilter_src = 1;
+ argc -= 2;
+ argv += 2;
+ continue;
+ }
+ if (!strcmp(kind, "package"))
+ kind = "";
+ if (!strcmp(kind, "all"))
+ queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
+ else
+ queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
+ argc -= 2;
+ argv += 2;
+ }
+ else
+ break;
+ }
+
+ if (mainmode == MODE_SEARCH)
+ {
+ Queue sel, q;
+ Dataiterator di;
+ if (argc != 2)
+ usage(1);
+ pool_createwhatprovides(pool);
+ queue_init(&sel);
+ dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
+ dataiterator_set_keyname(&di, SOLVABLE_NAME);
+ dataiterator_set_search(&di, 0, 0);
+ while (dataiterator_step(&di))
+ queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+ dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
+ dataiterator_set_search(&di, 0, 0);
+ while (dataiterator_step(&di))
+ queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+ dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
+ dataiterator_set_search(&di, 0, 0);
+ while (dataiterator_step(&di))
+ queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+ dataiterator_free(&di);
+ if (repofilter.count)
+ selection_filter(pool, &sel, &repofilter);
+
+ queue_init(&q);
+ selection_solvables(pool, &sel, &q);
+ queue_free(&sel);
+ for (i = 0; i < q.count; i++)
+ {
+ Solvable *s = pool_id2solvable(pool, q.elements[i]);
+ printf(" - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
+ }
+ queue_free(&q);
+ exit(0);
+ }
+
+ /* process command line packages */
+ if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
+ {
+ for (i = 1; i < argc; i++)
+ {
+ if (!is_cmdline_package((const char *)argv[i]))
+ continue;
+ if (access(argv[i], R_OK))
+ {
+ perror(argv[i]);
+ exit(1);
+ }
+ if (!commandlinepkgs)
+ commandlinepkgs = solv_calloc(argc, sizeof(Id));
+ if (!commandlinerepo)
+ commandlinerepo = repo_create(pool, "@commandline");
+ p = add_cmdline_package(commandlinerepo, (const char *)argv[i]);
+ if (!p)
+ {
+ fprintf(stderr, "could not add '%s'\n", argv[i]);
+ exit(1);
+ }
+ commandlinepkgs[i] = p;
+ }
+ if (commandlinerepo)
+ repo_internalize(commandlinerepo);
+ }
+
+#if defined(ENABLE_RPMDB)
+ if (pool->disttype == DISTTYPE_RPM)
+ addfileprovides(pool);
+#endif
+#ifdef SUSE
+ add_autopackages(pool);
+#endif
+ pool_createwhatprovides(pool);
+
+ if (keyname)
+ keyname = solv_dupjoin("solvable:", keyname, 0);
+ queue_init(&job);
+ for (i = 1; i < argc; i++)
+ {
+ Queue job2;
+ int flags, rflags;
+
+ if (commandlinepkgs && commandlinepkgs[i])
+ {
+ queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
+ continue;
+ }
+ queue_init(&job2);
+ flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
+ flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
+ if (kindfilter.count)
+ flags |= SELECTION_SKIP_KIND;
+ if (mode == MODE_LIST || archfilter_src)
+ flags |= SELECTION_WITH_SOURCE;
+ if (argv[i][0] == '/')
+ flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
+ if (!keyname)
+ rflags = selection_make(pool, &job2, argv[i], flags);
+ else
+ rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
+ if (repofilter.count)
+ selection_filter(pool, &job2, &repofilter);
+ if (archfilter.count)
+ selection_filter(pool, &job2, &archfilter);
+ if (kindfilter.count)
+ selection_filter(pool, &job2, &kindfilter);
+ if (!job2.count)
+ {
+ flags |= SELECTION_NOCASE;
+ if (!keyname)
+ rflags = selection_make(pool, &job2, argv[i], flags);
+ else
+ rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
+ if (repofilter.count)
+ selection_filter(pool, &job2, &repofilter);
+ if (archfilter.count)
+ selection_filter(pool, &job2, &archfilter);
+ if (kindfilter.count)
+ selection_filter(pool, &job2, &kindfilter);
+ if (job2.count)
+ printf("[ignoring case for '%s']\n", argv[i]);
+ }
+ if (!job2.count)
+ {
+ fprintf(stderr, "nothing matches '%s'\n", argv[i]);
+ exit(1);
+ }
+ if (rflags & SELECTION_FILELIST)
+ printf("[using file list match for '%s']\n", argv[i]);
+ if (rflags & SELECTION_PROVIDES)
+ printf("[using capability match for '%s']\n", argv[i]);
+ queue_insertn(&job, job.count, job2.count, job2.elements);
+ queue_free(&job2);
+ }
+ keyname = solv_free(keyname);
+
+ if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
+ {
+ queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
+ if (repofilter.count)
+ selection_filter(pool, &job, &repofilter);
+ if (archfilter.count)
+ selection_filter(pool, &job, &archfilter);
+ if (kindfilter.count)
+ selection_filter(pool, &job, &kindfilter);
+ }
+ queue_free(&repofilter);
+ queue_free(&archfilter);
+ queue_free(&kindfilter);
+
+ if (!job.count && mainmode != MODE_PATCH)
+ {
+ printf("no package matched\n");
+ exit(1);
+ }
+
+ if (mainmode == MODE_LIST || mainmode == MODE_INFO)
+ {
+ /* list mode, no solver needed */
+ Queue q;
+ queue_init(&q);
+ for (i = 0; i < job.count; i += 2)
+ {
+ int j;
+ queue_empty(&q);
+ pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
+ for (j = 0; j < q.count; j++)
+ {
+ Solvable *s = pool_id2solvable(pool, q.elements[j]);
+ if (mainmode == MODE_INFO)
+ {
+ const char *str;
+ printf("Name: %s\n", pool_solvable2str(pool, s));
+ printf("Repo: %s\n", s->repo->name);
+ printf("Summary: %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
+ str = solvable_lookup_str(s, SOLVABLE_URL);
+ if (str)
+ printf("Url: %s\n", str);
+ str = solvable_lookup_str(s, SOLVABLE_LICENSE);
+ if (str)
+ printf("License: %s\n", str);
+ printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
+ printf("\n");
+ }
+ else
+ {
+#if 1
+ const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
+#else
+ const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
+#endif
+ printf(" - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
+ if (sum)
+ printf(" %s\n", sum);
+ }
+ }
+ }
+ queue_free(&q);
+ queue_free(&job);
+ pool_free(pool);
+ free_repoinfos(repoinfos, nrepoinfos);
+ solv_free(commandlinepkgs);
+ exit(0);
+ }
+
+#if defined(SUSE) || defined(FEDORA)
+ if (mainmode == MODE_PATCH)
+ add_patchjobs(pool, &job);
+#endif
+
+ // add mode
+ for (i = 0; i < job.count; i += 2)
+ {
+ job.elements[i] |= mode;
+ if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
+ job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
+ if (cleandeps)
+ job.elements[i] |= SOLVER_CLEANDEPS;
+ if (forcebest)
+ job.elements[i] |= SOLVER_FORCEBEST;
+ }
+
+ // multiversion test
+ // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
+ // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
+ // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
+#if 0
+ queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
+ queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
+#endif
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+rerunsolver:
+#endif
+ solv = solver_create(pool);
+ solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
+#ifdef FEDORA
+ solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
+#endif
+ if (mainmode == MODE_ERASE)
+ solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1); /* don't nag */
+ solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
+
+ for (;;)
+ {
+ Id problem, solution;
+ int pcnt, scnt;
+
+ if (!solver_solve(solv, &job))
+ break;
+ pcnt = solver_problem_count(solv);
+ printf("Found %d problems:\n", pcnt);
+ for (problem = 1; problem <= pcnt; problem++)
+ {
+ int take = 0;
+ printf("Problem %d/%d:\n", problem, pcnt);
+ solver_printprobleminfo(solv, problem);
+ printf("\n");
+ scnt = solver_solution_count(solv, problem);
+ for (solution = 1; solution <= scnt; solution++)
+ {
+ printf("Solution %d:\n", solution);
+ solver_printsolution(solv, problem, solution);
+ printf("\n");
+ }
+ for (;;)
+ {
+ char inbuf[128], *ip;
+ printf("Please choose a solution: ");
+ fflush(stdout);
+ *inbuf = 0;
+ if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
+ {
+ printf("Abort.\n");
+ exit(1);
+ }
+ while (*ip == ' ' || *ip == '\t')
+ ip++;
+ if (*ip >= '0' && *ip <= '9')
+ {
+ take = atoi(ip);
+ if (take >= 1 && take <= scnt)
+ break;
+ }
+ if (*ip == 's')
+ {
+ take = 0;
+ break;
+ }
+ if (*ip == 'q')
+ {
+ printf("Abort.\n");
+ exit(1);
+ }
+ }
+ if (!take)
+ continue;
+ solver_take_solution(solv, problem, take, &job);
+ }
+ }
+
+ trans = solver_create_transaction(solv);
+ if (!trans->steps.count)
+ {
+ printf("Nothing to do.\n");
+ transaction_free(trans);
+ solver_free(solv);
+ queue_free(&job);
+ pool_free(pool);
+ free_repoinfos(repoinfos, nrepoinfos);
+ solv_free(commandlinepkgs);
+ exit(1);
+ }
+
+ /* display transaction to the user and ask for confirmation */
+ printf("\n");
+ printf("Transaction summary:\n\n");
+ transaction_print(trans);
+#if defined(SUSE)
+ showdiskusagechanges(trans);
+#endif
+ printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
+ printf("\n");
+
+ if (!yesno("OK to continue (y/n)? "))
+ {
+ printf("Abort.\n");
+ transaction_free(trans);
+ solver_free(solv);
+ queue_free(&job);
+ pool_free(pool);
+ free_repoinfos(repoinfos, nrepoinfos);
+ solv_free(commandlinepkgs);
+ exit(1);
+ }
+
+ /* download all new packages */
+ queue_init(&checkq);
+ newpkgs = transaction_installedresult(trans, &checkq);
+ newpkgsfps = 0;
+ if (newpkgs)
+ {
+ int downloadsize = 0;
+ for (i = 0; i < newpkgs; i++)
+ {
+ Solvable *s;
+
+ p = checkq.elements[i];
+ s = pool_id2solvable(pool, p);
+ downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
+ }
+ printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
+ newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
+ for (i = 0; i < newpkgs; i++)
+ {
+ const char *loc;
+ Solvable *s;
+ struct repoinfo *cinfo;
+
+ p = checkq.elements[i];
+ s = pool_id2solvable(pool, p);
+ if (s->repo == commandlinerepo)
+ {
+ loc = solvable_lookup_location(s, 0);
+ if (!loc)
+ continue;
+ if (!(newpkgsfps[i] = fopen(loc, "r")))
+ {
+ perror(loc);
+ exit(1);
+ }
+ putchar('.');
+ continue;
+ }
+ cinfo = s->repo->appdata;
+ if (!cinfo || cinfo->type == TYPE_INSTALLED)
+ {
+ printf("%s: no repository information\n", s->repo->name);
+ exit(1);
+ }
+ loc = solvable_lookup_location(s, 0);
+ if (!loc)
+ continue; /* pseudo package? */
+#if defined(ENABLE_RPMDB)
+ if (pool->installed && pool->installed->nsolvables)
+ {
+ if ((newpkgsfps[i] = trydeltadownload(s, loc)) != 0)
+ {
+ putchar('d');
+ fflush(stdout);
+ continue; /* delta worked! */
+ }
+ }
+#endif
+ if ((newpkgsfps[i] = downloadpackage(s, loc)) == 0)
+ {
+ printf("\n%s: %s not found in repository\n", s->repo->name, loc);
+ exit(1);
+ }
+ putchar('.');
+ fflush(stdout);
+ }
+ putchar('\n');
+ }
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ /* check for file conflicts */
+ if (newpkgs)
+ {
+ Queue conflicts;
+ queue_init(&conflicts);
+ if (checkfileconflicts(pool, &checkq, newpkgs, newpkgsfps, &conflicts))
+ {
+ if (yesno("Re-run solver (y/n/q)? "))
+ {
+ for (i = 0; i < newpkgs; i++)
+ if (newpkgsfps[i])
+ fclose(newpkgsfps[i]);
+ newpkgsfps = solv_free(newpkgsfps);
+ solver_free(solv);
+ solv = 0;
+ pool_add_fileconflicts_deps(pool, &conflicts);
+ queue_free(&conflicts);
+ goto rerunsolver;
+ }
+ }
+ queue_free(&conflicts);
+ }
+#endif
+
+ /* and finally commit the transaction */
+ printf("Committing transaction:\n\n");
+ transaction_order(trans, 0);
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ int j;
+ FILE *fp;
+ Id type;
+
+ p = trans->steps.elements[i];
+ type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
+ switch(type)
+ {
+ case SOLVER_TRANSACTION_ERASE:
+ printf("erase %s\n", pool_solvid2str(pool, p));
+ commit_transactionelement(pool, type, p, 0);
+ break;
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ printf("install %s\n", pool_solvid2str(pool, p));
+ for (j = 0; j < newpkgs; j++)
+ if (checkq.elements[j] == p)
+ break;
+ fp = j < newpkgs ? newpkgsfps[j] : 0;
+ if (!fp)
+ continue;
+ commit_transactionelement(pool, type, p, fp);
+ fclose(fp);
+ newpkgsfps[j] = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; i < newpkgs; i++)
+ if (newpkgsfps[i])
+ fclose(newpkgsfps[i]);
+ solv_free(newpkgsfps);
+ queue_free(&checkq);
+ transaction_free(trans);
+ solver_free(solv);
+ queue_free(&job);
+ pool_free(pool);
+ free_repoinfos(repoinfos, nrepoinfos);
+ solv_free(commandlinepkgs);
+ exit(0);
+}
--- /dev/null
+#!/usr/bin/tclsh
+
+package require solv
+package require inifile
+package require fileutil
+
+set reposdir /etc/zypp/repos.d
+
+### some helpers
+
+proc fileno {file} {
+ if [regexp -- {^file(\d+)$} $file match fd] {
+ return $fd
+ }
+ error "file not open"
+}
+
+set ::globalarray_cnt 0
+
+proc globalarray {} {
+ set name "::globalarray_[incr ::globalarray_cnt]"
+ array set $name [list varName $name]
+ return $name
+}
+
+### generic repo handling (cache reading/writing)
+
+proc repo_calc_cookie_file {selfName filename} {
+ upvar $selfName self
+ set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
+ $chksum add "1.1"
+ $chksum add_stat $filename
+ return [$chksum raw]
+}
+
+proc repo_calc_cookie_fp {selfName fp} {
+ upvar $selfName self
+ set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
+ $chksum add "1.1"
+ $chksum add_fp $fp
+ return [$chksum raw]
+}
+
+proc repo_calc_cookie_ext {selfName f cookie} {
+ upvar $selfName self
+ set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
+ $chksum add "1.1"
+ $chksum add $cookie
+ $chksum add_fstat [$f fileno]
+ return [$chksum raw]
+}
+
+proc repo_cachepath {selfName {ext "-"}} {
+ upvar $selfName self
+ regsub {^\.} $self(name) _ path
+ if {$ext ne "-"} {
+ set path "${path}_$ext.solvx"
+ } else {
+ set path "${path}.solv"
+ }
+ regsub -all / $path _ path
+ return "/var/cache/solv/$path"
+}
+
+proc repo_generic_load {selfName pool} {
+ upvar $selfName self
+ set handle [ $pool add_repo $self(name) ]
+ set self(handle) $handle
+ $handle configure -priority [expr 99 - $self(priority)] -appdata $self(varName)
+ set dorefresh $self(autorefresh)
+ set metadata_expire $self(metadata_expire)
+ catch {
+ if {$metadata_expire == -1 || [clock seconds] - [file mtime [repo_cachepath self]] < $metadata_expire} {
+ set dorefresh 0
+ }
+ }
+ set self(cookie) {}
+ set self(extcookie) {}
+ if { !$dorefresh && [repo_usecachedrepo self] } {
+ puts "repo $self(name): cached"
+ return 1
+ }
+ return 0
+}
+
+proc repo_free_handle {selfName} {
+ upvar $selfName self
+ set handle $self(handle)
+ unset self(handle)
+ $handle free 1
+}
+
+proc repo_usecachedrepo {selfName {ext "-"} {mark 0}} {
+ upvar $selfName self
+ set repopath [repo_cachepath self $ext]
+ set code [catch {
+ set f [open $repopath "rb"]
+ seek $f -32 end
+ set fcookie [read $f 32]
+ set cookie [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
+ if {$cookie ne {} && $cookie ne $fcookie} {
+ close $f
+ return 0
+ }
+ set fextcookie {}
+ if {$ext eq "-" && $self(type) ne "system"} {
+ seek $f -64 end
+ set fextcookie [read $f 32]
+ }
+ seek $f 0 start
+ set ff [solv::xfopen_fd {} [fileno $f]]
+ close $f
+ set flags 0
+ if {$ext ne "-"} {
+ set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
+ if {$ext ne "DL"} {
+ set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
+ }
+ }
+ if {! [$self(handle) add_solv $ff $flags]} {
+ $ff close
+ return 0
+ }
+ $ff close
+ if {$self(type) ne "system" && $ext eq "-"} {
+ set self(cookie) $fcookie
+ set self(extcookie) $fextcookie
+ }
+ if {$mark} {
+ catch {
+ ::fileutil::touch -c -m -t [clock seconds] $repopath
+ }
+ }
+ return 1
+ } res]
+ return [expr {$code == 2 ? $res : 0}]
+}
+
+proc repo_writecachedrepo {selfName {ext "-"} {repodata "NULL"}} {
+ upvar $selfName self
+ if [info exists self(incomplete)] {
+ return
+ }
+ file mkdir "/var/cache/solv"
+ ::fileutil::tempdir "/var/cache/solv"
+ set tempfilename [::fileutil::tempfile ".newsolv-"]
+ ::fileutil::tempdirReset
+ set f [solv::xfopen $tempfilename "w+"]
+ file attributes $tempfilename -permissions 0444
+ if {$repodata eq {NULL}} {
+ $self(handle) write $f
+ } else {
+ $repodata write $f
+ }
+ $f flush
+ if {$self(type) ne "system" && $ext eq "-"} {
+ if {$self(extcookie) eq {}} {
+ set self(extcookie) [repo_calc_cookie_ext self $f $self(cookie)]
+ }
+ $f write $self(extcookie)
+ }
+ $f write [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
+ $f close
+ file rename -force -- $tempfilename [repo_cachepath self $ext]
+}
+
+proc repo_download {selfName file uncompress chksum {markincomplete 0}} {
+ upvar $selfName self
+ regsub {/$} $self(baseurl) {} url
+ set url "$url/$file"
+ set tempfilename [::fileutil::tempfile]
+ set f [open $tempfilename rb+]
+ file delete -- $tempfilename
+ if [catch {
+ exec -ignorestderr -- curl -f -s -L $url ">@$f"
+ }] {
+ seek $f 0 end
+ if {($chksum ne "" && $chksum ne "NULL") || [tell $f] != 0} {
+ puts "$file: download error"
+ }
+ close $f
+ return {NULL}
+ }
+ seek $f 0 start
+ if {$chksum ne "" && $chksum ne "NULL"} {
+ set fchksum [solv::new_Chksum [$chksum cget -type]]
+ if {$fchksum eq "" || $fchksum eq "NULL"} {
+ puts "$file: unknown checksum type"
+ if {$markincomplete} {
+ set self(incomplete) 1
+ }
+ close $f
+ return {NULL}
+ }
+ $fchksum add_fd [fileno $f]
+ if {[$fchksum != $chksum]} {
+ puts "$file: checksum mismatch"
+ if {$markincomplete} {
+ set self(incomplete) 1
+ }
+ close $f
+ return {NULL}
+ }
+ }
+ set ff [solv::xfopen_fd [expr {$uncompress ? $file : ""}] [fileno $f]]
+ close $f
+ return $ff
+}
+
+proc repo_generic_add_ext_keys {selfName ext repodata h} {
+ upvar $selfName self
+ if {$ext eq "DL"} {
+ $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOSITORY_DELTAINFO
+ $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_FLEXARRAY
+ } elseif {$ext eq "DU"} {
+ $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_DISKUSAGE
+ $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRNUMNUMARRAY
+ } elseif {$ext eq "FL"} {
+ $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_FILELIST
+ $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRSTRARRAY
+ }
+}
+
+### system
+
+proc repo_system_load {selfName pool} {
+ upvar $selfName self
+ set handle [ $pool add_repo $self(name) ]
+ set self(handle) $handle
+ $handle configure -appdata $self(varName)
+ $pool configure -installed $handle
+ puts -nonewline "rpm database: "
+ set self(cookie) [repo_calc_cookie_file self "/var/lib/rpm/Packages"]
+ if [repo_usecachedrepo self] {
+ puts "cached"
+ return 1
+ }
+ puts "reading"
+ set f [solv::xfopen [repo_cachepath self]]
+ $handle add_rpmdb_reffp $f $solv::Repo_REPO_REUSE_REPODATA
+ repo_writecachedrepo self
+}
+
+### repomd
+
+proc repo_repomd_add_ext {selfName repodata what ext} {
+ upvar $selfName self
+ set where [repo_repomd_find self $what]
+ if {$where eq {}} {
+ return
+ }
+ set h [$repodata new_handle]
+ $repodata set_poolstr $h $solv::REPOSITORY_REPOMD_TYPE $what
+ $repodata set_str $h $solv::REPOSITORY_REPOMD_LOCATION [lindex $where 0]
+ $repodata set_checksum $h $solv::REPOSITORY_REPOMD_CHECKSUM [lindex $where 1]
+ repo_generic_add_ext_keys self $ext $repodata $h
+ $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
+}
+
+proc repo_repomd_add_exts {selfName} {
+ upvar $selfName self
+ set repodata [$self(handle) add_repodata 0]
+ repo_repomd_add_ext self $repodata "filelists" "FL"
+ $repodata internalize
+}
+
+proc repo_repomd_find {selfName what} {
+ upvar $selfName self
+ set di [$self(handle) Dataiterator_meta $solv::REPOSITORY_REPOMD_TYPE $what $solv::Dataiterator_SEARCH_STRING]
+ $di prepend_keyname $solv::REPOSITORY_REPOMD
+ solv::iter d $di {
+ set dp [$d parentpos]
+ set filename [$dp lookup_str $solv::REPOSITORY_REPOMD_LOCATION]
+ set checksum [$dp lookup_checksum $solv::REPOSITORY_REPOMD_CHECKSUM]
+ if {$filename ne "" && $checksum eq "NULL"} {
+ puts "no $filename file checksum"
+ } elseif {$filename ne ""} {
+ return [list $filename $checksum]
+ }
+ }
+ return {}
+}
+
+proc repo_repomd_load {selfName pool} {
+ upvar $selfName self
+ if [repo_generic_load self $pool] {
+ return 1
+ }
+ puts -nonewline "rpmmd repo '$self(name)': "
+ set f [repo_download self {repodata/repomd.xml} 0 {}]
+ if {$f eq {NULL}} {
+ puts "no repomd.xml file, skipped"
+ repo_free_handle self
+ return 0
+ }
+ set self(cookie) [repo_calc_cookie_fp self $f]
+ if [repo_usecachedrepo self "-" 1] {
+ puts "cached"
+ return 1
+ }
+ set handle $self(handle)
+ $handle add_repomdxml $f
+ puts "fetching"
+ set primary [repo_repomd_find self primary]
+ if {$primary ne {}} {
+ set f [repo_download self [lindex $primary 0] 1 [lindex $primary 1] 1]
+ if {$f ne {NULL}} {
+ $handle add_rpmmd $f {}
+ $f close
+ }
+ if [info exists self(incomplete)] {
+ return 0
+ }
+ }
+ set updateinfo [repo_repomd_find self primary]
+ if {$updateinfo ne {}} {
+ set f [repo_download self [lindex $updateinfo 0] 1 [lindex $updateinfo 1] 1]
+ if {$f ne {NULL}} {
+ $handle add_updateinfoxml $f
+ $f close
+ }
+ }
+ repo_repomd_add_exts self
+ repo_writecachedrepo self
+ $self(handle) create_stubs
+ return 1
+}
+
+proc repo_repomd_packagespath {selfName} {
+ return ""
+}
+
+proc repo_repomd_load_ext {selfName repodata} {
+ upvar $selfName self
+ switch -- [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_TYPE] {
+ "deltainfo" {
+ set ext DL
+ }
+ "filelists" {
+ set ext FL
+ }
+ default {
+ return 0
+ }
+ }
+ puts -nonewline "\[$self(name):$ext: "
+ flush stdout
+ if [repo_usecachedrepo self $ext] {
+ puts "cached]"
+ return 1
+ }
+ puts "fetching]"
+ set handle $self(handle)
+ set filename [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_LOCATION]
+ set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::REPOSITORY_REPOMD_CHECKSUM]
+ set f [repo_download self $filename 1 $filechecksum]
+ if {$f eq {NULL}} {
+ return 0
+ }
+ if {$ext eq "FL"} {
+ $handle add_rpmmd $f "FL" [ expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES | $solv::Repo_REPO_LOCALPOOL]
+ }
+ $f close
+ repo_writecachedrepo self $ext $repodata
+ return 1
+}
+
+### susetags
+
+proc repo_susetags_add_ext {selfName repodata what ext} {
+ upvar $selfName self
+ set where [repo_susetags_find self $what]
+ if {$where eq {}} {
+ return
+ }
+ set h [$repodata new_handle]
+ $repodata set_str $h $solv::SUSETAGS_FILE_NAME [lindex $where 0]
+ $repodata set_checksum $h $solv::SUSETAGS_FILE_CHECKSUM [lindex $where 1]
+ repo_generic_add_ext_keys self $ext $repodata $h
+ $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
+}
+
+proc repo_susetags_add_exts {selfName} {
+ upvar $selfName self
+ set repodata [$self(handle) add_repodata 0]
+ repo_susetags_add_ext self $repodata "packages.FL" "FL"
+ repo_susetags_add_ext self $repodata "packages.FL.gz" "FL"
+ $repodata internalize
+}
+
+proc repo_susetags_find {selfName what} {
+ upvar $selfName self
+ set di [$self(handle) Dataiterator_meta $solv::SUSETAGS_FILE_NAME $what $solv::Dataiterator_SEARCH_STRING]
+ $di prepend_keyname $solv::SUSETAGS_FILE
+ solv::iter d $di {
+ set dp [$d parentpos]
+ set checksum [$dp lookup_checksum $solv::SUSETAGS_FILE_CHECKSUM]
+ return [list $what $checksum]
+ }
+ return {}
+}
+
+proc repo_susetags_load {selfName pool} {
+ upvar $selfName self
+ if [repo_generic_load self $pool] {
+ return 1
+ }
+ puts -nonewline "susetags repo '$self(name)': "
+ set f [repo_download self {content} 0 {}]
+ if {$f eq {NULL}} {
+ puts "no content file, skipped"
+ repo_free_handle self
+ return 0
+ }
+ set self(cookie) [repo_calc_cookie_fp self $f]
+ if [repo_usecachedrepo self "-" 1] {
+ puts "cached"
+ return 1
+ }
+ set handle $self(handle)
+ $handle add_content $f
+ puts "fetching"
+ set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
+ set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
+ if {$descrdir eq {NULL}} {
+ set descrdir "suse/setup/descr"
+ }
+ set packages [repo_susetags_find self "packages.gz"]
+ if {$packages eq {}} {
+ set packages [repo_susetags_find self "packages"]
+ }
+ if {$packages ne {}} {
+ set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
+ if {$f ne {NULL}} {
+ $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_SUSETAGS_RECORD_SHARES]
+ $f close
+ set packages [repo_susetags_find self "packages.en.gz"]
+ if {$packages eq {}} {
+ set packages [repo_susetags_find self "packages.en"]
+ }
+ if {$packages ne {}} {
+ set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
+ if {$f ne {NULL}} {
+ $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_REPO_REUSE_REPODATA | $solv::Repo_REPO_EXTEND_SOLVABLES ]
+ $f close
+ }
+ }
+ $handle internalize
+ }
+ }
+ repo_susetags_add_exts self
+ repo_writecachedrepo self
+ $self(handle) create_stubs
+ return 1
+}
+
+proc repo_susetags_packagespath {selfName} {
+ upvar $selfName self
+ set datadir [[$self(handle) cget -meta] lookup_str $solv::SUSETAGS_DATADIR]
+ return [expr {$datadir ne {} ? "$datadir/" : "suse/"}]
+}
+
+proc repo_susetags_load_ext {selfName repodata} {
+ upvar $selfName self
+ set filename [$repodata lookup_str $solv::SOLVID_META $solv::SUSETAGS_FILE_NAME]
+ set ext [string range $filename 9 10]
+ puts -nonewline "\[$self(name):$ext: "
+ flush stdout
+ if [repo_usecachedrepo self $ext] {
+ puts "cached]"
+ return 1
+ }
+ puts "fetching]"
+ set handle $self(handle)
+ set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
+ set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
+ if {$descrdir eq {NULL}} {
+ set descrdir "suse/setup/descr"
+ }
+ set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::SUSETAGS_FILE_CHECKSUM]
+ set f [repo_download self "$descrdir/$filename" 1 $filechecksum]
+ if {$f eq {NULL}} {
+ return 0
+ }
+ set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
+ if {$ext ne "DL"} {
+ set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
+ }
+ $handle add_susetags $f $defvendorid $ext $flags
+ $f close
+ repo_writecachedrepo self $ext $repodata
+ return 1
+}
+
+### unknown
+
+proc repo_unknown_load {selfName pool} {
+ upvar $selfName self
+ puts "unsupported repo '$self(name)': skipped"
+ return 0
+}
+
+### poor man's OO
+
+proc repo_load {selfName pool} {
+ upvar $selfName self
+ "repo_$self(type)_load" self $pool
+}
+
+proc repo_packagespath {selfName} {
+ upvar $selfName self
+ "repo_$self(type)_packagespath" self
+}
+
+proc repo_load_ext {selfName repodata} {
+ upvar $selfName self
+ "repo_$self(type)_load_ext" self $repodata
+}
+
+###
+
+proc load_stub {repodata} {
+ set code [catch {
+ upvar #0 [[$repodata cget -repo] cget -appdata] repo
+ if [info exists repo(handle)] {
+ return [repo_load_ext repo $repodata]
+ }
+ return 0
+ } res]
+ if {$code == 2} {
+ return $res
+ }
+ puts stderr $res
+ return 0
+}
+
+###
+
+set repoNames {}
+foreach reponame [lsort [glob -nocomplain -directory $reposdir *.repo]] {
+ set ini [::ini::open $reponame r]
+ foreach alias [::ini::sections $ini] {
+ upvar #0 [globalarray] repo
+ array set repo {enabled 0 priority 99 autorefresh 1 type rpm-md metadata_expire 900}
+ array set repo [::ini::get $ini $alias]
+ set repo(name) $alias
+ switch -exact -- $repo(type) {
+ rpm-md { set repo(type) repomd }
+ yast2 { set repo(type) susetags }
+ default { set repo(type) unknown }
+ }
+ lappend repoNames $repo(varName)
+ }
+ ::ini::close $ini
+}
+
+set pool [solv::new_Pool]
+$pool setarch
+$pool set_loadcallback load_stub
+
+upvar #0 [globalarray] sysrepo
+array set sysrepo [list name {@System} type system]
+repo_load sysrepo $pool
+
+foreach repoName $repoNames {
+ upvar 0 $repoName repo
+ if {$repo(enabled)} {
+ repo_load repo $pool
+ }
+}
+
+
+set cmd [lindex $::argv 0]
+set ::argv [lreplace $::argv 0 0]
+
+array set cmdabbrev [ list \
+ in install \
+ rm erase \
+ ls list \
+ ve verify \
+ se search \
+]
+if [info exists cmdabbrev($cmd)] {
+ set cmd $cmdabbrev($cmd)
+}
+
+if {$cmd eq "search"} {
+ set arg [lindex $::argv 0]
+ $pool createwhatprovides
+ set sel [$pool Selection]
+ set di [$pool Dataiterator $solv::SOLVABLE_NAME $arg [ expr $solv::Dataiterator_SEARCH_SUBSTRING | $solv::Dataiterator_SEARCH_NOCASE ]]
+ solv::iter d $di {
+ $sel add_raw $solv::Job_SOLVER_SOLVABLE [$d cget -solvid]
+ }
+ foreach s [$sel solvables] {
+ puts [format { - %s [%s]: %s} [$s str] [[$s cget -repo] cget -name] [$s lookup_str $solv::SOLVABLE_SUMMARY]]
+ }
+ exit
+}
+
+$pool addfileprovides
+$pool createwhatprovides
+
+array set cmdactionmap [ list \
+ install $solv::Job_SOLVER_INSTALL \
+ erase $solv::Job_SOLVER_ERASE \
+ up $solv::Job_SOLVER_UPDATE \
+ dup $solv::Job_SOLVER_DISTUPGRADE \
+ verify $solv::Job_SOLVER_VERIFY \
+ list 0 \
+ info 0 \
+]
+
+set jobs {}
+foreach arg $::argv {
+ set flags [expr $solv::Selection_SELECTION_NAME | $solv::Selection_SELECTION_PROVIDES | $solv::Selection_SELECTION_GLOB | \
+ $solv::Selection_SELECTION_CANON | $solv::Selection_SELECTION_DOTARCH | $solv::Selection_SELECTION_REL ]
+ switch -glob -- $arg {
+ "/*" {
+ set flags [expr $flags | $solv::Selection_SELECTION_FILELIST ]
+ if {$cmd eq "erase"} {
+ set flags [expr $flags | $solv::Selection_SELECTION_INSTALLED_ONLY ]
+ }
+ }
+ }
+ set sel [$pool select $arg $flags]
+ if [$sel isempty] {
+ set sel [$pool select $arg [expr $flags | $solv::Selection_SELECTION_NOCASE]]
+ if {![$sel isempty]} {
+ puts "\[ignoring case for '$arg']"
+ }
+ }
+ if [$sel isempty] {
+ puts "nothing matches '$arg'"
+ exit 1
+ }
+ if {[$sel flags] & $solv::Selection_SELECTION_FILELIST} {
+ puts "\[using file list match for '$arg']"
+ }
+ if {[$sel flags] & $solv::Selection_SELECTION_PROVIDES} {
+ puts "\[using capability match for '$arg']"
+ }
+ lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
+}
+
+if {$jobs eq {} && ($cmd eq "up" || $cmd eq "dup" || $cmd eq "verify") } {
+ set sel [$pool Selection_all]
+ lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
+}
+
+if {$jobs eq {}} {
+ puts "no package matched."
+ exit 1
+}
+
+if {$cmd eq "list" || $cmd eq "info"} {
+ foreach job $jobs {
+ foreach s [$job solvables] {
+ if {$cmd eq "info"} {
+ puts [format {Name: %s} [$s str]]
+ puts [format {Repo: %s} [[$s cget -repo] cget -name]]
+ puts [format {Summary: %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
+ set str [$s lookup_str $solv::SOLVABLE_URL]
+ if {$str ne {}} {
+ puts [format {Url: %s} $str]
+ }
+ set str [$s lookup_str $solv::SOLVABLE_LICENSE]
+ if {$str ne {}} {
+ puts [format {License %s} $str]
+ }
+ puts [format {Description: %s} [$s lookup_str $solv::SOLVABLE_DESCRIPTION]]
+ puts {}
+ } else {
+ puts [format { - %s [%s]} [$s str] [[$s cget -repo] cget -name]]
+ puts [format { %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
+ }
+ }
+ }
+ exit
+}
+
+#$pool set_debuglevel 1
+set solver [$pool Solver]
+$solver set_flag $solv::Solver_SOLVER_FLAG_SPLITPROVIDES 1
+if {$cmd eq "erase"} {
+ $solver set_flag $solv::Solver_SOLVER_FLAG_ALLOW_UNINSTALL 1
+}
+
+set problems [$solver solve $jobs]
+if {$problems ne {}} {
+ set pcnt 1
+ foreach problem $problems {
+ puts [format {Problem %d/%d:} $pcnt [llength $problems]]
+ puts [$problem str]
+ incr pcnt
+ }
+ exit 1
+}
+
+set trans [$solver transaction]
+
+if [$trans isempty] {
+ puts "Nothing to do."
+ exit
+}
+
+puts {}
+puts "Transaction summary:"
+puts {}
+foreach cl [$trans classify [expr $solv::Transaction_SOLVER_TRANSACTION_SHOW_OBSOLETES | $solv::Transaction_SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE]] {
+ switch -- [$cl cget -type] \
+ $solv::Transaction_SOLVER_TRANSACTION_ERASE {
+ puts [format {%d erased packages:} [$cl cget -count]]
+ } \
+ $solv::Transaction_SOLVER_TRANSACTION_INSTALL {
+ puts [format {%d installed packages:} [$cl cget -count]]
+ } \
+ $solv::Transaction_SOLVER_TRANSACTION_REINSTALLED {
+ puts [format {%d reinstalled packages:} [$cl cget -count]]
+ } \
+ $solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED {
+ puts [format {%d downgraded packages:} [$cl cget -count]]
+ } \
+ $solv::Transaction_SOLVER_TRANSACTION_CHANGED {
+ puts [format {%d changed packages:} [$cl cget -count]]
+ } \
+ $solv::Transaction_SOLVER_TRANSACTION_UPGRADED {
+ puts [format {%d upgraded packages:} [$cl cget -count]]
+ } \
+ $solv::Transaction_SOLVER_TRANSACTION_VENDORCHANGE {
+ puts [format {%d vendor changes from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
+ } \
+ $solv::Transaction_SOLVER_TRANSACTION_ARCHCHANGE {
+ puts [format {%d archchanges from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
+ } \
+ default continue
+ foreach p [$cl solvables] {
+ set cltype [$cl cget -type]
+ if {$cltype == $solv::Transaction_SOLVER_TRANSACTION_UPGRADED || $cltype ==$solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED} {
+ set op [$trans othersolvable $p]
+ puts [format { - %s -> %s} [$p str] [$op str]]
+ } else {
+ puts [format { - %s} [$p str]]
+ }
+ }
+ puts {}
+}
+puts [format {install size change: %d K} [$trans calc_installsizechange]]
+puts {}
+
+while 1 {
+ puts -nonewline "OK to continue (y/n)? "
+ flush stdout
+ set yn [gets stdin]
+ if {$yn eq "y"} {
+ break
+ }
+ if {$yn eq "n" || $yn eq "q"} {
+ exit
+ }
+}
+
+set newpkgs [$trans newsolvables]
+array set newpkgs_f {}
+if {$newpkgs ne {}} {
+ set downloadsize 0
+ foreach p $newpkgs {
+ set downloadsize [expr $downloadsize + [$p lookup_num $solv::SOLVABLE_DOWNLOADSIZE]]
+ }
+ puts [format {Downloading %d packages, %d K} [llength $newpkgs] [expr $downloadsize / 1024]]
+ foreach p $newpkgs {
+ upvar #0 [[$p cget -repo] cget -appdata] repo
+ set location [$p lookup_location]
+ if {$location eq {}} {
+ continue
+ }
+ set location "[repo_packagespath repo][lindex $location 0]"
+ set checksum [$p lookup_checksum $solv::SOLVABLE_CHECKSUM]
+ set f [repo_download repo $location 0 $checksum]
+ set newpkgs_f([$p cget -id]) $f
+ puts -nonewline "."
+ flush stdout
+ }
+ puts {}
+}
+
+puts "Committing transaction:"
+$trans order
+foreach p [$trans steps] {
+ set steptype [$trans steptype $p $solv::Transaction_SOLVER_TRANSACTION_RPM_ONLY]
+ if {$steptype == $solv::Transaction_SOLVER_TRANSACTION_ERASE} {
+ puts "erase [$p str]"
+ regsub {^[0-9]+:} [$p cget -evr] {} nvr
+ set nvr "[$p cget -name]-$nvr.[$p cget -arch]"
+ exec -ignorestderr -- rpm -e --nodeps --nodigest --nosignature $nvr
+ } elseif {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL || $steptype == $solv::Transaction_SOLVER_TRANSACTION_MULTIINSTALL} {
+ puts "install [$p str]"
+ set f $newpkgs_f([$p cget -id])
+ set mode [expr {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL ? "-U" : "-i"}]
+ $f cloexec 0
+ exec -ignorestderr -- rpm $mode --force --nodeps --nodigest --nosignature "/dev/fd/[$f fileno]"
+ }
+}
--- /dev/null
+SET (libsolvext_SRCS
+ solv_xfopen.c testcase.c)
+
+SET (libsolvext_HEADERS
+ tools_util.h solv_xfopen.h testcase.h)
+
+IF (ENABLE_RPMDB)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ pool_fileconflicts.c repo_rpmdb.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ pool_fileconflicts.h repo_rpmdb.h)
+ENDIF (ENABLE_RPMDB)
+
+IF (ENABLE_PUBKEY)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_pubkey.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_pubkey.h)
+ENDIF (ENABLE_PUBKEY)
+
+IF (ENABLE_PGPVRFY)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ solv_pgpvrfy.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ solv_pgpvrfy.h)
+ENDIF (ENABLE_PGPVRFY)
+
+IF (ENABLE_RPMMD)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_repomdxml.c repo_rpmmd.c
+ repo_deltainfoxml.c repo_updateinfoxml.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_repomdxml.h repo_rpmmd.h
+ repo_deltainfoxml.h repo_updateinfoxml.h)
+ENDIF (ENABLE_RPMMD)
+
+IF (ENABLE_SUSEREPO)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_content.c repo_products.c repo_releasefile_products.c
+ repo_susetags.c repo_zyppdb.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_content.h repo_products.h repo_releasefile_products.h
+ repo_susetags.h repo_zyppdb.h)
+ENDIF (ENABLE_SUSEREPO)
+
+# old cmake does not support parenthetical expressions...
+IF (ENABLE_COMPLEX_DEPS)
+IF (ENABLE_SUSEREPO OR ENABLE_RPMMD OR ENABLE_RPMDB)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ pool_parserpmrichdep.c)
+ENDIF (ENABLE_SUSEREPO OR ENABLE_RPMMD OR ENABLE_RPMDB)
+ENDIF (ENABLE_COMPLEX_DEPS)
+
+IF (SUSE)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_autopattern.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_autopattern.h)
+ENDIF (SUSE)
+
+IF (ENABLE_COMPS)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_comps.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_comps.h)
+ENDIF (ENABLE_COMPS)
+
+IF (ENABLE_DEBIAN)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_deb.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_deb.h)
+ENDIF (ENABLE_DEBIAN)
+
+IF (ENABLE_HELIXREPO)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_helix.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_helix.h)
+ENDIF (ENABLE_HELIXREPO)
+
+IF (ENABLE_MDKREPO)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_mdk.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_mdk.h)
+ENDIF (ENABLE_MDKREPO)
+
+IF (ENABLE_ARCHREPO)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_arch.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_arch.h)
+ENDIF (ENABLE_ARCHREPO)
+
+IF (ENABLE_CUDFREPO)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_cudf.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_cudf.h)
+ENDIF (ENABLE_CUDFREPO)
+
+IF (ENABLE_HAIKU)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_haiku.cpp)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_haiku.h)
+ENDIF (ENABLE_HAIKU)
+
+IF (ENABLE_APPDATA)
+ SET (libsolvext_SRCS ${libsolvext_SRCS}
+ repo_appdata.c)
+ SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+ repo_appdata.h)
+ENDIF (ENABLE_APPDATA)
+
+SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+IF (HAVE_LINKER_VERSION_SCRIPT)
+SET (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_FLAGS} -Wl,--version-script=${CMAKE_SOURCE_DIR}/ext/libsolvext.ver")
+ENDIF (HAVE_LINKER_VERSION_SCRIPT)
+
+IF (DISABLE_SHARED)
+ADD_LIBRARY (libsolvext STATIC ${libsolvext_SRCS})
+ELSE (DISABLE_SHARED)
+ADD_LIBRARY (libsolvext SHARED ${libsolvext_SRCS})
+TARGET_LINK_LIBRARIES(libsolvext libsolv ${SYSTEM_LIBRARIES})
+ENDIF (DISABLE_SHARED)
+
+SET_TARGET_PROPERTIES(libsolvext PROPERTIES OUTPUT_NAME "solvext")
+SET_TARGET_PROPERTIES(libsolvext PROPERTIES SOVERSION ${LIBSOLVEXT_SOVERSION})
+SET_TARGET_PROPERTIES(libsolvext PROPERTIES INSTALL_NAME_DIR ${LIB_INSTALL_DIR})
+
+INSTALL (FILES ${libsolvext_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/solv")
+INSTALL (TARGETS libsolvext LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+
+IF (ENABLE_STATIC AND NOT DISABLE_SHARED)
+ADD_LIBRARY (libsolvext_static STATIC ${libsolvext_SRCS})
+SET_TARGET_PROPERTIES(libsolvext_static PROPERTIES OUTPUT_NAME "solvext")
+SET_TARGET_PROPERTIES(libsolvext_static PROPERTIES SOVERSION ${LIBSOLVEXT_SOVERSION})
+INSTALL (TARGETS libsolvext_static LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+ENDIF (ENABLE_STATIC AND NOT DISABLE_SHARED)
--- /dev/null
+SOLV_1.0 {
+ global:
+ pool_deb_get_autoinstalled;
+ pool_findfileconflicts;
+ repo_add_appdata;
+ repo_add_appdata_dir;
+ repo_add_arch_local;
+ repo_add_arch_pkg;
+ repo_add_arch_repo;
+ repo_add_autopattern;
+ repo_add_code11_products;
+ repo_add_content;
+ repo_add_comps;
+ repo_add_cudf;
+ repo_add_deb;
+ repo_add_debdb;
+ repo_add_debpackages;
+ repo_add_deltainfoxml;
+ repo_add_haiku_installed_packages;
+ repo_add_haiku_package;
+ repo_add_haiku_package_info;
+ repo_add_haiku_packages;
+ repo_add_helix;
+ repo_add_keydir;
+ repo_add_keyring;
+ repo_add_mdk;
+ repo_add_mdk_info;
+ repo_add_products;
+ repo_add_pubkey;
+ repo_add_releasefile_products;
+ repo_add_repomdxml;
+ repo_add_rpm;
+ repo_add_rpm_handle;
+ repo_add_rpmdb;
+ repo_add_rpmdb_pubkeys;
+ repo_add_rpmdb_reffp;
+ repo_add_rpmmd;
+ repo_add_susetags;
+ repo_add_updateinfoxml;
+ repo_add_zyppdb_products;
+ repo_find_all_pubkeys;
+ repo_find_pubkey;
+ repo_verify_sigdata;
+ rpm_byfp;
+ rpm_byrpmdbid;
+ rpm_byrpmh;
+ rpm_installedrpmdbids;
+ rpm_iterate_filelist;
+ rpm_query;
+ rpm_query_num;
+ rpm_state_create;
+ rpm_state_free;
+ solv_verify_sig;
+ solv_xfopen;
+ solv_xfopen_buf;
+ solv_xfopen_fd;
+ solv_xfopen_iscompressed;
+ solvsig_create;
+ solvsig_free;
+ solvsig_verify;
+ testcase_add_testtags;
+ testcase_dep2str;
+ testcase_job2str;
+ testcase_solvid2str;
+ testcase_str2dep;
+ testcase_str2job;
+ testcase_str2repo;
+ testcase_read;
+ testcase_resultdiff;
+ testcase_solverresult;
+ testcase_write;
+ testcase_write_testtags;
+ local:
+ *;
+};
--- /dev/null
+/*
+ * Copyright (c) 2009-2013, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "hash.h"
+#include "repo_rpmdb.h"
+#include "pool_fileconflicts.h"
+
+struct cbdata {
+ Pool *pool;
+ int create;
+ int aliases;
+
+ Queue lookat; /* conflict candidates */
+ Queue lookat_dir; /* not yet conflicting directories */
+
+ Hashtable cflmap;
+ Hashval cflmapn;
+ unsigned int cflmapused;
+
+ Hashtable dirmap;
+ Hashval dirmapn;
+ unsigned int dirmapused;
+ int dirconflicts;
+
+ Map idxmap;
+
+ unsigned int lastdiridx; /* last diridx we have seen */
+ unsigned int lastdirhash; /* strhash of last dir we have seen */
+
+ Id idx; /* index of package we're looking at */
+ Id hx; /* used in findfileconflicts2_cb, limit to files matching hx */
+
+ Id dirid; /* used in findfileconflicts2_cb, limit to dirs matching dirid */
+ Id dirhash; /* used in findfileconflicts2_cb, limit to dirs matching dirhash */
+
+ Queue files;
+ unsigned char *filesspace;
+ unsigned int filesspacen;
+
+ Hashtable normap;
+ Hashval normapn;
+ unsigned int normapused;
+ Queue norq;
+
+ Hashtable statmap;
+ Hashval statmapn;
+ unsigned int statmapused;
+
+ int usestat;
+ int statsmade;
+
+ const char *rootdir;
+ int rootdirl;
+
+ char *canonspace;
+ int canonspacen;
+};
+
+#define FILESSPACE_BLOCK 255
+
+static Hashtable
+growhash(Hashtable map, Hashval *mapnp)
+{
+ Hashval mapn = *mapnp;
+ Hashval newn = (mapn + 1) * 2 - 1;
+ Hashval i, h, hh;
+ Hashtable m;
+ Id hx, qx;
+
+ m = solv_calloc(newn + 1, 2 * sizeof(Id));
+ for (i = 0; i <= mapn; i++)
+ {
+ hx = map[2 * i];
+ if (!hx)
+ continue;
+ h = hx & newn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = m[2 * h];
+ if (!qx)
+ break;
+ h = HASHCHAIN_NEXT(h, hh, newn);
+ }
+ m[2 * h] = hx;
+ m[2 * h + 1] = map[2 * i + 1];
+ }
+ solv_free(map);
+ *mapnp = newn;
+ return m;
+}
+
+static void
+finddirs_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
+{
+ struct cbdata *cbdata = cbdatav;
+ Hashval h, hh;
+ Id hx, qx;
+ Id oidx, idx = cbdata->idx;
+
+ hx = strhash(fn);
+ if (!hx)
+ hx = strlen(fn) + 1;
+ h = hx & cbdata->dirmapn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = cbdata->dirmap[2 * h];
+ if (!qx)
+ break;
+ if (qx == hx)
+ break;
+ h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn);
+ }
+ if (!qx)
+ {
+ /* a miss */
+ if (!cbdata->create)
+ return;
+ cbdata->dirmap[2 * h] = hx;
+ cbdata->dirmap[2 * h + 1] = idx;
+ if (++cbdata->dirmapused * 2 > cbdata->dirmapn)
+ cbdata->dirmap = growhash(cbdata->dirmap, &cbdata->dirmapn);
+ return;
+ }
+ oidx = cbdata->dirmap[2 * h + 1];
+ if (oidx == idx)
+ return;
+ /* found a conflict, this dir may be used in multiple packages */
+ if (oidx != -1)
+ {
+ MAPSET(&cbdata->idxmap, oidx);
+ cbdata->dirmap[2 * h + 1] = -1;
+ cbdata->dirconflicts++;
+ }
+ MAPSET(&cbdata->idxmap, idx);
+}
+
+static inline int
+isindirmap(struct cbdata *cbdata, Id hx)
+{
+ Hashval h, hh;
+ Id qx;
+
+ h = hx & cbdata->dirmapn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = cbdata->dirmap[2 * h];
+ if (!qx)
+ return 0;
+ if (qx == hx)
+ return cbdata->dirmap[2 * h + 1] == -1 ? 1 : 0;
+ h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn);
+ }
+}
+
+static void
+findfileconflicts_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
+{
+ struct cbdata *cbdata = cbdatav;
+ int isdir = S_ISDIR(info->mode);
+ const char *dp;
+ Id idx, oidx;
+ Id hx, qx;
+ Hashval h, hh, dhx;
+
+ idx = cbdata->idx;
+
+ if (!info->dirlen)
+ return;
+ dp = fn + info->dirlen;
+ if (info->diridx != cbdata->lastdiridx)
+ {
+ cbdata->lastdiridx = info->diridx;
+ cbdata->lastdirhash = strnhash(fn, dp - fn);
+ }
+ dhx = cbdata->lastdirhash;
+ /* this mirrors the "if (!hx) hx = strlen(fn) + 1" in finddirs_cb */
+ if (!isindirmap(cbdata, dhx ? dhx : dp - fn + 1))
+ return;
+ hx = strhash_cont(dp, dhx);
+ if (!hx)
+ hx = strlen(fn) + 1;
+
+ h = hx & cbdata->cflmapn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = cbdata->cflmap[2 * h];
+ if (!qx)
+ break;
+ if (qx == hx)
+ break;
+ h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
+ }
+ if (!qx)
+ {
+ /* a miss */
+ if (!cbdata->create)
+ return;
+ cbdata->cflmap[2 * h] = hx;
+ cbdata->cflmap[2 * h + 1] = (isdir ? ~idx : idx);
+ if (++cbdata->cflmapused * 2 > cbdata->cflmapn)
+ cbdata->cflmap = growhash(cbdata->cflmap, &cbdata->cflmapn);
+ return;
+ }
+ oidx = cbdata->cflmap[2 * h + 1];
+ if (oidx < 0)
+ {
+ int i;
+ if (isdir)
+ {
+ /* both are directories. delay the conflict, keep oidx in slot */
+ queue_push2(&cbdata->lookat_dir, hx, idx);
+ return;
+ }
+ oidx = ~oidx;
+ /* now have file, had directories before. */
+ cbdata->cflmap[2 * h + 1] = oidx; /* make it a file */
+ /* dump all delayed directory hits for hx */
+ for (i = 0; i < cbdata->lookat_dir.count; i += 2)
+ if (cbdata->lookat_dir.elements[i] == hx)
+ {
+ queue_push2(&cbdata->lookat, hx, cbdata->lookat_dir.elements[i + 1]);
+ queue_push2(&cbdata->lookat, 0, 0);
+ }
+ }
+ else if (oidx == idx)
+ return; /* no conflicts with ourself, please */
+ queue_push2(&cbdata->lookat, hx, oidx);
+ queue_push2(&cbdata->lookat, 0, 0);
+ queue_push2(&cbdata->lookat, hx, idx);
+ queue_push2(&cbdata->lookat, 0, 0);
+}
+
+/* same as findfileconflicts_cb, but
+ * - hashes with just the basename
+ * - sets idx in a map instead of pushing to lookat
+ * - sets the hash element to -1 if there may be a conflict
+ */
+static void
+findfileconflicts_basename_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
+{
+ struct cbdata *cbdata = cbdatav;
+ int isdir = S_ISDIR(info->mode);
+ const char *dp;
+ Id idx, oidx;
+ Id hx, qx;
+ Hashval h, hh;
+
+ idx = cbdata->idx;
+
+ if (!info->dirlen)
+ return;
+ dp = fn + info->dirlen;
+ hx = strhash(dp);
+ if (!hx)
+ hx = strlen(fn) + 1;
+
+ h = hx & cbdata->cflmapn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = cbdata->cflmap[2 * h];
+ if (!qx)
+ break;
+ if (qx == hx)
+ break;
+ h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
+ }
+ if (!qx)
+ {
+ /* a miss */
+ if (!cbdata->create)
+ return;
+ cbdata->cflmap[2 * h] = hx;
+ cbdata->cflmap[2 * h + 1] = (isdir ? -idx - 2 : idx);
+ if (++cbdata->cflmapused * 2 > cbdata->cflmapn)
+ cbdata->cflmap = growhash(cbdata->cflmap, &cbdata->cflmapn);
+ return;
+ }
+ oidx = cbdata->cflmap[2 * h + 1];
+ if (oidx < -1)
+ {
+ int i;
+ if (isdir)
+ {
+ /* both are directories. delay the conflict, keep oidx in slot */
+ queue_push2(&cbdata->lookat_dir, hx, idx);
+ return;
+ }
+ oidx = -idx - 2;
+ /* now have file, had directories before. */
+ cbdata->cflmap[2 * h + 1] = oidx; /* make it a file */
+ /* dump all delayed directory hits for hx */
+ for (i = 0; i < cbdata->lookat_dir.count; i += 2)
+ if (cbdata->lookat_dir.elements[i] == hx)
+ MAPSET(&cbdata->idxmap, cbdata->lookat_dir.elements[i + 1]);
+ }
+ else if (oidx == idx)
+ return; /* no conflicts with ourself, please */
+ if (oidx >= 0)
+ MAPSET(&cbdata->idxmap, oidx);
+ MAPSET(&cbdata->idxmap, idx);
+ if (oidx != -1)
+ cbdata->cflmap[2 * h + 1] = -1;
+}
+
+static inline Id
+addfilesspace(struct cbdata *cbdata, int len)
+{
+ unsigned int off = cbdata->filesspacen;
+ cbdata->filesspace = solv_extend(cbdata->filesspace, cbdata->filesspacen, len, 1, FILESSPACE_BLOCK);
+ cbdata->filesspacen += len;
+ return off;
+}
+
+static Id
+unifywithstat(struct cbdata *cbdata, Id diroff, int dirl)
+{
+ struct stat stb;
+ int i;
+ Hashval h, hh;
+ Id hx, qx;
+ Id nspaceoff;
+ unsigned char statdata[16 + sizeof(stb.st_dev) + sizeof(stb.st_ino)];
+
+ if (dirl > 1 && cbdata->filesspace[diroff + dirl - 1] == '/')
+ cbdata->filesspace[diroff + dirl - 1] = 0;
+ cbdata->statsmade++;
+ i = stat((char *)cbdata->filesspace + diroff, &stb);
+ if (dirl > 1 && cbdata->filesspace[diroff + dirl - 1] == 0)
+ cbdata->filesspace[diroff + dirl - 1] = '/';
+ if (i)
+ return diroff;
+ memset(statdata, 0, 16);
+ memcpy(statdata + 8, &stb.st_dev, sizeof(stb.st_dev));
+ memcpy(statdata, &stb.st_ino, sizeof(stb.st_ino));
+ hx = 0;
+ for (i = 15; i >= 0; i--)
+ hx = (unsigned int)hx * 13 + statdata[i];
+ h = hx & cbdata->statmapn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = cbdata->statmap[2 * h];
+ if (!qx)
+ break;
+ if (qx == hx)
+ {
+ Id off = cbdata->statmap[2 * h + 1];
+ char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off];
+ if (!memcmp(dp, statdata, 16))
+ return cbdata->norq.elements[off + 1];
+ }
+ h = HASHCHAIN_NEXT(h, hh, cbdata->statmapn);
+ }
+ /* new stat result. work. */
+ nspaceoff = addfilesspace(cbdata, 16);
+ memcpy(cbdata->filesspace + nspaceoff, statdata, 16);
+ queue_push2(&cbdata->norq, nspaceoff, nspaceoff);
+ cbdata->statmap[2 * h] = hx;
+ cbdata->statmap[2 * h + 1] = cbdata->norq.count - 2;
+ if (++cbdata->statmapused * 2 > cbdata->statmapn)
+ cbdata->statmap = growhash(cbdata->statmap, &cbdata->statmapn);
+ return nspaceoff;
+}
+
+/* forward declaration */
+static Id normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create);
+
+static Id
+unifywithcanon(struct cbdata *cbdata, Id diroff, int dirl)
+{
+ Id dirnameid;
+ int i, l, ll, lo;
+ struct stat stb;
+
+#if 0
+ printf("UNIFY %.*s\n", dirl, (char *)cbdata->filesspace + diroff);
+#endif
+ if (!dirl || cbdata->filesspace[diroff] != '/')
+ return diroff;
+ /* strip / at end*/
+ while (dirl && cbdata->filesspace[diroff + dirl - 1] == '/')
+ dirl--;
+ if (!dirl)
+ return diroff;
+
+ /* find dirname */
+ for (i = dirl - 1; i > 0; i--)
+ if (cbdata->filesspace[diroff + i] == '/')
+ break;
+ i++; /* include trailing / */
+
+ /* normalize dirname */
+ dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + diroff, i, strnhash((char *)cbdata->filesspace + diroff, i), 1);
+ if (dirnameid == -1)
+ return diroff; /* hit "in progress" marker, some cyclic link */
+
+ /* sanity check result */
+ if (cbdata->filesspace[dirnameid] != '/')
+ return diroff; /* hmm */
+ l = strlen((char *)cbdata->filesspace + dirnameid);
+ if (l && cbdata->filesspace[dirnameid + l - 1] != '/')
+ return diroff; /* hmm */
+
+ /* special handling for "." and ".." basename */
+ if (cbdata->filesspace[diroff + i] == '.')
+ {
+ if (dirl - i == 1)
+ return dirnameid;
+ if (dirl - i == 2 && cbdata->filesspace[diroff + i + 1] == '.')
+ {
+ if (l <= 2)
+ return dirnameid; /* we hit our root */
+ for (i = l - 2; i > 0; i--)
+ if (cbdata->filesspace[dirnameid + i] == '/')
+ break;
+ i++; /* include trailing / */
+ dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + dirnameid, i, strnhash((char *)cbdata->filesspace + dirnameid, i), 1);
+ return dirnameid == -1 ? diroff : dirnameid;
+ }
+ }
+
+ /* append basename to normalized dirname */
+ if (cbdata->rootdirl + l + dirl - i + 1 > cbdata->canonspacen)
+ {
+ cbdata->canonspacen = cbdata->rootdirl + l + dirl - i + 20;
+ cbdata->canonspace = solv_realloc(cbdata->canonspace, cbdata->canonspacen);
+ strcpy(cbdata->canonspace, cbdata->rootdir);
+ }
+ strcpy(cbdata->canonspace + cbdata->rootdirl, (char *)cbdata->filesspace + dirnameid);
+ strncpy(cbdata->canonspace + cbdata->rootdirl + l, (char *)cbdata->filesspace + diroff + i, dirl - i);
+ cbdata->canonspace[cbdata->rootdirl + l + dirl - i] = 0;
+
+#if 0
+ printf("stat()ing %s\n", cbdata->canonspace);
+#endif
+ cbdata->statsmade++;
+ if (lstat(cbdata->canonspace, &stb) != 0 || !S_ISLNK(stb.st_mode))
+ {
+ /* not a symlink or stat failed, have new canon entry */
+ diroff = addfilesspace(cbdata, l + dirl - i + 2);
+ strcpy((char *)cbdata->filesspace + diroff, cbdata->canonspace + cbdata->rootdirl);
+ l += dirl - i;
+ /* add trailing / */
+ if (cbdata->filesspace[diroff + l - 1] != '/')
+ {
+ cbdata->filesspace[diroff + l++] = '/';
+ cbdata->filesspace[diroff + l] = 0;
+ }
+ /* call normalizedir on new entry for unification purposes */
+ dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + diroff, l, strnhash((char *)cbdata->filesspace + diroff, l), 1);
+ return dirnameid == -1 ? diroff : dirnameid;
+ }
+ /* oh no, a symlink! follow */
+ lo = cbdata->rootdirl + l + dirl - i + 1;
+ if (lo + stb.st_size + 2 > cbdata->canonspacen)
+ {
+ cbdata->canonspacen = lo + stb.st_size + 20;
+ cbdata->canonspace = solv_realloc(cbdata->canonspace, cbdata->canonspacen);
+ }
+ ll = readlink(cbdata->canonspace, cbdata->canonspace + lo, stb.st_size);
+ if (ll < 0 || ll > stb.st_size)
+ return diroff; /* hmm */
+ if (ll == 0)
+ return dirnameid; /* empty means current dir */
+ if (cbdata->canonspace[lo + ll - 1] != '/')
+ cbdata->canonspace[lo + ll++] = '/'; /* add trailing / */
+ cbdata->canonspace[lo + ll] = 0; /* zero terminate */
+ if (cbdata->canonspace[lo] != '/')
+ {
+ /* relative link, concatenate to dirname */
+ memmove(cbdata->canonspace + cbdata->rootdirl + l, cbdata->canonspace + lo, ll + 1);
+ lo = cbdata->rootdirl;
+ ll += l;
+ }
+ dirnameid = normalizedir(cbdata, cbdata->canonspace + lo, ll, strnhash(cbdata->canonspace + lo, ll), 1);
+ return dirnameid == -1 ? diroff : dirnameid;
+}
+
+/*
+ * map a directory (containing a trailing /) into a number.
+ * for unifywithstat this is the offset to the 16 byte stat result.
+ * for unifywithcanon this is the offset to the normailzed dir.
+ */
+static Id
+normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create)
+{
+ Hashval h, hh;
+ Id qx;
+ Id nspaceoff;
+ int mycnt;
+
+ if (!hx)
+ hx = dirl + 1;
+ h = hx & cbdata->normapn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = cbdata->normap[2 * h];
+ if (!qx)
+ break;
+ if (qx == hx)
+ {
+ Id off = cbdata->normap[2 * h + 1];
+ char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off];
+ if (!strncmp(dp, dir, dirl) && dp[dirl] == 0)
+ return cbdata->norq.elements[off + 1];
+ }
+ h = HASHCHAIN_NEXT(h, hh, cbdata->normapn);
+ }
+ if (!create)
+ return 0;
+ /* new dir. work. */
+ if (dir >= (const char *)cbdata->filesspace && dir < (const char *)cbdata->filesspace + cbdata->filesspacen)
+ {
+ /* can happen when called from unifywithcanon */
+ Id off = dir - (const char *)cbdata->filesspace;
+ nspaceoff = addfilesspace(cbdata, dirl + 1);
+ dir = (const char *)cbdata->filesspace + off;
+ }
+ else
+ nspaceoff = addfilesspace(cbdata, dirl + 1);
+ if (dirl)
+ memcpy(cbdata->filesspace + nspaceoff, dir, dirl);
+ cbdata->filesspace[nspaceoff + dirl] = 0;
+ mycnt = cbdata->norq.count;
+ queue_push2(&cbdata->norq, nspaceoff, -1); /* -1: in progress */
+ cbdata->normap[2 * h] = hx;
+ cbdata->normap[2 * h + 1] = mycnt;
+ if (++cbdata->normapused * 2 > cbdata->normapn)
+ cbdata->normap = growhash(cbdata->normap, &cbdata->normapn);
+ /* unify */
+ if (cbdata->usestat)
+ nspaceoff = unifywithstat(cbdata, nspaceoff, dirl);
+ else
+ nspaceoff = unifywithcanon(cbdata, nspaceoff, dirl);
+ cbdata->norq.elements[mycnt + 1] = nspaceoff; /* patch in result */
+#if 0
+ if (!cbdata->usestat)
+ printf("%s normalized to %d: %s\n", cbdata->filesspace + cbdata->norq.elements[mycnt], nspaceoff, cbdata->filesspace + nspaceoff);
+#endif
+ return nspaceoff;
+}
+
+static void
+findfileconflicts_alias_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
+{
+ int isdir = S_ISDIR(info->mode);
+ struct cbdata *cbdata = cbdatav;
+ const char *dp;
+ Id idx, dirid;
+ Id hx, qx;
+ Hashval h, hh;
+
+ idx = cbdata->idx;
+
+ if (!info->dirlen)
+ return;
+ dp = fn + info->dirlen;
+ if (info->diridx != cbdata->lastdiridx)
+ {
+ cbdata->lastdiridx = info->diridx;
+ cbdata->lastdirhash = 0;
+ }
+ dp = fn + info->dirlen;
+ hx = strhash(dp);
+ if (!hx)
+ hx = strlen(fn) + 1;
+
+ h = hx & cbdata->cflmapn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = cbdata->cflmap[2 * h];
+ if (!qx)
+ break;
+ if (qx == hx)
+ break;
+ h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
+ }
+ if (!qx || cbdata->cflmap[2 * h + 1] != -1)
+ return;
+ if (!cbdata->lastdirhash)
+ cbdata->lastdirhash = strnhash(fn, dp - fn);
+ dirid = normalizedir(cbdata, fn, dp - fn, cbdata->lastdirhash, 1);
+ queue_push2(&cbdata->lookat, hx, idx);
+ queue_push2(&cbdata->lookat, cbdata->lastdirhash, isdir ? -dirid : dirid);
+}
+
+static void
+findfileconflicts2_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
+{
+ struct cbdata *cbdata = cbdatav;
+ Hashval hx;
+ const char *dp;
+ char md5padded[34];
+ Id off;
+
+ if (!info->dirlen)
+ return;
+ dp = fn + info->dirlen;
+ if (info->diridx != cbdata->lastdiridx)
+ {
+ cbdata->lastdiridx = info->diridx;
+ cbdata->lastdirhash = strnhash(fn, dp - fn);
+ }
+ if (cbdata->aliases)
+ {
+ if (cbdata->lastdirhash != cbdata->dirhash)
+ return;
+ hx = strhash(dp);
+ }
+ else
+ {
+ hx = cbdata->lastdirhash;
+ hx = strhash_cont(dp, hx);
+ }
+ if (!hx)
+ hx = strlen(fn) + 1;
+ if ((Id)hx != cbdata->hx)
+ return;
+ if (cbdata->dirid && cbdata->dirid != normalizedir(cbdata, fn, dp - fn, cbdata->dirhash, 0))
+ return;
+ strncpy(md5padded, info->digest, 32);
+ md5padded[32] = 0;
+ md5padded[33] = info->color;
+ /* printf("%d, hx %x -> %s %d %s\n", cbdata->idx, hx, fn, info->mode, info->digest); */
+ off = addfilesspace(cbdata, strlen(fn) + (34 + 1));
+ memcpy(cbdata->filesspace + off, (unsigned char *)md5padded, 34);
+ strcpy((char *)cbdata->filesspace + off + 34, fn);
+ queue_push(&cbdata->files, off);
+}
+
+static int
+lookat_idx_cmp(const void *ap, const void *bp, void *dp)
+{
+ const Id *a = ap, *b = bp;
+ unsigned int ahx, bhx;
+ if (a[1] - b[1] != 0) /* idx */
+ return a[1] - b[1];
+ if (a[3] - b[3] != 0) /* dirid */
+ return a[3] - b[3];
+ ahx = (unsigned int)a[0]; /* can be < 0 */
+ bhx = (unsigned int)b[0];
+ if (ahx != bhx)
+ return ahx < bhx ? -1 : 1;
+ ahx = (unsigned int)a[2]; /* dhx */
+ bhx = (unsigned int)b[2];
+ if (ahx != bhx)
+ return ahx < bhx ? -1 : 1;
+ return 0;
+}
+
+static int
+lookat_hx_cmp(const void *ap, const void *bp, void *dp)
+{
+ const Id *a = ap, *b = bp;
+ unsigned int ahx, bhx;
+ Id adirid, bdirid;
+ ahx = (unsigned int)a[0]; /* can be < 0 */
+ bhx = (unsigned int)b[0];
+ if (ahx != bhx)
+ return ahx < bhx ? -1 : 1;
+ adirid = a[3] < 0 ? -a[3] : a[3];
+ bdirid = b[3] < 0 ? -b[3] : b[3];
+ if (adirid - bdirid != 0) /* dirid */
+ return adirid - bdirid;
+ if (a[3] != b[3])
+ return a[3] > 0 ? -1 : 1; /* bring positive dirids to front */
+ if (a[1] - b[1] != 0) /* idx */
+ return a[1] - b[1];
+ ahx = (unsigned int)a[2]; /* dhx */
+ bhx = (unsigned int)b[2];
+ if (ahx != bhx)
+ return ahx < bhx ? -1 : 1;
+ return 0;
+}
+
+static int
+conflicts_cmp(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = dp;
+ const Id *a = ap;
+ const Id *b = bp;
+ if (a[0] != b[0]) /* filename1 */
+ return strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
+ if (a[3] != b[3]) /* filename2 */
+ return strcmp(pool_id2str(pool, a[3]), pool_id2str(pool, b[3]));
+ if (a[1] != b[1]) /* pkgid1 */
+ return a[1] - b[1];
+ if (a[4] != b[4]) /* pkgid2 */
+ return a[4] - b[4];
+ return 0;
+}
+
+static void
+iterate_solvable_dirs(Pool *pool, Id p, void (*cb)(void *, const char *, struct filelistinfo *), void *cbdata)
+{
+ Repodata *lastdata = 0;
+ Id lastdirid = -1;
+ Dataiterator di;
+
+ dataiterator_init(&di, pool, 0, p, SOLVABLE_FILELIST, 0, SEARCH_COMPLETE_FILELIST);
+ while (dataiterator_step(&di))
+ {
+ if (di.data == lastdata && di.kv.id == lastdirid)
+ continue;
+ lastdata = di.data;
+ lastdirid = di.kv.id;
+ cb(cbdata, repodata_dir2str(di.data, di.kv.id, ""), 0);
+ }
+ dataiterator_free(&di);
+}
+
+/* before calling the expensive findfileconflicts_cb we check if any of
+ * the files match. This only makes sense when cbdata->create is off.
+ */
+static int
+precheck_solvable_files(struct cbdata *cbdata, Pool *pool, Id p)
+{
+ Dataiterator di;
+ Id hx, qx;
+ Hashval h, hh;
+ int found = 0;
+ int aliases = cbdata->aliases;
+ unsigned int lastdirid = -1;
+ Hashval lastdirhash = 0;
+ int lastdirlen = 0;
+ int checkthisdir = 0;
+ Repodata *lastrepodata = 0;
+
+ dataiterator_init(&di, pool, 0, p, SOLVABLE_FILELIST, 0, SEARCH_COMPLETE_FILELIST);
+ while (dataiterator_step(&di))
+ {
+ if (aliases)
+ {
+ /* hash just the basename */
+ hx = strhash(di.kv.str);
+ if (!hx)
+ hx = strlen(di.kv.str) + 1;
+ }
+ else
+ {
+ /* hash the full path */
+ if (di.data != lastrepodata || di.kv.id != lastdirid)
+ {
+ const char *dir;
+ lastrepodata = di.data;
+ lastdirid = di.kv.id;
+ dir = repodata_dir2str(lastrepodata, lastdirid, "");
+ lastdirlen = strlen(dir);
+ lastdirhash = strhash(dir);
+ checkthisdir = isindirmap(cbdata, lastdirhash ? lastdirhash : lastdirlen + 1);
+ }
+ if (!checkthisdir)
+ continue;
+ hx = strhash_cont(di.kv.str, lastdirhash);
+ if (!hx)
+ hx = lastdirlen + strlen(di.kv.str) + 1;
+ }
+ h = hx & cbdata->cflmapn;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ qx = cbdata->cflmap[2 * h];
+ if (!qx)
+ break;
+ if (qx == hx)
+ {
+ found = 1;
+ break;
+ }
+ h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
+ }
+ if (found)
+ break;
+ }
+ dataiterator_free(&di);
+ return found;
+}
+
+
+int
+pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, int flags, void *(*handle_cb)(Pool *, Id, void *) , void *handle_cbdata)
+{
+ int i, j, cflmapn, idxmapset;
+ struct cbdata cbdata;
+ unsigned int now, start;
+ void *handle;
+ Repo *installed = pool->installed;
+ Id p;
+ int obsoleteusescolors = pool_get_flag(pool, POOL_FLAG_OBSOLETEUSESCOLORS);
+ int hdrfetches;
+
+ queue_empty(conflicts);
+ if (!pkgs->count)
+ return 0;
+
+ now = start = solv_timems(0);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "searching for file conflicts\n");
+ POOL_DEBUG(SOLV_DEBUG_STATS, "packages: %d, cutoff %d\n", pkgs->count, cutoff);
+
+ memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.aliases = flags & FINDFILECONFLICTS_CHECK_DIRALIASING;
+ cbdata.pool = pool;
+ if (cbdata.aliases && (flags & FINDFILECONFLICTS_USE_ROOTDIR) != 0)
+ {
+ cbdata.rootdir = pool_get_rootdir(pool);
+ if (cbdata.rootdir && !strcmp(cbdata.rootdir, "/"))
+ cbdata.rootdir = 0;
+ if (cbdata.rootdir)
+ cbdata.rootdirl = strlen(cbdata.rootdir);
+ if (!cbdata.rootdir)
+ cbdata.usestat = 1;
+ }
+ queue_init(&cbdata.lookat);
+ queue_init(&cbdata.lookat_dir);
+ map_init(&cbdata.idxmap, pkgs->count);
+
+ if (cutoff <= 0)
+ cutoff = pkgs->count;
+
+ /* avarage file list size: 200 files per package */
+ /* avarage dir count: 20 dirs per package */
+
+ /* first pass: scan dirs */
+ if (!cbdata.aliases)
+ {
+ hdrfetches = 0;
+ cflmapn = (cutoff + 3) * 64;
+ while ((cflmapn & (cflmapn - 1)) != 0)
+ cflmapn = cflmapn & (cflmapn - 1);
+ cbdata.dirmap = solv_calloc(cflmapn, 2 * sizeof(Id));
+ cbdata.dirmapn = cflmapn - 1; /* make it a mask */
+ cbdata.create = 1;
+ idxmapset = 0;
+ for (i = 0; i < pkgs->count; i++)
+ {
+ if (i == cutoff)
+ cbdata.create = 0;
+ cbdata.idx = i;
+ p = pkgs->elements[i];
+ if ((flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed)
+ {
+ if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed)
+ {
+ iterate_solvable_dirs(pool, p, finddirs_cb, &cbdata);
+ if (MAPTST(&cbdata.idxmap, i))
+ idxmapset++;
+ continue;
+ }
+ }
+ handle = (*handle_cb)(pool, p, handle_cbdata);
+ if (!handle)
+ continue;
+ hdrfetches++;
+ rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_ONLYDIRS, finddirs_cb, &cbdata);
+ if (MAPTST(&cbdata.idxmap, i))
+ idxmapset++;
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap size: %d, used %d\n", cbdata.dirmapn + 1, cbdata.dirmapused);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap memory usage: %d K\n", (cbdata.dirmapn + 1) * 2 * (int)sizeof(Id) / 1024);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap creation took %d ms\n", solv_timems(now));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "dir conflicts found: %d, idxmap %d of %d\n", cbdata.dirconflicts, idxmapset, pkgs->count);
+ }
+
+ /* second pass: scan files */
+ now = solv_timems(0);
+ cflmapn = (cutoff + 3) * 128;
+ while ((cflmapn & (cflmapn - 1)) != 0)
+ cflmapn = cflmapn & (cflmapn - 1);
+ cbdata.cflmap = solv_calloc(cflmapn, 2 * sizeof(Id));
+ cbdata.cflmapn = cflmapn - 1; /* make it a mask */
+ cbdata.create = 1;
+ hdrfetches = 0;
+ for (i = 0; i < pkgs->count; i++)
+ {
+ if (i == cutoff)
+ cbdata.create = 0;
+ if (!cbdata.aliases && !MAPTST(&cbdata.idxmap, i))
+ continue;
+ cbdata.idx = i;
+ p = pkgs->elements[i];
+ if (!cbdata.create && (flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed)
+ {
+ if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed)
+ if (!precheck_solvable_files(&cbdata, pool, p))
+ continue;
+ }
+ /* can't use FINDFILECONFLICTS_USE_SOLVABLEFILELIST because we have to know if
+ * the file is a directory or not */
+ handle = (*handle_cb)(pool, p, handle_cbdata);
+ if (!handle)
+ continue;
+ hdrfetches++;
+ cbdata.lastdiridx = -1;
+ rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_NOGHOSTS, cbdata.aliases ? findfileconflicts_basename_cb : findfileconflicts_cb, &cbdata);
+ }
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "filemap size: %d, used %d\n", cbdata.cflmapn + 1, cbdata.cflmapused);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "filemap memory usage: %d K\n", (cbdata.cflmapn + 1) * 2 * (int)sizeof(Id) / 1024);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "filemap creation took %d ms\n", solv_timems(now));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "lookat_dir size: %d\n", cbdata.lookat_dir.count);
+ queue_free(&cbdata.lookat_dir);
+
+ /* we need another pass for aliases */
+ if (cbdata.aliases)
+ {
+ now = solv_timems(0);
+ /* make sure the first offset is not zero */
+ addfilesspace(&cbdata, 1);
+ cflmapn = (cutoff + 3) * 16;
+ while ((cflmapn & (cflmapn - 1)) != 0)
+ cflmapn = cflmapn & (cflmapn - 1);
+ cbdata.normap = solv_calloc(cflmapn, 2 * sizeof(Id));
+ cbdata.normapn = cflmapn - 1; /* make it a mask */
+ if (cbdata.usestat)
+ {
+ cbdata.statmap = solv_calloc(cflmapn, 2 * sizeof(Id));
+ cbdata.statmapn = cflmapn - 1; /* make it a mask */
+ }
+ cbdata.create = 0;
+ hdrfetches = 0;
+ for (i = 0; i < pkgs->count; i++)
+ {
+ if (!MAPTST(&cbdata.idxmap, i))
+ continue;
+ p = pkgs->elements[i];
+ cbdata.idx = i;
+ /* can't use FINDFILECONFLICTS_USE_SOLVABLEFILELIST because we have to know if
+ * the file is a directory or not */
+ handle = (*handle_cb)(pool, p, handle_cbdata);
+ if (!handle)
+ continue;
+ hdrfetches++;
+ cbdata.lastdiridx = -1;
+ rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_NOGHOSTS, findfileconflicts_alias_cb, &cbdata);
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "normap size: %d, used %d\n", cbdata.normapn + 1, cbdata.normapused);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "normap memory usage: %d K\n", (cbdata.normapn + 1) * 2 * (int)sizeof(Id) / 1024);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "stats made: %d\n", cbdata.statsmade);
+ if (cbdata.usestat)
+ {
+ POOL_DEBUG(SOLV_DEBUG_STATS, "statmap size: %d, used %d\n", cbdata.statmapn + 1, cbdata.statmapused);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "statmap memory usage: %d K\n", (cbdata.statmapn + 1) * 2 * (int)sizeof(Id) / 1024);
+ }
+ cbdata.statmap = solv_free(cbdata.statmap);
+ cbdata.statmapn = 0;
+ cbdata.canonspace = solv_free(cbdata.canonspace);
+ cbdata.canonspacen = 0;
+ POOL_DEBUG(SOLV_DEBUG_STATS, "alias processing took %d ms\n", solv_timems(now));
+ }
+
+ cbdata.dirmap = solv_free(cbdata.dirmap);
+ cbdata.dirmapn = 0;
+ cbdata.dirmapused = 0;
+ cbdata.cflmap = solv_free(cbdata.cflmap);
+ cbdata.cflmapn = 0;
+ cbdata.cflmapused = 0;
+
+ map_free(&cbdata.idxmap);
+
+ /* sort and unify/prune */
+ now = solv_timems(0);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "raw candidates: %d, pruning\n", cbdata.lookat.count / 4);
+ solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_hx_cmp, pool);
+ for (i = j = 0; i < cbdata.lookat.count; )
+ {
+ int first = 1;
+ Id hx = cbdata.lookat.elements[i];
+ Id idx = cbdata.lookat.elements[i + 1];
+ Id dhx = cbdata.lookat.elements[i + 2];
+ Id dirid = cbdata.lookat.elements[i + 3];
+ i += 4;
+ for (; i < cbdata.lookat.count && hx == cbdata.lookat.elements[i] && (dirid == cbdata.lookat.elements[i + 3] || dirid == -cbdata.lookat.elements[i + 3]); i += 4)
+ {
+ if (idx == cbdata.lookat.elements[i + 1] && dhx == cbdata.lookat.elements[i + 2])
+ continue; /* ignore duplicates */
+ if (first)
+ {
+ if (dirid < 0)
+ continue; /* all have a neg dirid */
+ cbdata.lookat.elements[j++] = hx;
+ cbdata.lookat.elements[j++] = idx;
+ cbdata.lookat.elements[j++] = dhx;
+ cbdata.lookat.elements[j++] = dirid;
+ first = 0;
+ }
+ idx = cbdata.lookat.elements[i + 1];
+ dhx = cbdata.lookat.elements[i + 2];
+ cbdata.lookat.elements[j++] = hx;
+ cbdata.lookat.elements[j++] = idx;
+ cbdata.lookat.elements[j++] = dhx;
+ cbdata.lookat.elements[j++] = dirid;
+ }
+ }
+ queue_truncate(&cbdata.lookat, j);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "candidates now: %d\n", cbdata.lookat.count / 4);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "pruning took %d ms\n", solv_timems(now));
+
+ /* third pass: collect file info for all files that match a hx */
+ now = solv_timems(0);
+ solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_idx_cmp, pool);
+ queue_init(&cbdata.files);
+ hdrfetches = 0;
+ for (i = 0; i < cbdata.lookat.count; i += 4)
+ {
+ Id idx = cbdata.lookat.elements[i + 1];
+ int iterflags = RPM_ITERATE_FILELIST_WITHMD5 | RPM_ITERATE_FILELIST_NOGHOSTS;
+ if (obsoleteusescolors)
+ iterflags |= RPM_ITERATE_FILELIST_WITHCOL;
+ p = pkgs->elements[idx];
+ handle = (*handle_cb)(pool, p, handle_cbdata);
+ if (handle)
+ hdrfetches++;
+ for (;; i += 4)
+ {
+ int fstart = cbdata.files.count;
+ queue_push(&cbdata.files, idx);
+ queue_push(&cbdata.files, 0);
+ cbdata.idx = idx;
+ cbdata.hx = cbdata.lookat.elements[i];
+ cbdata.dirhash = cbdata.lookat.elements[i + 2];
+ cbdata.dirid = cbdata.lookat.elements[i + 3];
+ cbdata.lastdiridx = -1;
+ if (handle)
+ rpm_iterate_filelist(handle, iterflags, findfileconflicts2_cb, &cbdata);
+ cbdata.files.elements[fstart + 1] = cbdata.files.count;
+ cbdata.lookat.elements[i + 1] = fstart;
+ if (i + 4 >= cbdata.lookat.count || cbdata.lookat.elements[i + 4 + 1] != idx)
+ break;
+ }
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "file info fetching took %d ms\n", solv_timems(now));
+
+ cbdata.normap = solv_free(cbdata.normap);
+ cbdata.normapn = 0;
+
+ /* forth pass: for each hx we have, compare all matching files against all other matching files */
+ now = solv_timems(0);
+ solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_hx_cmp, pool);
+ for (i = 0; i < cbdata.lookat.count - 4; i += 4)
+ {
+ Id hx = cbdata.lookat.elements[i];
+ Id pstart = cbdata.lookat.elements[i + 1];
+ Id dirid = cbdata.lookat.elements[i + 3];
+ Id pidx = cbdata.files.elements[pstart];
+ Id pend = cbdata.files.elements[pstart + 1];
+ if (cbdata.lookat.elements[i + 4] != hx)
+ continue; /* no package left with that hx */
+ for (j = i + 4; j < cbdata.lookat.count && cbdata.lookat.elements[j] == hx && cbdata.lookat.elements[j + 3] == dirid; j += 4)
+ {
+ Id qstart = cbdata.lookat.elements[j + 1];
+ Id qidx = cbdata.files.elements[qstart];
+ Id qend = cbdata.files.elements[qstart + 1];
+ int ii, jj;
+ if (pidx >= cutoff && qidx >= cutoff)
+ continue; /* no conflicts between packages with idx >= cutoff */
+ for (ii = pstart + 2; ii < pend; ii++)
+ for (jj = qstart + 2; jj < qend; jj++)
+ {
+ char *fsi = (char *)cbdata.filesspace + cbdata.files.elements[ii];
+ char *fsj = (char *)cbdata.filesspace + cbdata.files.elements[jj];
+ if (cbdata.aliases)
+ {
+ /* compare just the basenames, the dirs match because of the dirid */
+ char *bsi = strrchr(fsi + 34, '/');
+ char *bsj = strrchr(fsj + 34, '/');
+ if (!bsi || !bsj)
+ continue;
+ if (strcmp(bsi, bsj))
+ continue; /* different base names */
+ }
+ else
+ {
+ if (strcmp(fsi + 34, fsj + 34))
+ continue; /* different file names */
+ }
+ if (!strcmp(fsi, fsj))
+ continue; /* file digests match, no conflict */
+ if (obsoleteusescolors && fsi[33] && fsj[33] && (fsi[33] & fsj[33]) == 0)
+ continue; /* colors do not conflict */
+ queue_push(conflicts, pool_str2id(pool, fsi + 34, 1));
+ queue_push(conflicts, pkgs->elements[pidx]);
+ queue_push(conflicts, pool_str2id(pool, fsi, 1));
+ queue_push(conflicts, pool_str2id(pool, fsj + 34, 1));
+ queue_push(conflicts, pkgs->elements[qidx]);
+ queue_push(conflicts, pool_str2id(pool, fsj, 1));
+ }
+ }
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "filespace size: %d K\n", cbdata.filesspacen / 1024);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "candidate check took %d ms\n", solv_timems(now));
+ cbdata.filesspace = solv_free(cbdata.filesspace);
+ cbdata.filesspacen = 0;
+ queue_free(&cbdata.lookat);
+ queue_free(&cbdata.files);
+ if (conflicts->count > 6)
+ solv_sort(conflicts->elements, conflicts->count / 6, 6 * sizeof(Id), conflicts_cmp, pool);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file conflicts\n", conflicts->count / 6);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "file conflict detection took %d ms\n", solv_timems(start));
+
+ return conflicts->count / 6;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2009-2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef POOL_FILECONFLICTS_H
+#define POOL_FILECONFLICTS_H
+
+#include "pool.h"
+
+extern int pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, int flags, void *(*handle_cb)(Pool *, Id, void *) , void *handle_cbdata);
+
+#define FINDFILECONFLICTS_USE_SOLVABLEFILELIST (1 << 0)
+#define FINDFILECONFLICTS_CHECK_DIRALIASING (1 << 1)
+#define FINDFILECONFLICTS_USE_ROOTDIR (1 << 2)
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2015, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* this is used by repo_rpmmd, repo_rpmdb, and repo_susetags */
+
+#include <stdio.h>
+
+#include "pool.h"
+#include "pool_parserpmrichdep.h"
+
+static struct RichOpComp {
+ const char *n;
+ int l;
+ Id fl;
+} RichOps[] = {
+ { "and", 3, REL_AND },
+ { "or", 2, REL_OR },
+ { "if", 2, REL_COND },
+ { "else", 4, REL_ELSE },
+ { NULL, 0, 0},
+};
+
+static Id
+parseRichDep(Pool *pool, const char **depp, Id chainfl)
+{
+ const char *p = *depp;
+ const char *n;
+ Id id, evr;
+ int fl, bl;
+ struct RichOpComp *op;
+
+ if (!chainfl && *p++ != '(')
+ return 0;
+ while (*p == ' ')
+ p++;
+ if (*p == ')')
+ return 0;
+ if (*p == '(')
+ {
+ id = parseRichDep(pool, &p, 0);
+ if (!id)
+ return 0;
+ }
+ else
+ {
+ n = p;
+ bl = 0;
+ while (*p && !(*p == ' ' || *p == ',' || (*p == ')' && bl-- <= 0)))
+ if (*p++ == '(')
+ bl++;
+ if (n == p)
+ return 0;
+ id = pool_strn2id(pool, n, p - n, 1);
+ while (*p == ' ')
+ p++;
+ if (*p)
+ {
+ fl = 0;
+ for (;; p++)
+ {
+ if (*p == '<')
+ fl |= REL_LT;
+ else if (*p == '=')
+ fl |= REL_EQ;
+ else if (*p == '>')
+ fl |= REL_GT;
+ else
+ break;
+ }
+ if (fl)
+ {
+ while (*p == ' ')
+ p++;
+ n = p;
+ bl = 0;
+ while (*p && !(*p == ' ' || *p == ',' || (*p == ')' && bl-- <= 0)))
+ if (*p++ == '(')
+ bl++;
+ if (p - n > 2 && n[0] == '0' && n[1] == ':')
+ n += 2; /* strip zero epoch */
+ if (n == p)
+ return 0;
+ id = pool_rel2id(pool, id, pool_strn2id(pool, n, p - n, 1), fl, 1);
+ }
+ }
+ }
+ while (*p == ' ')
+ p++;
+ if (!*p)
+ return 0;
+ if (*p == ')')
+ {
+ *depp = p + 1;
+ return id;
+ }
+ n = p;
+ while (*p && *p != ' ')
+ p++;
+ for (op = RichOps; op->n; op++)
+ if (p - n == op->l && !strncmp(n, op->n, op->l))
+ break;
+ fl = op->fl;
+ if (!fl)
+ return 0;
+ if (chainfl == REL_COND && fl == REL_ELSE)
+ chainfl = 0;
+ if (chainfl && fl != chainfl)
+ return 0;
+ evr = parseRichDep(pool, &p, fl);
+ if (!evr)
+ return 0;
+ *depp = p;
+ return pool_rel2id(pool, id, evr, fl, 1);
+}
+
+Id
+pool_parserpmrichdep(Pool *pool, const char *dep)
+{
+ Id id = parseRichDep(pool, &dep, 0);
+ if (id && *dep)
+ id = 0;
+ return id;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2014, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef POOL_PARSERPMRICHDEP_H
+#define POOL_PARSERPMRICHDEP_H
+
+#include "pool.h"
+
+extern Id pool_parserpmrichdep(Pool *pool, const char *dep);
+
+#endif
--- /dev/null
+/*
+ * repo_appdatadb.c
+ *
+ * Parses AppSteam Data files.
+ * See http://people.freedesktop.org/~hughsient/appdata/
+ *
+ *
+ * Copyright (c) 2013, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <dirent.h>
+#include <expat.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "repo_appdata.h"
+
+
+enum state {
+ STATE_START,
+ STATE_APPLICATION,
+ STATE_ID,
+ STATE_PKGNAME,
+ STATE_LICENCE,
+ STATE_NAME,
+ STATE_SUMMARY,
+ STATE_DESCRIPTION,
+ STATE_P,
+ STATE_UL,
+ STATE_UL_LI,
+ STATE_OL,
+ STATE_OL_LI,
+ STATE_URL,
+ STATE_GROUP,
+ STATE_KEYWORDS,
+ STATE_KEYWORD,
+ STATE_EXTENDS,
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+/* !! must be sorted by first column !! */
+static struct stateswitch stateswitches[] = {
+ { STATE_START, "applications", STATE_START, 0 },
+ { STATE_START, "components", STATE_START, 0 },
+ { STATE_START, "application", STATE_APPLICATION, 0 },
+ { STATE_START, "component", STATE_APPLICATION, 0 },
+ { STATE_APPLICATION, "id", STATE_ID, 1 },
+ { STATE_APPLICATION, "pkgname", STATE_PKGNAME, 1 },
+ { STATE_APPLICATION, "product_license", STATE_LICENCE, 1 },
+ { STATE_APPLICATION, "name", STATE_NAME, 1 },
+ { STATE_APPLICATION, "summary", STATE_SUMMARY, 1 },
+ { STATE_APPLICATION, "description", STATE_DESCRIPTION, 0 },
+ { STATE_APPLICATION, "url", STATE_URL, 1 },
+ { STATE_APPLICATION, "project_group", STATE_GROUP, 1 },
+ { STATE_APPLICATION, "keywords", STATE_KEYWORDS, 0 },
+ { STATE_APPLICATION, "extends", STATE_EXTENDS, 1 },
+ { STATE_DESCRIPTION, "p", STATE_P, 1 },
+ { STATE_DESCRIPTION, "ul", STATE_UL, 0 },
+ { STATE_DESCRIPTION, "ol", STATE_OL, 0 },
+ { STATE_UL, "li", STATE_UL_LI, 1 },
+ { STATE_OL, "li", STATE_OL_LI, 1 },
+ { STATE_KEYWORDS, "keyword", STATE_KEYWORD, 1 },
+ { NUMSTATES }
+};
+
+struct parsedata {
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+
+ Solvable *solvable;
+ Id handle;
+
+ char *description;
+ int licnt;
+ int skip_depth;
+ int flags;
+ char *desktop_file;
+ int havesummary;
+ const char *filename;
+ Queue *owners;
+};
+
+
+static inline const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ return 0;
+}
+
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ struct stateswitch *sw;
+ const char *type;
+
+#if 0
+ fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ pd->depth++;
+ if (!pd->swtab[pd->state]) /* no statetable -> no substates */
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#endif
+ return;
+ }
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
+ if (!strcmp(sw->ename, name))
+ break;
+
+ if (sw->from != pd->state)
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#endif
+ return;
+ }
+ pd->state = sw->to;
+ pd->docontent = sw->docontent;
+ pd->statedepth = pd->depth;
+ pd->lcontent = 0;
+ *pd->content = 0;
+
+ if (!pd->skip_depth && find_attr("xml:lang", atts))
+ pd->skip_depth = pd->depth;
+ if (pd->skip_depth)
+ {
+ pd->docontent = 0;
+ return;
+ }
+
+ switch(pd->state)
+ {
+ case STATE_APPLICATION:
+ s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+ pd->handle = s - pool->solvables;
+ pd->havesummary = 0;
+ type = find_attr("type", atts);
+ if (!type || !*type)
+ type = "desktop";
+ repodata_set_poolstr(pd->data, pd->handle, SOLVABLE_CATEGORY, type);
+ break;
+ case STATE_DESCRIPTION:
+ pd->description = solv_free(pd->description);
+ break;
+ case STATE_OL:
+ case STATE_UL:
+ pd->licnt = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+/* replace whitespace with one space/newline */
+/* also strip starting/ending whitespace */
+static void
+wsstrip(struct parsedata *pd)
+{
+ int i, j;
+ int ws = 0;
+ for (i = j = 0; pd->content[i]; i++)
+ {
+ if (pd->content[i] == ' ' || pd->content[i] == '\t' || pd->content[i] == '\n')
+ {
+ ws |= pd->content[i] == '\n' ? 2 : 1;
+ continue;
+ }
+ if (ws && j)
+ pd->content[j++] = (ws & 2) ? '\n' : ' ';
+ ws = 0;
+ pd->content[j++] = pd->content[i];
+ }
+ pd->content[j] = 0;
+ pd->lcontent = j;
+}
+
+/* indent all lines */
+static void
+indent(struct parsedata *pd, int il)
+{
+ int i, l;
+ for (l = 0; pd->content[l]; )
+ {
+ if (pd->content[l] == '\n')
+ {
+ l++;
+ continue;
+ }
+ if (pd->lcontent + il + 1 > pd->acontent)
+ {
+ pd->acontent = pd->lcontent + il + 256;
+ pd->content = realloc(pd->content, pd->acontent);
+ }
+ memmove(pd->content + l + il, pd->content + l, pd->lcontent - l + 1);
+ for (i = 0; i < il; i++)
+ pd->content[l + i] = ' ';
+ pd->lcontent += il;
+ while (pd->content[l] && pd->content[l] != '\n')
+ l++;
+ }
+}
+
+static void
+add_missing_tags_from_desktop_file(struct parsedata *pd, Solvable *s, const char *desktop_file)
+{
+ Pool *pool = pd->pool;
+ FILE *fp;
+ const char *filepath;
+ char buf[1024];
+ char *p, *p2, *p3;
+ int inde = 0;
+
+ filepath = pool_tmpjoin(pool, "/usr/share/applications/", desktop_file, 0);
+ if (pd->flags & REPO_USE_ROOTDIR)
+ filepath = pool_prepend_rootdir_tmp(pool, filepath);
+ if (!(fp = fopen(filepath, "r")))
+ return;
+ while (fgets(buf, sizeof(buf), fp) > 0)
+ {
+ int c, l = strlen(buf);
+ if (!l)
+ continue;
+ if (buf[l - 1] != '\n')
+ {
+ /* ignore overlong lines */
+ while ((c = getc(fp)) != EOF)
+ if (c == '\n')
+ break;
+ if (c == EOF)
+ break;
+ continue;
+ }
+ buf[--l] = 0;
+ while (l && (buf[l - 1] == ' ' || buf[l - 1] == '\t'))
+ buf[--l] = 0;
+ p = buf;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (!*p || *p == '#')
+ continue;
+ if (*p == '[')
+ inde = 0;
+ if (!strcmp(p, "[Desktop Entry]"))
+ {
+ inde = 1;
+ continue;
+ }
+ if (!inde)
+ continue;
+ p2 = strchr(p, '=');
+ if (!p2 || p2 == p)
+ continue;
+ *p2 = 0;
+ for (p3 = p2 - 1; *p3 == ' ' || *p3 == '\t'; p3--)
+ *p3 = 0;
+ p2++;
+ while (*p2 == ' ' || *p2 == '\t')
+ p2++;
+ if (!*p2)
+ continue;
+ if (!s->name && !strcmp(p, "Name"))
+ s->name = pool_str2id(pool, pool_tmpjoin(pool, "application:", p2, 0), 1);
+ else if (!pd->havesummary && !strcmp(p, "Comment"))
+ {
+ pd->havesummary = 1;
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_SUMMARY, p2);
+ }
+ else
+ continue;
+ if (s->name && pd->havesummary)
+ break; /* our work is done */
+ }
+ fclose(fp);
+}
+
+static char *
+guess_filename_from_id(Pool *pool, const char *id)
+{
+ int l = strlen(id);
+ char *r = pool_tmpjoin(pool, id, ".metainfo.xml", 0);
+ if (l > 8 && !strcmp(".desktop", id + l - 8))
+ strcpy(r + l - 8, ".appdata.xml");
+ else if (l > 4 && !strcmp(".ttf", id + l - 4))
+ strcpy(r + l - 4, ".metainfo.xml");
+ else if (l > 4 && !strcmp(".otf", id + l - 4))
+ strcpy(r + l - 4, ".metainfo.xml");
+ else if (l > 4 && !strcmp(".xml", id + l - 4))
+ strcpy(r + l - 4, ".metainfo.xml");
+ else if (l > 3 && !strcmp(".db", id + l - 3))
+ strcpy(r + l - 3, ".metainfo.xml");
+ else
+ return 0;
+ return r;
+}
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ Id id;
+
+#if 0
+ fprintf(stderr, "end: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+#if 0
+ fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+#endif
+ return;
+ }
+
+ pd->depth--;
+ pd->statedepth--;
+
+ if (pd->skip_depth && pd->depth + 1 >= pd->skip_depth)
+ {
+ if (pd->depth + 1 == pd->skip_depth)
+ pd->skip_depth = 0;
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+ return;
+ }
+ pd->skip_depth = 0;
+
+ switch (pd->state)
+ {
+ case STATE_APPLICATION:
+ if (!s->arch)
+ s->arch = ARCH_NOARCH;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ if ((!s->name || !pd->havesummary) && (pd->flags & APPDATA_CHECK_DESKTOP_FILE) != 0 && pd->desktop_file)
+ add_missing_tags_from_desktop_file(pd, s, pd->desktop_file);
+ if (!s->name && pd->desktop_file)
+ {
+ char *name = pool_tmpjoin(pool, "application:", pd->desktop_file, 0);
+ int l = strlen(name);
+ if (l > 8 && !strcmp(".desktop", name + l - 8))
+ l -= 8;
+ s->name = pool_strn2id(pool, name, l, 1);
+ }
+ if (!s->requires && pd->owners)
+ {
+ int i;
+ Id id;
+ for (i = 0; i < pd->owners->count; i++)
+ {
+ Solvable *os = pd->pool->solvables + pd->owners->elements[i];
+ s->requires = repo_addid_dep(pd->repo, s->requires, os->name, 0);
+ id = pool_str2id(pd->pool, pool_tmpjoin(pd->pool, "application-appdata(", pool_id2str(pd->pool, os->name), ")"), 1);
+ s->provides = repo_addid_dep(pd->repo, s->provides, id, 0);
+ }
+ }
+ if (!s->requires && (pd->desktop_file || pd->filename))
+ {
+ /* add appdata() link requires/provides */
+ const char *filename = pd->filename;
+ if (!filename)
+ filename = guess_filename_from_id(pool, pd->desktop_file);
+ if (filename)
+ {
+ filename = pool_tmpjoin(pool, "application-appdata(", filename, ")");
+ s->requires = repo_addid_dep(pd->repo, s->requires, pool_str2id(pd->pool, filename + 12, 1), 0);
+ s->provides = repo_addid_dep(pd->repo, s->provides, pool_str2id(pd->pool, filename, 1), 0);
+ }
+ }
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
+ pd->solvable = 0;
+ pd->desktop_file = solv_free(pd->desktop_file);
+ break;
+ case STATE_ID:
+ pd->desktop_file = solv_strdup(pd->content);
+ break;
+ case STATE_NAME:
+ s->name = pool_str2id(pd->pool, pool_tmpjoin(pool, "application:", pd->content, 0), 1);
+ break;
+ case STATE_LICENCE:
+ repodata_add_poolstr_array(pd->data, pd->handle, SOLVABLE_LICENSE, pd->content);
+ break;
+ case STATE_SUMMARY:
+ pd->havesummary = 1;
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_SUMMARY, pd->content);
+ break;
+ case STATE_URL:
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_URL, pd->content);
+ break;
+ case STATE_GROUP:
+ repodata_add_poolstr_array(pd->data, pd->handle, SOLVABLE_GROUP, pd->content);
+ break;
+ case STATE_EXTENDS:
+ repodata_add_poolstr_array(pd->data, pd->handle, SOLVABLE_EXTENDS, pd->content);
+ break;
+ case STATE_DESCRIPTION:
+ if (pd->description)
+ {
+ /* strip trailing newlines */
+ int l = strlen(pd->description);
+ while (l && pd->description[l - 1] == '\n')
+ pd->description[--l] = 0;
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_DESCRIPTION, pd->description);
+ }
+ break;
+ case STATE_P:
+ wsstrip(pd);
+ pd->description = solv_dupappend(pd->description, pd->content, "\n\n");
+ break;
+ case STATE_UL_LI:
+ wsstrip(pd);
+ indent(pd, 4);
+ pd->content[2] = '-';
+ pd->description = solv_dupappend(pd->description, pd->content, "\n");
+ break;
+ case STATE_OL_LI:
+ wsstrip(pd);
+ indent(pd, 4);
+ if (++pd->licnt >= 10)
+ pd->content[0] = '0' + (pd->licnt / 10) % 10;
+ pd->content[1] = '0' + pd->licnt % 10;
+ pd->content[2] = '.';
+ pd->description = solv_dupappend(pd->description, pd->content, "\n");
+ break;
+ case STATE_UL:
+ case STATE_OL:
+ pd->description = solv_dupappend(pd->description, "\n", 0);
+ break;
+ case STATE_PKGNAME:
+ id = pool_str2id(pd->pool, pd->content, 1);
+ s->requires = repo_addid_dep(pd->repo, s->requires, id, 0);
+ id = pool_str2id(pd->pool, pool_tmpjoin(pd->pool, "application-appdata(", pd->content, ")"), 1);
+ s->provides = repo_addid_dep(pd->repo, s->provides, id, 0);
+ break;
+ case STATE_KEYWORD:
+ repodata_add_poolstr_array(pd->data, pd->handle, SOLVABLE_KEYWORDS, pd->content);
+ break;
+ default:
+ break;
+ }
+
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+
+#if 0
+ fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
+#endif
+}
+
+
+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->acontent = l + 256;
+ pd->content = realloc(pd->content, pd->acontent);
+ }
+ c = pd->content + pd->lcontent;
+ pd->lcontent += len;
+ while (len-- > 0)
+ *c++ = *s++;
+ *c = 0;
+}
+
+#define BUFF_SIZE 8192
+
+static int
+repo_add_appdata_fn(Repo *repo, FILE *fp, int flags, const char *filename, Queue *owners)
+{
+ Pool *pool = repo->pool;
+ struct parsedata pd;
+ struct stateswitch *sw;
+ Repodata *data;
+ char buf[BUFF_SIZE];
+ int i, l;
+ int ret = 0;
+
+ data = repo_add_repodata(repo, flags);
+ memset(&pd, 0, sizeof(pd));
+ pd.repo = repo;
+ pd.pool = repo->pool;
+ pd.data = data;
+ pd.flags = flags;
+ pd.filename = filename;
+ pd.owners = owners;
+
+ pd.content = malloc(256);
+ pd.acontent = 256;
+
+ 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;
+ }
+
+ 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)
+ {
+ pool_error(pool, -1, "repo_appdata: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ if (pd.solvable)
+ {
+ repo_free_solvable(repo, pd.solvable - pd.pool->solvables, 1);
+ pd.solvable = 0;
+ }
+ ret = -1;
+ break;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+
+ solv_free(pd.content);
+ solv_free(pd.desktop_file);
+ solv_free(pd.description);
+ return ret;
+}
+
+int
+repo_add_appdata(Repo *repo, FILE *fp, int flags)
+{
+ return repo_add_appdata_fn(repo, fp, flags, 0, 0);
+}
+
+static void
+search_uninternalized_filelist(Repo *repo, const char *dir, Queue *res)
+{
+ Pool *pool = repo->pool;
+ Id rdid, p;
+ Id iter, did, idid;
+
+ for (rdid = 1; rdid < repo->nrepodata; rdid++)
+ {
+ Repodata *data = repo_id2repodata(repo, rdid);
+ if (!data)
+ continue;
+ if (data->state == REPODATA_STUB)
+ continue;
+ if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
+ continue;
+ did = repodata_str2dir(data, dir, 0);
+ if (!did)
+ continue;
+ for (p = data->start; p < data->end; p++)
+ {
+ if (p >= pool->nsolvables)
+ continue;
+ if (pool->solvables[p].repo != repo)
+ continue;
+ iter = 0;
+ for (;;)
+ {
+ const char *str;
+ int l;
+ Id id;
+ idid = did;
+ str = repodata_lookup_dirstrarray_uninternalized(data, p, SOLVABLE_FILELIST, &idid, &iter);
+ if (!iter)
+ break;
+ l = strlen(str);
+ if (l > 12 && strncmp(str + l - 12, ".appdata.xml", 12))
+ id = pool_str2id(pool, str, 1);
+ else if (l > 13 && strncmp(str + l - 13, ".metainfo.xml", 13))
+ id = pool_str2id(pool, str, 1);
+ else
+ continue;
+ queue_push2(res, p, id);
+ }
+ }
+ }
+}
+
+/* add all files ending in .appdata.xml */
+int
+repo_add_appdata_dir(Repo *repo, const char *appdatadir, int flags)
+{
+ DIR *dir;
+ char *dirpath;
+ Repodata *data;
+ Queue flq;
+ Queue oq;
+
+ queue_init(&flq);
+ queue_init(&oq);
+ if (flags & APPDATA_SEARCH_UNINTERNALIZED_FILELIST)
+ search_uninternalized_filelist(repo, appdatadir, &flq);
+ data = repo_add_repodata(repo, flags);
+ if (flags & REPO_USE_ROOTDIR)
+ dirpath = pool_prepend_rootdir(repo->pool, appdatadir);
+ else
+ dirpath = solv_strdup(appdatadir);
+ if ((dir = opendir(dirpath)) != 0)
+ {
+ struct dirent *entry;
+ while ((entry = readdir(dir)))
+ {
+ const char *n;
+ FILE *fp;
+ int len = strlen(entry->d_name);
+ if (entry->d_name[0] == '.')
+ continue;
+ if (!(len > 12 && !strcmp(entry->d_name + len - 12, ".appdata.xml")) &&
+ !(len > 13 && !strcmp(entry->d_name + len - 13, ".metainfo.xml")))
+ continue;
+ n = pool_tmpjoin(repo->pool, dirpath, "/", entry->d_name);
+ fp = fopen(n, "r");
+ if (!fp)
+ {
+ pool_error(repo->pool, 0, "%s: %s", n, strerror(errno));
+ continue;
+ }
+ if (flags & APPDATA_SEARCH_UNINTERNALIZED_FILELIST)
+ {
+ Id id = pool_str2id(repo->pool, entry->d_name, 0);
+ queue_empty(&oq);
+ if (id)
+ {
+ int i;
+ for (i = 0; i < flq.count; i += 2)
+ if (flq.elements[i + 1] == id)
+ queue_push(&oq, flq.elements[i]);
+ }
+ }
+ repo_add_appdata_fn(repo, fp, flags | REPO_NO_INTERNALIZE | REPO_REUSE_REPODATA | APPDATA_CHECK_DESKTOP_FILE, entry->d_name, oq.count ? &oq : 0);
+ fclose(fp);
+ }
+ closedir(dir);
+ }
+ solv_free(dirpath);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ queue_free(&oq);
+ queue_free(&flq);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+int repo_add_appdata(Repo *repo, FILE *fp, int flags);
+int repo_add_appdata_dir(Repo *repo, const char *appdatadir, int flags);
+
+#define APPDATA_SEARCH_UNINTERNALIZED_FILELIST (1 << 8)
+#define APPDATA_CHECK_DESKTOP_FILE (1 << 30) /* internal */
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "chksum.h"
+#include "solv_xfopen.h"
+#include "repo_arch.h"
+
+static long long parsenum(unsigned char *p, int cnt)
+{
+ long long x = 0;
+ if (!cnt)
+ return -1;
+ if (*p & 0x80)
+ {
+ /* binary format */
+ x = *p & 0x40 ? (-1 << 8 | *p) : (*p ^ 0x80);
+ while (--cnt > 0)
+ x = (x << 8) | *p++;
+ return x;
+ }
+ while (cnt > 0 && (*p == ' ' || *p == '\t'))
+ cnt--, p++;
+ if (*p == '-')
+ return -1;
+ for (; cnt > 0 && *p >= '0' && *p < '8'; cnt--, p++)
+ x = (x << 3) | (*p - '0');
+ return x;
+}
+
+static int readblock(FILE *fp, unsigned char *blk)
+{
+ int r, l = 0;
+ while (l < 512)
+ {
+ r = fread(blk + l, 1, 512 - l, fp);
+ if (r <= 0)
+ return -1;
+ l += r;
+ }
+ return 0;
+}
+
+struct tarhead {
+ FILE *fp;
+ unsigned char blk[512];
+ int type;
+ long long length;
+ char *path;
+ int eof;
+ int ispax;
+ int off;
+ int end;
+};
+
+static char *getsentry(struct tarhead *th, char *s, int size)
+{
+ char *os = s;
+ if (th->eof || size <= 1)
+ return 0;
+ size--; /* terminating 0 */
+ for (;;)
+ {
+ int i;
+ for (i = th->off; i < th->end; i++)
+ {
+ *s++ = th->blk[i];
+ size--;
+ if (!size || th->blk[i] == '\n')
+ {
+ th->off = i + 1;
+ *s = 0;
+ return os;
+ }
+ }
+ th->off = i;
+ if (!th->path)
+ {
+ /* fake entry */
+ th->end = fread(th->blk, 1, 512, th->fp);
+ if (th->end <= 0)
+ {
+ th->eof = 1;
+ return 0;
+ }
+ th->off = 0;
+ continue;
+ }
+ if (th->length <= 0)
+ return 0;
+ if (readblock(th->fp, th->blk))
+ {
+ th->eof = 1;
+ return 0;
+ }
+ th->off = 0;
+ th->end = th->length > 512 ? 512 : th->length;
+ th->length -= th->end;
+ }
+}
+
+static void skipentry(struct tarhead *th)
+{
+ for (; th->length > 0; th->length -= 512)
+ {
+ if (readblock(th->fp, th->blk))
+ {
+ th->eof = 1;
+ th->length = 0;
+ return;
+ }
+ }
+ th->length = 0;
+ th->off = th->end = 0;
+}
+
+static void inittarhead(struct tarhead *th, FILE *fp)
+{
+ memset(th, 0, sizeof(*th));
+ th->fp = fp;
+}
+
+static void freetarhead(struct tarhead *th)
+{
+ solv_free(th->path);
+}
+
+static int gettarhead(struct tarhead *th)
+{
+ int l, type;
+ long long length;
+
+ th->path = solv_free(th->path);
+ th->ispax = 0;
+ th->type = 0;
+ th->length = 0;
+ th->off = 0;
+ th->end = 0;
+ if (th->eof)
+ return 0;
+ for (;;)
+ {
+ int r = readblock(th->fp, th->blk);
+ if (r)
+ {
+ if (feof(th->fp))
+ {
+ th->eof = 1;
+ return 0;
+ }
+ return -1;
+ }
+ if (th->blk[0] == 0)
+ {
+ th->eof = 1;
+ return 0;
+ }
+ length = parsenum(th->blk + 124, 12);
+ if (length < 0)
+ return -1;
+ type = 0;
+ switch (th->blk[156])
+ {
+ case 'S': case '0':
+ type = 1; /* file */
+ break;
+ case '1':
+ /* hard link, special length magic... */
+ if (!th->ispax)
+ length = 0;
+ break;
+ case '5':
+ type = 2; /* dir */
+ break;
+ case '2': case '3': case '4': case '6':
+ length = 0;
+ break;
+ case 'X': case 'x': case 'L':
+ {
+ char *data, *pp;
+ if (length < 1 || length >= 1024 * 1024)
+ return -1;
+ data = pp = solv_malloc(length + 512);
+ for (l = length; l > 0; l -= 512, pp += 512)
+ if (readblock(th->fp, (unsigned char *)pp))
+ {
+ solv_free(data);
+ return -1;
+ }
+ data[length] = 0;
+ type = 3; /* extension */
+ if (th->blk[156] == 'L')
+ {
+ solv_free(th->path);
+ th->path = data;
+ length = 0;
+ break;
+ }
+ pp = data;
+ while (length > 0)
+ {
+ int ll = 0;
+ for (l = 0; l < length && pp[l] >= '0' && pp[l] <= '9'; l++)
+ ll = ll * 10 + (pp[l] - '0');
+ if (l == length || pp[l] != ' ' || ll < 1 || ll > length || pp[ll - 1] != '\n')
+ {
+ solv_free(data);
+ return -1;
+ }
+ length -= ll;
+ pp += l + 1;
+ ll -= l + 1;
+ pp[ll - 1] = 0;
+ if (!strncmp(pp, "path=", 5))
+ {
+ solv_free(th->path);
+ th->path = solv_strdup(pp + 5);
+ }
+ pp += ll;
+ }
+ solv_free(data);
+ th->ispax = 1;
+ length = 0;
+ break;
+ }
+ default:
+ type = 3; /* extension */
+ break;
+ }
+ if ((type == 1 || type == 2) && !th->path)
+ {
+ char path[157];
+ memcpy(path, th->blk, 156);
+ path[156] = 0;
+ if (!memcmp(th->blk + 257, "ustar\0\060\060", 8) && !th->path && th->blk[345])
+ {
+ /* POSIX ustar with prefix */
+ char prefix[156];
+ memcpy(prefix, th->blk + 345, 155);
+ prefix[155] = 0;
+ l = strlen(prefix);
+ if (l && prefix[l - 1] == '/')
+ prefix[l - 1] = 0;
+ th->path = solv_dupjoin(prefix, "/", path);
+ }
+ else
+ th->path = solv_dupjoin(path, 0, 0);
+ }
+ if (type == 1 || type == 2)
+ {
+ l = strlen(th->path);
+ if (l && th->path[l - 1] == '/')
+ {
+ if (l > 1)
+ th->path[l - 1] = 0;
+ type = 2;
+ }
+ }
+ if (type != 3)
+ break;
+ while (length > 0)
+ {
+ r = readblock(th->fp, th->blk);
+ if (r)
+ return r;
+ length -= 512;
+ }
+ }
+ th->type = type;
+ th->length = length;
+ return 1;
+}
+
+static Offset
+adddep(Repo *repo, Offset olddeps, char *line)
+{
+ Pool *pool = repo->pool;
+ char *p;
+ Id id;
+
+ while (*line == ' ' || *line == '\t')
+ line++;
+ p = line;
+ while (*p && *p != ' ' && *p != '\t' && *p != '<' && *p != '=' && *p != '>')
+ p++;
+ id = pool_strn2id(pool, line, p - line, 1);
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '<' || *p == '=' || *p == '>')
+ {
+ int flags = 0;
+ for (;; p++)
+ {
+ if (*p == '<')
+ flags |= REL_LT;
+ else if (*p == '=')
+ flags |= REL_EQ;
+ else if (*p == '>')
+ flags |= REL_GT;
+ else
+ break;
+ }
+ while (*p == ' ' || *p == '\t')
+ p++;
+ line = p;
+ while (*p && *p != ' ' && *p != '\t')
+ p++;
+ id = pool_rel2id(pool, id, pool_strn2id(pool, line, p - line, 1), flags, 1);
+ }
+ return repo_addid_dep(repo, olddeps, id, 0);
+}
+
+Id
+repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ FILE *fp;
+ struct tarhead th;
+ char line[4096];
+ int ignoreline;
+ Solvable *s;
+ int l, fd;
+ struct stat stb;
+ Chksum *pkgidchk = 0;
+
+ data = repo_add_repodata(repo, flags);
+ if ((fd = open(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, fn) : fn, O_RDONLY, 0)) < 0)
+ {
+ pool_error(pool, -1, "%s: %s", fn, strerror(errno));
+ return 0;
+ }
+ if (fstat(fd, &stb))
+ {
+ pool_error(pool, -1, "%s: fstat: %s", fn, strerror(errno));
+ close(fd);
+ return 0;
+ }
+ if (!(fp = solv_xfopen_fd(fn, fd, "r")))
+ {
+ pool_error(pool, -1, "%s: fdopen failed", fn);
+ close(fd);
+ return 0;
+ }
+ s = 0;
+ inittarhead(&th, fp);
+ while (gettarhead(&th) > 0)
+ {
+ if (th.type != 1 || strcmp(th.path, ".PKGINFO") != 0)
+ {
+ skipentry(&th);
+ continue;
+ }
+ ignoreline = 0;
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ if (flags & ARCH_ADD_WITH_PKGID)
+ pkgidchk = solv_chksum_create(REPOKEY_TYPE_MD5);
+ while (getsentry(&th, line, sizeof(line)))
+ {
+ l = strlen(line);
+ if (l == 0)
+ continue;
+ if (pkgidchk)
+ solv_chksum_add(pkgidchk, line, l);
+ if (line[l - 1] != '\n')
+ {
+ ignoreline = 1;
+ continue;
+ }
+ if (ignoreline)
+ {
+ ignoreline = 0;
+ continue;
+ }
+ line[--l] = 0;
+ if (l == 0 || line[0] == '#')
+ continue;
+ if (!strncmp(line, "pkgname = ", 10))
+ s->name = pool_str2id(pool, line + 10, 1);
+ else if (!strncmp(line, "pkgver = ", 9))
+ s->evr = pool_str2id(pool, line + 9, 1);
+ else if (!strncmp(line, "pkgdesc = ", 10))
+ {
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 10);
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line + 10);
+ }
+ else if (!strncmp(line, "url = ", 6))
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line + 6);
+ else if (!strncmp(line, "builddate = ", 12))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line + 12, 0, 10));
+ else if (!strncmp(line, "packager = ", 11))
+ repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line + 11);
+ else if (!strncmp(line, "size = ", 7))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line + 7, 0, 10));
+ else if (!strncmp(line, "arch = ", 7))
+ s->arch = pool_str2id(pool, line + 7, 1);
+ else if (!strncmp(line, "license = ", 10))
+ repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line + 10);
+ else if (!strncmp(line, "replaces = ", 11))
+ s->obsoletes = adddep(repo, s->obsoletes, line + 11);
+ else if (!strncmp(line, "group = ", 8))
+ repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_GROUP, line + 8);
+ else if (!strncmp(line, "depend = ", 9))
+ s->requires = adddep(repo, s->requires, line + 9);
+ else if (!strncmp(line, "optdepend = ", 12))
+ {
+ char *p = strchr(line, ':');
+ if (p)
+ *p = 0;
+ s->suggests = adddep(repo, s->suggests, line + 12);
+ }
+ else if (!strncmp(line, "conflict = ", 11))
+ s->conflicts = adddep(repo, s->conflicts, line + 11);
+ else if (!strncmp(line, "provides = ", 11))
+ s->provides = adddep(repo, s->provides, line + 11);
+ }
+ break;
+ }
+ freetarhead(&th);
+ fclose(fp);
+ if (!s)
+ {
+ pool_error(pool, -1, "%s: not an arch package", fn);
+ if (pkgidchk)
+ solv_chksum_free(pkgidchk, 0);
+ return 0;
+ }
+ if (s && !s->name)
+ {
+ pool_error(pool, -1, "%s: package has no name", fn);
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ s = 0;
+ }
+ if (s)
+ {
+ if (!s->arch)
+ s->arch = ARCH_ANY;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ if (!(flags & REPO_NO_LOCATION))
+ repodata_set_location(data, s - pool->solvables, 0, 0, fn);
+ if (S_ISREG(stb.st_mode))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
+ if (pkgidchk)
+ {
+ unsigned char pkgid[16];
+ solv_chksum_free(pkgidchk, pkgid);
+ repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
+ pkgidchk = 0;
+ }
+ }
+ if (pkgidchk)
+ solv_chksum_free(pkgidchk, 0);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return s ? s - pool->solvables : 0;
+}
+
+static char *getsentrynl(struct tarhead *th, char *s, int size)
+{
+ int l;
+ if (!getsentry(th, s, size))
+ {
+ *s = 0; /* eof */
+ return 0;
+ }
+ l = strlen(s);
+ if (!l)
+ return 0;
+ if (l && s[l - 1] == '\n')
+ {
+ s[l - 1] = 0;
+ return s;
+ }
+ while (getsentry(th, s, size))
+ {
+ l = strlen(s);
+ if (!l || s[l - 1] == '\n')
+ return 0;
+ }
+ *s = 0; /* eof */
+ return 0;
+}
+
+static Hashtable
+joinhash_init(Repo *repo, Hashval *hmp)
+{
+ Hashval hm = mkmask(repo->nsolvables);
+ Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
+ Hashval h, hh;
+ Solvable *s;
+ int i;
+
+ FOR_REPO_SOLVABLES(repo, i, s)
+ {
+ hh = HASHCHAIN_START;
+ h = s->name & hm;
+ while (ht[h])
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ ht[h] = i;
+ }
+ *hmp = hm;
+ return ht;
+}
+
+static Solvable *
+joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, const char *fn)
+{
+ const char *p;
+ Id name, evr;
+ Hashval h, hh;
+
+ if ((p = strrchr(fn, '/')) != 0)
+ fn = p + 1;
+ /* here we assume that the dirname is name-evr */
+ if (!*fn)
+ return 0;
+ for (p = fn + strlen(fn) - 1; p > fn; p--)
+ {
+ while (p > fn && *p != '-')
+ p--;
+ if (p == fn)
+ return 0;
+ name = pool_strn2id(repo->pool, fn, p - fn, 0);
+ if (!name)
+ continue;
+ evr = pool_str2id(repo->pool, p + 1, 0);
+ if (!evr)
+ continue;
+ /* found valid name/evr combination, check hash */
+ hh = HASHCHAIN_START;
+ h = name & hm;
+ while (ht[h])
+ {
+ Solvable *s = repo->pool->solvables + ht[h];
+ if (s->name == name && s->evr == evr)
+ return s;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ }
+ return 0;
+}
+
+static void
+adddata(Repodata *data, Solvable *s, struct tarhead *th)
+{
+ Repo *repo = data->repo;
+ Pool *pool = repo->pool;
+ char line[4096];
+ int l;
+ int havesha256 = 0;
+
+ while (getsentry(th, line, sizeof(line)))
+ {
+ l = strlen(line);
+ if (l == 0 || line[l - 1] != '\n')
+ continue;
+ line[--l] = 0;
+ if (l <= 2 || line[0] != '%' || line[l - 1] != '%')
+ continue;
+ if (!strcmp(line, "%FILENAME%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ repodata_set_location(data, s - pool->solvables, 0, 0, line);
+ }
+ else if (!strcmp(line, "%NAME%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ s->name = pool_str2id(pool, line, 1);
+ }
+ else if (!strcmp(line, "%VERSION%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ s->evr = pool_str2id(pool, line, 1);
+ }
+ else if (!strcmp(line, "%DESC%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ {
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line);
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line);
+ }
+ }
+ else if (!strcmp(line, "%GROUPS%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_GROUP, line);
+ }
+ else if (!strcmp(line, "%CSIZE%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(line, 0, 10));
+ }
+ else if (!strcmp(line, "%ISIZE%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line, 0, 10));
+ }
+ else if (!strcmp(line, "%MD5SUM%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)) && !havesha256)
+ repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_MD5, line);
+ }
+ else if (!strcmp(line, "%SHA256SUM%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ {
+ repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, line);
+ havesha256 = 1;
+ }
+ }
+ else if (!strcmp(line, "%URL%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line);
+ }
+ else if (!strcmp(line, "%LICENSE%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line);
+ }
+ else if (!strcmp(line, "%ARCH%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ s->arch = pool_str2id(pool, line, 1);
+ }
+ else if (!strcmp(line, "%BUILDDATE%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line, 0, 10));
+ }
+ else if (!strcmp(line, "%PACKAGER%"))
+ {
+ if (getsentrynl(th, line, sizeof(line)))
+ repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line);
+ }
+ else if (!strcmp(line, "%REPLACES%"))
+ {
+ while (getsentrynl(th, line, sizeof(line)) && *line)
+ s->obsoletes = adddep(repo, s->obsoletes, line);
+ }
+ else if (!strcmp(line, "%DEPENDS%"))
+ {
+ while (getsentrynl(th, line, sizeof(line)) && *line)
+ s->requires = adddep(repo, s->requires, line);
+ }
+ else if (!strcmp(line, "%CONFLICTS%"))
+ {
+ while (getsentrynl(th, line, sizeof(line)) && *line)
+ s->conflicts = adddep(repo, s->conflicts, line);
+ }
+ else if (!strcmp(line, "%PROVIDES%"))
+ {
+ while (getsentrynl(th, line, sizeof(line)) && *line)
+ s->provides = adddep(repo, s->provides, line);
+ }
+ else if (!strcmp(line, "%OPTDEPENDS%"))
+ {
+ while (getsentrynl(th, line, sizeof(line)) && *line)
+ {
+ char *p = strchr(line, ':');
+ if (p && p > line)
+ *p = 0;
+ s->suggests = adddep(repo, s->suggests, line);
+ }
+ }
+ else if (!strcmp(line, "%FILES%"))
+ {
+ while (getsentrynl(th, line, sizeof(line)) && *line)
+ {
+ char *p;
+ Id id;
+ l = strlen(line);
+ if (l > 1 && line[l - 1] == '/')
+ line[--l] = 0; /* remove trailing slashes */
+ if ((p = strrchr(line , '/')) != 0)
+ {
+ *p++ = 0;
+ if (line[0] != '/') /* anchor */
+ {
+ char tmp = *p;
+ memmove(line + 1, line, p - 1 - line);
+ *line = '/';
+ *p = 0;
+ id = repodata_str2dir(data, line, 1);
+ *p = tmp;
+ }
+ else
+ id = repodata_str2dir(data, line, 1);
+ }
+ else
+ {
+ p = line;
+ id = 0;
+ }
+ if (!id)
+ id = repodata_str2dir(data, "/", 1);
+ repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, id, p);
+ }
+ }
+ while (*line)
+ getsentrynl(th, line, sizeof(line));
+ }
+}
+
+static void
+finishsolvable(Repo *repo, Solvable *s)
+{
+ Pool *pool = repo->pool;
+ if (!s)
+ return;
+ if (!s->name)
+ {
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ return;
+ }
+ if (!s->arch)
+ s->arch = ARCH_ANY;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+}
+
+int
+repo_add_arch_repo(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ struct tarhead th;
+ char *lastdn = 0;
+ int lastdnlen = 0;
+ Solvable *s = 0;
+ Hashtable joinhash = 0;
+ Hashval joinhashmask = 0;
+
+ data = repo_add_repodata(repo, flags);
+
+ if (flags & REPO_EXTEND_SOLVABLES)
+ joinhash = joinhash_init(repo, &joinhashmask);
+
+ inittarhead(&th, fp);
+ while (gettarhead(&th) > 0)
+ {
+ char *bn;
+ if (th.type != 1)
+ {
+ skipentry(&th);
+ continue;
+ }
+ bn = strrchr(th.path, '/');
+ if (!bn || (strcmp(bn + 1, "desc") != 0 && strcmp(bn + 1, "depends") != 0 && strcmp(bn + 1, "files") != 0))
+ {
+ skipentry(&th);
+ continue;
+ }
+ if ((flags & REPO_EXTEND_SOLVABLES) != 0 && (!strcmp(bn + 1, "desc") || !strcmp(bn + 1, "depends")))
+ {
+ skipentry(&th);
+ continue; /* skip those when we're extending */
+ }
+ if (!lastdn || (bn - th.path) != lastdnlen || strncmp(lastdn, th.path, lastdnlen) != 0)
+ {
+ finishsolvable(repo, s);
+ solv_free(lastdn);
+ lastdn = solv_strdup(th.path);
+ lastdnlen = bn - th.path;
+ lastdn[lastdnlen] = 0;
+ if (flags & REPO_EXTEND_SOLVABLES)
+ {
+ s = joinhash_lookup(repo, joinhash, joinhashmask, lastdn);
+ if (!s)
+ {
+ skipentry(&th);
+ continue;
+ }
+ }
+ else
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ }
+ adddata(data, s, &th);
+ }
+ finishsolvable(repo, s);
+ solv_free(joinhash);
+ solv_free(lastdn);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+int
+repo_add_arch_local(Repo *repo, const char *dir, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ DIR *dp;
+ struct dirent *de;
+ char *entrydir, *file;
+ FILE *fp;
+ Solvable *s;
+
+ data = repo_add_repodata(repo, flags);
+
+ if (flags & REPO_USE_ROOTDIR)
+ dir = pool_prepend_rootdir(pool, dir);
+ dp = opendir(dir);
+ if (dp)
+ {
+ while ((de = readdir(dp)) != 0)
+ {
+ if (!de->d_name[0] || de->d_name[0] == '.')
+ continue;
+ entrydir = solv_dupjoin(dir, "/", de->d_name);
+ file = pool_tmpjoin(repo->pool, entrydir, "/desc", 0);
+ s = 0;
+ if ((fp = fopen(file, "r")) != 0)
+ {
+ struct tarhead th;
+ inittarhead(&th, fp);
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ adddata(data, s, &th);
+ freetarhead(&th);
+ fclose(fp);
+ file = pool_tmpjoin(repo->pool, entrydir, "/files", 0);
+ if ((fp = fopen(file, "r")) != 0)
+ {
+ inittarhead(&th, fp);
+ adddata(data, s, &th);
+ freetarhead(&th);
+ fclose(fp);
+ }
+ }
+ solv_free(entrydir);
+ }
+ closedir(dp);
+ }
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ if (flags & REPO_USE_ROOTDIR)
+ solv_free((char *)dir);
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define ARCH_ADD_WITH_PKGID (1 << 8)
+
+extern Id repo_add_arch_pkg(Repo *repo, const char *fn, int flags);
+extern Id repo_add_arch_repo(Repo *repo, FILE *fp, int flags);
+extern Id repo_add_arch_local(Repo *repo, const char *dir, int flags);
+
--- /dev/null
+/*
+ * Copyright (c) 2013, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "repo_autopattern.h"
+
+static void
+unescape(char *p)
+{
+ char *q = p;
+ while (*p)
+ {
+ if (*p == '%' && p[1] && p[2])
+ {
+ int d1 = p[1], d2 = p[2];
+ if (d1 >= '0' && d1 <= '9')
+ d1 -= '0';
+ else if (d1 >= 'a' && d1 <= 'f')
+ d1 -= 'a' - 10;
+ else if (d1 >= 'A' && d1 <= 'F')
+ d1 -= 'A' - 10;
+ else
+ d1 = -1;
+ if (d2 >= '0' && d2 <= '9')
+ d2 -= '0';
+ else if (d2 >= 'a' && d2 <= 'f')
+ d2 -= 'a' - 10;
+ else if (d2 >= 'A' && d2 <= 'F')
+ d2 -= 'A' - 10;
+ else
+ d2 = -1;
+ if (d1 != -1 && d2 != -1)
+ {
+ *q++ = d1 << 4 | d2;
+ p += 3;
+ continue;
+ }
+ }
+ *q++ = *p++;
+ }
+ *q = 0;
+}
+
+static time_t
+datestr2timestamp(const char *date)
+{
+ const char *p;
+ struct tm tm;
+
+ if (!date || !*date)
+ return 0;
+ for (p = date; *p >= '0' && *p <= '9'; p++)
+ ;
+ if (!*p)
+ return atoi(date);
+ memset(&tm, 0, sizeof(tm));
+ p = strptime(date, "%F%T", &tm);
+ if (!p)
+ {
+ memset(&tm, 0, sizeof(tm));
+ p = strptime(date, "%F", &tm);
+ if (!p || *p)
+ return 0;
+ }
+ return timegm(&tm);
+}
+
+int
+repo_add_autopattern(Repo *repo, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data = 0;
+ Solvable *s, *s2;
+ Queue patq, patq2;
+ Queue prdq, prdq2;
+ Id p;
+ Id pattern_id, product_id;
+ Id autopattern_id = 0, autoproduct_id = 0;
+ int i, j;
+
+ queue_init(&patq);
+ queue_init(&patq2);
+ queue_init(&prdq);
+ queue_init(&prdq2);
+
+ if (repo == pool->installed)
+ flags |= ADD_NO_AUTOPRODUCTS; /* no auto products for installed repos */
+
+ pattern_id = pool_str2id(pool, "pattern()", 9);
+ product_id = pool_str2id(pool, "product()", 9);
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ const char *n = pool_id2str(pool, s->name);
+ if (*n == 'p')
+ {
+ if (!strncmp("pattern:", n, 8))
+ {
+ queue_push(&patq, p);
+ continue;
+ }
+ else if (!strncmp("product:", n, 8))
+ {
+ queue_push(&prdq, p);
+ continue;
+ }
+ }
+ if (s->provides)
+ {
+ Id prv, *prvp = repo->idarraydata + s->provides;
+ while ((prv = *prvp++) != 0) /* go through all provides */
+ if (ISRELDEP(prv))
+ {
+ Reldep *rd = GETRELDEP(pool, prv);
+ if (rd->flags != REL_EQ)
+ continue;
+ if (rd->name == pattern_id)
+ {
+ const char *evrstr = pool_id2str(pool, rd->evr);
+ if (evrstr[0] == '.') /* hack to allow provides that do not create a pattern */
+ continue;
+ if (patq2.count && patq2.elements[patq2.count - 2] == p)
+ {
+ /* hmm, two provides. choose by evrstr */
+ if (strcmp(evrstr, pool_id2str(pool, patq2.elements[patq2.count - 1])) >= 0)
+ continue;
+ patq2.count -= 2;
+ }
+ queue_push2(&patq2, p, rd->evr);
+ }
+ if (rd->name == product_id)
+ {
+ const char *evrstr = pool_id2str(pool, rd->evr);
+ if (prdq2.count && prdq2.elements[prdq2.count - 2] == p)
+ {
+ /* hmm, two provides. choose by evrstr */
+ if (strcmp(evrstr, pool_id2str(pool, prdq2.elements[prdq2.count - 1])) >= 0)
+ continue;
+ prdq2.count -= 2;
+ }
+ queue_push2(&prdq2, p, rd->evr);
+ }
+ }
+ }
+ }
+ for (i = 0; i < patq2.count; i += 2)
+ {
+ const char *pn = 0;
+ char *newname;
+ Id name, prv, *prvp;
+ const char *str;
+ unsigned long long num;
+
+ s = pool->solvables + patq2.elements[i];
+ /* construct new name */
+ newname = pool_tmpjoin(pool, "pattern:", pool_id2str(pool, patq2.elements[i + 1]), 0);
+ unescape(newname);
+ name = pool_str2id(pool, newname, 0);
+ if (name)
+ {
+ /* check if we already have that pattern */
+ for (j = 0; j < patq.count; j++)
+ {
+ s2 = pool->solvables + patq.elements[j];
+ if (s2->name == name && s2->arch == s->arch && s2->evr == s->evr)
+ break;
+ }
+ if (j < patq.count)
+ continue; /* yes, do not add again */
+ }
+ /* new pattern */
+ if (!name)
+ name = pool_str2id(pool, newname, 1);
+ if (!data)
+ {
+ repo_internalize(repo); /* to make that the lookups work */
+ data = repo_add_repodata(repo, flags);
+ }
+ s2 = pool_id2solvable(pool, repo_add_solvable(repo));
+ s = pool->solvables + patq2.elements[i]; /* re-calc pointer */
+ s2->name = name;
+ s2->arch = s->arch;
+ s2->evr = s->evr;
+ s2->vendor = s->vendor;
+ /* add link requires */
+ s2->requires = repo_addid_dep(repo, s2->requires, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1) , 0);
+ /* add autopattern provides */
+ if (!autopattern_id)
+ autopattern_id = pool_str2id(pool, "autopattern()", 1);
+ s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autopattern_id, s->name, REL_EQ, 1), 0);
+ /* add self provides */
+ s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
+ if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
+ repodata_set_num(data, s2 - pool->solvables, SOLVABLE_INSTALLTIME, num);
+ if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
+ repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
+ if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
+ if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
+ /* fill in stuff from provides */
+ prvp = repo->idarraydata + s->provides;
+ while ((prv = *prvp++) != 0) /* go through all provides */
+ {
+ Id evr = 0;
+ if (ISRELDEP(prv))
+ {
+ Reldep *rd = GETRELDEP(pool, prv);
+ if (rd->flags != REL_EQ)
+ continue;
+ prv = rd->name;
+ evr = rd->evr;
+ }
+ pn = pool_id2str(pool, prv);
+ if (strncmp("pattern-", pn, 8) != 0)
+ continue;
+ newname = 0;
+ if (evr)
+ {
+ newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
+ unescape(newname);
+ }
+ if (!strncmp(pn, "pattern-category(", 17) && evr)
+ {
+ char lang[9];
+ int l = strlen(pn);
+ Id langtag;
+ if (l > 17 + 9 || pn[l - 1] != ')')
+ continue;
+ strncpy(lang, pn + 17, l - 17 - 1);
+ lang[l - 17 - 1] = 0;
+ langtag = SOLVABLE_CATEGORY;
+ if (*lang && strcmp(lang, "en") != 0)
+ langtag = pool_id2langid(pool, SOLVABLE_CATEGORY, lang, 1);
+ if (newname[solv_validutf8(newname)] == 0)
+ repodata_set_str(data, s2 - pool->solvables, langtag, newname);
+ else
+ {
+ char *ustr = solv_latin1toutf8(newname);
+ repodata_set_str(data, s2 - pool->solvables, langtag, ustr);
+ solv_free(ustr);
+ }
+ }
+ else if (!strcmp(pn, "pattern-includes()") && evr)
+ repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_INCLUDES, pool_tmpjoin(pool, "pattern:", newname, 0));
+ else if (!strcmp(pn, "pattern-extends()") && evr)
+ repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_EXTENDS, pool_tmpjoin(pool, "pattern:", newname, 0));
+ else if (!strcmp(pn, "pattern-icon()") && evr)
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ICON, newname);
+ else if (!strcmp(pn, "pattern-order()") && evr)
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ORDER, newname);
+ else if (!strcmp(pn, "pattern-visible()"))
+ {
+ if (!evr)
+ repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
+ else
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE, newname);
+ }
+ }
+ }
+ queue_free(&patq);
+ queue_free(&patq2);
+
+ if ((flags & ADD_NO_AUTOPRODUCTS) != 0)
+ queue_empty(&prdq2);
+
+ for (i = 0; i < prdq2.count; i += 2)
+ {
+ const char *pn = 0;
+ char *newname;
+ Id name, evr = 0, prv, *prvp;
+ const char *str;
+ unsigned long long num;
+
+ s = pool->solvables + prdq2.elements[i];
+ /* construct new name */
+ newname = pool_tmpjoin(pool, "product(", pool_id2str(pool, prdq2.elements[i + 1]), ")");
+ unescape(newname);
+ name = pool_str2id(pool, newname, 0);
+ if (!name)
+ continue; /* must have it in provides! */
+ prvp = repo->idarraydata + s->provides;
+ while ((prv = *prvp++) != 0) /* go through all provides */
+ {
+ if (ISRELDEP(prv))
+ {
+ Reldep *rd = GETRELDEP(pool, prv);
+ if (rd->name == name && rd->flags == REL_EQ)
+ {
+ evr = rd->evr;
+ break;
+ }
+ }
+ }
+ if (!prv)
+ continue; /* not found in provides */
+ newname = pool_tmpjoin(pool, "product:", pool_id2str(pool, prdq2.elements[i + 1]), 0);
+ unescape(newname);
+ name = pool_str2id(pool, newname, 0);
+ if (name)
+ {
+ /* check if we already have that product */
+ for (j = 0; j < prdq.count; j++)
+ {
+ s2 = pool->solvables + prdq.elements[j];
+ if (s2->name == name && s2->arch == s->arch && s2->evr == evr)
+ break;
+ }
+ if (j < prdq.count)
+ continue; /* yes, do not add again */
+ }
+ /* new product */
+ if (!name)
+ name = pool_str2id(pool, newname, 1);
+ if (!data)
+ {
+ repo_internalize(repo); /* to make that the lookups work */
+ data = repo_add_repodata(repo, flags);
+ }
+ if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
+ continue; /* eek, not for installed packages, please! */
+ s2 = pool_id2solvable(pool, repo_add_solvable(repo));
+ s = pool->solvables + prdq2.elements[i]; /* re-calc pointer */
+ s2->name = name;
+ s2->arch = s->arch;
+ s2->evr = evr;
+ s2->vendor = s->vendor;
+ /* add link requires */
+ s2->requires = repo_addid_dep(repo, s2->requires, prv, 0);
+ if (!autoproduct_id)
+ autoproduct_id = pool_str2id(pool, "autoproduct()", 1);
+ s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autoproduct_id, s->name, REL_EQ, 1), 0);
+ /* add self provides */
+ s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
+ if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
+ repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
+ if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
+ if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
+ if ((str = solvable_lookup_str(s, SOLVABLE_DISTRIBUTION)) != 0)
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DISTRIBUTION, str);
+ /* fill in stuff from provides */
+ prvp = repo->idarraydata + s->provides;
+ while ((prv = *prvp++) != 0) /* go through all provides */
+ {
+ Id evr = 0;
+ if (ISRELDEP(prv))
+ {
+ Reldep *rd = GETRELDEP(pool, prv);
+ if (rd->flags != REL_EQ)
+ continue;
+ prv = rd->name;
+ evr = rd->evr;
+ }
+ pn = pool_id2str(pool, prv);
+ if (strncmp("product-", pn, 8) != 0)
+ continue;
+ newname = 0;
+ if (evr)
+ {
+ newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
+ unescape(newname);
+ }
+ if (!strcmp(pn, "product-label()") && evr)
+ repodata_set_str(data, s2 - pool->solvables, PRODUCT_SHORTLABEL, newname);
+ else if (!strcmp(pn, "product-register-target()") && evr)
+ repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_TARGET, newname);
+ else if (!strcmp(pn, "product-register-flavor()") && evr)
+ repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_FLAVOR, newname);
+ else if (!strcmp(pn, "product-type()") && evr)
+ repodata_set_str(data, s2 - pool->solvables, PRODUCT_TYPE, newname);
+ else if (!strcmp(pn, "product-cpeid()") && evr)
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_CPEID, newname);
+ else if (!strcmp(pn, "product-flags()") && evr)
+ repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_FLAGS, newname);
+ else if (!strcmp(pn, "product-updates-repoid()") && evr)
+ {
+ Id h = repodata_new_handle(data);
+ repodata_set_str(data, h, PRODUCT_UPDATES_REPOID, newname);
+ repodata_add_flexarray(data, s2 - pool->solvables, PRODUCT_UPDATES, h);
+ }
+ else if (!strcmp(pn, "product-endoflife()") && evr)
+ {
+ time_t t = datestr2timestamp(newname);
+ if (t)
+ repodata_set_num(data, s2 - pool->solvables, PRODUCT_ENDOFLIFE, t);
+ }
+ else if (!strncmp(pn, "product-url(", 12) && evr && pn[12] && pn[13] && strlen(pn + 12) < 32)
+ {
+ char type[34];
+ strcpy(type, pn + 12);
+ type[strlen(type) - 1] = 0; /* closing ) */
+ repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL_TYPE, type);
+ repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL, newname);
+ }
+ }
+ }
+ queue_free(&prdq);
+ queue_free(&prdq2);
+
+ if (data && !(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ else if (!data && !(flags & REPO_NO_INTERNALIZE))
+ repo_internalize(repo);
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2013, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define ADD_NO_AUTOPRODUCTS (1 << 8)
+
+extern int repo_add_autopattern(Repo *repo, int flags);
--- /dev/null
+/*
+ * repo_comps.c
+ *
+ * Parses RedHat comps format
+ *
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <dirent.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#define DISABLE_SPLIT
+#include "tools_util.h"
+#include "repo_comps.h"
+
+/*
+ * TODO:
+ *
+ * what's the difference between group/category?
+ * handle "default" and "langonly".
+ *
+ * maybe handle REL_COND in solver recommends handling?
+ */
+
+enum state {
+ STATE_START,
+ STATE_COMPS,
+ STATE_GROUP,
+ STATE_ID,
+ STATE_NAME,
+ STATE_DESCRIPTION,
+ STATE_DISPLAY_ORDER,
+ STATE_DEFAULT,
+ STATE_LANGONLY,
+ STATE_LANG_ONLY,
+ STATE_USERVISIBLE,
+ STATE_PACKAGELIST,
+ STATE_PACKAGEREQ,
+ STATE_CATEGORY,
+ STATE_CID,
+ STATE_CNAME,
+ STATE_CDESCRIPTION,
+ STATE_CDISPLAY_ORDER,
+ STATE_GROUPLIST,
+ STATE_GROUPID,
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+/* must be sorted by first column */
+static struct stateswitch stateswitches[] = {
+ { STATE_START, "comps", STATE_COMPS, 0 },
+ { STATE_COMPS, "group", STATE_GROUP, 0 },
+ { STATE_COMPS, "category", STATE_CATEGORY, 0 },
+ { STATE_GROUP, "id", STATE_ID, 1 },
+ { STATE_GROUP, "name", STATE_NAME, 1 },
+ { STATE_GROUP, "description", STATE_DESCRIPTION, 1 },
+ { STATE_GROUP, "uservisible", STATE_USERVISIBLE, 1 },
+ { STATE_GROUP, "display_order", STATE_DISPLAY_ORDER, 1 },
+ { STATE_GROUP, "default", STATE_DEFAULT, 1 },
+ { STATE_GROUP, "langonly", STATE_LANGONLY, 1 },
+ { STATE_GROUP, "lang_only", STATE_LANG_ONLY, 1 },
+ { STATE_GROUP, "packagelist", STATE_PACKAGELIST, 0 },
+ { STATE_PACKAGELIST, "packagereq", STATE_PACKAGEREQ, 1 },
+ { STATE_CATEGORY, "id", STATE_CID, 1 },
+ { STATE_CATEGORY, "name", STATE_CNAME, 1 },
+ { STATE_CATEGORY, "description", STATE_CDESCRIPTION, 1 },
+ { STATE_CATEGORY , "grouplist", STATE_GROUPLIST, 0 },
+ { STATE_CATEGORY , "display_order", STATE_CDISPLAY_ORDER, 1 },
+ { STATE_GROUPLIST, "groupid", STATE_GROUPID, 1 },
+ { NUMSTATES }
+};
+
+struct parsedata {
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+ const char *filename;
+ const char *basename;
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+ struct joindata jd;
+
+ const char *tmplang;
+ Id reqtype;
+ Id condreq;
+
+ Solvable *solvable;
+ Id handle;
+};
+
+
+/*
+ * find_attr
+ * find value for xml attribute
+ * I: txt, name of attribute
+ * I: atts, list of key/value attributes
+ * O: pointer to value of matching key, or NULL
+ *
+ */
+
+static inline const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ }
+ return 0;
+}
+
+
+/*
+ * XML callback: startElement
+ */
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ struct stateswitch *sw;
+
+#if 0
+ fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ pd->depth++;
+ if (!pd->swtab[pd->state]) /* no statetable -> no substates */
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#endif
+ return;
+ }
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
+ if (!strcmp(sw->ename, name))
+ break;
+
+ if (sw->from != pd->state)
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#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_GROUP:
+ case STATE_CATEGORY:
+ s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+ pd->handle = s - pool->solvables;
+ break;
+
+ case STATE_NAME:
+ case STATE_CNAME:
+ case STATE_DESCRIPTION:
+ case STATE_CDESCRIPTION:
+ pd->tmplang = join_dup(&pd->jd, find_attr("xml:lang", atts));
+ break;
+
+ case STATE_PACKAGEREQ:
+ {
+ const char *type = find_attr("type", atts);
+ pd->condreq = 0;
+ pd->reqtype = SOLVABLE_RECOMMENDS;
+ if (type && !strcmp(type, "conditional"))
+ {
+ const char *requires = find_attr("requires", atts);
+ if (requires && *requires)
+ pd->condreq = pool_str2id(pool, requires, 1);
+ }
+ else if (type && !strcmp(type, "mandatory"))
+ pd->reqtype = SOLVABLE_REQUIRES;
+ else if (type && !strcmp(type, "optional"))
+ pd->reqtype = SOLVABLE_SUGGESTS;
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ Solvable *s = pd->solvable;
+ Id id;
+
+#if 0
+ fprintf(stderr, "end: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+#if 0
+ fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+#endif
+ return;
+ }
+
+ pd->depth--;
+ pd->statedepth--;
+
+ switch (pd->state)
+ {
+ case STATE_GROUP:
+ case STATE_CATEGORY:
+ if (!s->arch)
+ s->arch = ARCH_NOARCH;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
+ pd->solvable = 0;
+ break;
+
+ case STATE_ID:
+ case STATE_CID:
+ s->name = pool_str2id(pd->pool, join2(&pd->jd, pd->state == STATE_ID ? "group" : "category", ":", pd->content), 1);
+ break;
+
+ case STATE_NAME:
+ case STATE_CNAME:
+ repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_SUMMARY, pd->tmplang, 1), pd->content);
+ break;
+
+ case STATE_DESCRIPTION:
+ case STATE_CDESCRIPTION:
+ repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_DESCRIPTION, pd->tmplang, 1), pd->content);
+ break;
+
+ case STATE_PACKAGEREQ:
+ id = pool_str2id(pd->pool, pd->content, 1);
+ if (pd->condreq)
+ id = pool_rel2id(pd->pool, id, pd->condreq, REL_COND, 1);
+ repo_add_idarray(pd->repo, pd->handle, pd->reqtype, id);
+ break;
+
+ case STATE_GROUPID:
+ id = pool_str2id(pd->pool, join2(&pd->jd, "group", ":", pd->content), 1);
+ s->requires = repo_addid_dep(pd->repo, s->requires, id, 0);
+ break;
+
+ case STATE_USERVISIBLE:
+ repodata_set_void(pd->data, pd->handle, SOLVABLE_ISVISIBLE);
+ break;
+
+ case STATE_DISPLAY_ORDER:
+ case STATE_CDISPLAY_ORDER:
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_ORDER, pd->content);
+ break;
+
+ case STATE_DEFAULT:
+ break;
+
+ case STATE_LANGONLY:
+ case STATE_LANG_ONLY:
+ break;
+
+ default:
+ break;
+ }
+
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+
+#if 0
+ fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
+#endif
+}
+
+
+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 = solv_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
+
+
+int
+repo_add_comps(Repo *repo, FILE *fp, int flags)
+{
+ Repodata *data;
+ struct parsedata pd;
+ char buf[BUFF_SIZE];
+ int i, l;
+ struct stateswitch *sw;
+ XML_Parser parser;
+
+ data = repo_add_repodata(repo, flags);
+
+ memset(&pd, 0, sizeof(pd));
+ pd.repo = repo;
+ pd.pool = repo->pool;
+ pd.data = data;
+
+ pd.content = solv_malloc(256);
+ pd.acontent = 256;
+
+ 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;
+ }
+
+ 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)
+ {
+ pool_debug(pd.pool, SOLV_ERROR, "%s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ break;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+
+ solv_free(pd.content);
+ join_freemem(&pd.jd);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_comps(Repo *repo, FILE *fp, int flags);
--- /dev/null
+/*
+ * repo_content.c
+ *
+ * Parses 'content' file into .solv
+ * A 'content' file is the repomd.xml of the susetags format
+ *
+ * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata/content for a description
+ * of the syntax
+ *
+ *
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "chksum.h"
+#include "repo_content.h"
+#define DISABLE_SPLIT
+#define DISABLE_JOIN2
+#include "tools_util.h"
+
+/* split off a word, return null terminated pointer to it.
+ * return NULL if there is no word left. */
+static char *
+splitword(char **lp)
+{
+ char *w, *l = *lp;
+
+ while (*l == ' ' || *l == '\t')
+ l++;
+ w = *l ? l : 0;
+ while (*l && *l != ' ' && *l != '\t')
+ l++;
+ if (*l)
+ *l++ = 0; /* terminate word */
+ while (*l == ' ' || *l == '\t')
+ l++; /* convenience: advance to next word */
+ *lp = l;
+ return w;
+}
+
+struct parsedata {
+ Repo *repo;
+ char *tmp;
+ int tmpl;
+
+ const char *tmpvers;
+ const char *tmprel;
+};
+
+/*
+ * dependency relations
+ */
+
+static char *flagtab[] = {
+ ">",
+ "=",
+ ">=",
+ "<",
+ "!=",
+ "<="
+};
+
+
+/*
+ * join up to three strings into one
+ */
+
+static char *
+join(struct parsedata *pd, const char *s1, const char *s2, const 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;
+ pd->tmp = solv_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);
+ }
+ *p = 0;
+ return pd->tmp;
+}
+
+
+/*
+ * add dependency to pool
+ * OBSOLETES product:SUSE_LINUX product:openSUSE < 11.0 package:openSUSE < 11.0
+ */
+
+static unsigned int
+adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
+{
+ char *name;
+ Id id;
+
+ while ((name = splitword(&line)) != 0)
+ {
+ /* Hack, as the content file adds 'package:' for package
+ dependencies sometimes. */
+ if (!strncmp (name, "package:", 8))
+ name += 8;
+ id = pool_str2id(pool, name, 1);
+ if (*line == '<' || *line == '>' || *line == '=') /* rel follows */
+ {
+ char *rel = splitword(&line);
+ char *evr = splitword(&line);
+ int flags;
+
+ if (!rel || !evr)
+ {
+ pool_debug(pool, SOLV_ERROR, "repo_content: bad relation '%s %s'\n", name, rel);
+ continue;
+ }
+ for (flags = 0; flags < 6; flags++)
+ if (!strcmp(rel, flagtab[flags]))
+ break;
+ if (flags == 6)
+ {
+ pool_debug(pool, SOLV_ERROR, "repo_content: unknown relation '%s'\n", rel);
+ continue;
+ }
+ id = pool_rel2id(pool, id, pool_str2id(pool, evr, 1), flags + 1, 1);
+ }
+ olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
+ }
+ return olddeps;
+}
+
+
+/*
+ * split value and add to pool
+ */
+
+static void
+add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
+{
+ char *str;
+
+ while ((str = splitword(&value)) != 0)
+ repodata_add_poolstr_array(data, handle, name, str);
+}
+
+/*
+ * split value and add to pool
+ */
+
+static void
+add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
+{
+ char *url;
+
+ while ((url = splitword(&value)) != 0)
+ {
+ repodata_add_poolstr_array(data, handle, PRODUCT_URL, url);
+ repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
+ }
+}
+
+
+
+/*
+ * add 'content' to repo
+ *
+ */
+
+int
+repo_add_content(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ char *line, *linep;
+ int aline;
+ Solvable *s;
+ struct parsedata pd;
+ Repodata *data;
+ Id handle = 0;
+ int contentstyle = 0;
+ char *descrdir = 0;
+ char *datadir = 0;
+ char *defvendor = 0;
+
+ int i = 0;
+ int res = 0;
+
+ /* architectures
+ we use the first architecture in BASEARCHS or noarch
+ for the product. At the end we create (clone) the product
+ for each one of the remaining architectures
+ we allow max 4 archs
+ */
+ unsigned int numotherarchs = 0;
+ Id *otherarchs = 0;
+
+ memset(&pd, 0, sizeof(pd));
+ line = solv_malloc(1024);
+ aline = 1024;
+
+ pd.repo = repo;
+ linep = line;
+ s = 0;
+
+ data = repo_add_repodata(repo, flags);
+
+ for (;;)
+ {
+ char *key, *value;
+
+ /* read line into big-enough buffer */
+ if (linep - line + 16 > aline)
+ {
+ aline = linep - line;
+ line = solv_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;
+ while ( --linep > line && ( linep[-1] == ' ' || linep[-1] == '\t' ) )
+ ; /* skip trailing ws */
+ *linep = 0;
+ linep = line;
+
+ /* expect "key value" lines */
+ value = line;
+ key = splitword(&value);
+
+ if (key)
+ {
+#if 0
+ fprintf (stderr, "key %s, value %s\n", key, value);
+#endif
+
+#define istag(x) (!strcmp (key, x))
+#define code10 (contentstyle == 10)
+#define code11 (contentstyle == 11)
+
+
+ if (istag ("CONTENTSTYLE"))
+ {
+ if (contentstyle)
+ pool_debug(pool, SOLV_ERROR, "repo_content: 'CONTENTSTYLE' must be first line of 'content'\n");
+ contentstyle = atoi(value);
+ continue;
+ }
+ if (!contentstyle)
+ contentstyle = 10;
+
+ /* repository tags */
+ /* we also replicate some of them into the product solvables
+ * to be backward compatible */
+
+ if (istag ("REPOID"))
+ {
+ repodata_add_poolstr_array(data, SOLVID_META, REPOSITORY_REPOID, value);
+ continue;
+ }
+ if (istag ("REPOKEYWORDS"))
+ {
+ add_multiple_strings(data, SOLVID_META, REPOSITORY_KEYWORDS, value);
+ continue;
+ }
+ if (istag ("DISTRO"))
+ {
+ Id dh = repodata_new_handle(data);
+ char *p;
+ /* like with createrepo --distro */
+ if ((p = strchr(value, ',')) != 0)
+ {
+ *p++ = 0;
+ if (*value)
+ repodata_set_poolstr(data, dh, REPOSITORY_PRODUCT_CPEID, value);
+ }
+ else
+ p = value;
+ if (*p)
+ repodata_set_str(data, dh, REPOSITORY_PRODUCT_LABEL, p);
+ repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DISTROS, dh);
+ continue;
+ }
+
+ if (istag ("DESCRDIR"))
+ {
+ if (descrdir)
+ free(descrdir);
+ else
+ repodata_set_str(data, SOLVID_META, SUSETAGS_DESCRDIR, value);
+ if (s)
+ repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, value);
+ descrdir = solv_strdup(value);
+ continue;
+ }
+ if (istag ("DATADIR"))
+ {
+ if (datadir)
+ free(datadir);
+ else
+ repodata_set_str(data, SOLVID_META, SUSETAGS_DATADIR, value);
+ if (s)
+ repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, value);
+ datadir = solv_strdup(value);
+ continue;
+ }
+ if (istag ("VENDOR"))
+ {
+ if (defvendor)
+ free(defvendor);
+ else
+ repodata_set_poolstr(data, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
+ if (s)
+ s->vendor = pool_str2id(pool, value, 1);
+ defvendor = solv_strdup(value);
+ continue;
+ }
+
+ if (istag ("META") || istag ("HASH") || istag ("KEY"))
+ {
+ char *checksumtype, *checksum;
+ Id fh, type;
+ int l;
+
+ if ((checksumtype = splitword(&value)) == 0)
+ continue;
+ if ((checksum = splitword(&value)) == 0)
+ continue;
+ if (!*value)
+ continue;
+ type = solv_chksum_str2type(checksumtype);
+ if (!type)
+ {
+ pool_error(pool, -1, "%s: unknown checksum type '%s'", value, checksumtype);
+ res = 1;
+ continue;
+ }
+ l = solv_chksum_len(type);
+ if (strlen(checksum) != 2 * l)
+ {
+ pool_error(pool, -1, "%s: invalid checksum length for %s", value, checksumtype);
+ res = 1;
+ continue;
+ }
+ fh = repodata_new_handle(data);
+ repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
+ repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
+ repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
+ repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
+ continue;
+ }
+
+ /* product tags */
+
+ if ((code10 && istag ("PRODUCT"))
+ || (code11 && istag ("NAME")))
+ {
+ if (s && !s->name)
+ {
+ /* this solvable was created without seeing a
+ PRODUCT entry, just set the name and continue */
+ s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
+ continue;
+ }
+ if (s)
+ {
+ /* finish old solvable */
+ if (!s->arch)
+ s->arch = ARCH_NOARCH;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ if (code10)
+ s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
+ }
+ /* create new solvable */
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ handle = s - pool->solvables;
+ s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
+ if (datadir)
+ repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, datadir);
+ if (descrdir)
+ repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
+ if (defvendor)
+ s->vendor = pool_str2id(pool, defvendor, 1);
+ continue;
+ }
+
+ /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
+ from here on. */
+ if (!s)
+ {
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ handle = s - pool->solvables;
+ }
+
+ if (istag ("VERSION"))
+ pd.tmpvers = solv_strdup(value);
+ else if (istag ("RELEASE"))
+ pd.tmprel = solv_strdup(value);
+ else if (code11 && istag ("DISTRIBUTION"))
+ repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
+ else if (istag ("UPDATEURLS"))
+ add_multiple_urls(data, handle, value, pool_str2id(pool, "update", 1));
+ else if (istag ("EXTRAURLS"))
+ add_multiple_urls(data, handle, value, pool_str2id(pool, "extra", 1));
+ else if (istag ("OPTIONALURLS"))
+ add_multiple_urls(data, handle, value, pool_str2id(pool, "optional", 1));
+ else if (istag ("RELNOTESURL"))
+ add_multiple_urls(data, handle, value, pool_str2id(pool, "releasenotes", 1));
+ else if (istag ("SHORTLABEL"))
+ repodata_set_str(data, s - pool->solvables, PRODUCT_SHORTLABEL, value);
+ else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, value);
+ else if (!strncmp (key, "LABEL.", 6))
+ repodata_set_str(data, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
+ else if (istag ("FLAGS"))
+ add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
+ else if (istag ("VENDOR")) /* actually already handled above */
+ s->vendor = pool_str2id(pool, value, 1);
+ else if (istag ("BASEARCHS"))
+ {
+ char *arch;
+
+ if ((arch = splitword(&value)) != 0)
+ {
+ s->arch = pool_str2id(pool, arch, 1);
+ while ((arch = splitword(&value)) != 0)
+ {
+ otherarchs = solv_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
+ otherarchs[numotherarchs++] = pool_str2id(pool, arch, 1);
+ }
+ }
+ }
+ if (!code10)
+ continue;
+
+ /*
+ * Every tag below is Code10 only
+ *
+ */
+
+ if (istag ("ARCH"))
+ /* Theoretically we want to have the best arch of the given
+ modifiers which still is compatible with the system
+ arch. We don't know the latter here, though. */
+ s->arch = ARCH_NOARCH;
+ else if (istag ("PREREQUIRES"))
+ s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
+ else if (istag ("REQUIRES"))
+ s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
+ else if (istag ("PROVIDES"))
+ s->provides = adddep(pool, &pd, s->provides, value, 0);
+ else if (istag ("CONFLICTS"))
+ s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
+ else if (istag ("OBSOLETES"))
+ s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
+ else if (istag ("RECOMMENDS"))
+ s->recommends = adddep(pool, &pd, s->recommends, value, 0);
+ else if (istag ("SUGGESTS"))
+ s->suggests = adddep(pool, &pd, s->suggests, value, 0);
+ else if (istag ("SUPPLEMENTS"))
+ s->supplements = adddep(pool, &pd, s->supplements, value, 0);
+ else if (istag ("ENHANCES"))
+ s->enhances = adddep(pool, &pd, s->enhances, value, 0);
+ /* FRESHENS doesn't seem to exist. */
+ else if (istag ("TYPE"))
+ repodata_set_str(data, s - pool->solvables, PRODUCT_TYPE, value);
+
+ /* XXX do something about LINGUAS and ARCH?
+ * <ma>: Don't think so. zypp does not use or propagate them.
+ */
+#undef istag
+ }
+ else
+ pool_debug(pool, SOLV_ERROR, "repo_content: malformed line: %s\n", line);
+ }
+
+ if (datadir)
+ free(datadir);
+ if (descrdir)
+ free(descrdir);
+ if (defvendor)
+ free(defvendor);
+
+ if (s && !s->name)
+ {
+ pool_debug(pool, SOLV_ERROR, "repo_content: 'content' incomplete, no product solvable created!\n");
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ s = 0;
+ }
+ if (s)
+ {
+ if (pd.tmprel)
+ s->evr = makeevr(pool, join(&pd, pd.tmpvers, "-", pd.tmprel));
+ else
+ s->evr = makeevr(pool, pd.tmpvers);
+ pd.tmpvers = solv_free((void *)pd.tmpvers);
+ pd.tmprel = solv_free((void *)pd.tmprel);
+
+ if (!s->arch)
+ s->arch = ARCH_NOARCH;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ if (code10)
+ s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
+
+ /* now for every other arch, clone the product except the architecture */
+ for (i = 0; i < numotherarchs; ++i)
+ {
+ Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
+ p->name = s->name;
+ p->evr = s->evr;
+ p->vendor = s->vendor;
+ p->arch = otherarchs[i];
+
+ /* self provides */
+ if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
+ p->provides = repo_addid_dep(repo, p->provides, pool_rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
+
+ /* now merge the attributes */
+ repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
+ }
+ }
+
+ if (pd.tmp)
+ solv_free(pd.tmp);
+ solv_free(line);
+ solv_free(otherarchs);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return res;
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_content(Repo *repo, FILE *fp, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <zlib.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "chksum.h"
+#include "solver.h"
+#include "repo_cudf.h"
+
+static Id
+parseonedep(Pool *pool, char *p)
+{
+ char *n, *ne, *e, *ee;
+ Id name, evr;
+ int flags;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ if (!*p)
+ return 0;
+ if (!strcmp(p, "!true"))
+ return 0;
+ if (!strcmp(p, "!false"))
+ return pool_str2id(pool, p, 1);
+ n = p;
+ /* find end of name */
+ while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '|')
+ p++;
+ ne = p;
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ evr = 0;
+ flags = 0;
+ e = ee = 0;
+ if (*p == '>' || *p == '<' || *p == '=' || *p == '!')
+ {
+ if (*p == '>')
+ flags |= REL_GT;
+ else if (*p == '=')
+ flags |= REL_EQ;
+ else if (*p == '<')
+ flags |= REL_LT;
+ else if (*p == '!')
+ flags |= REL_LT | REL_GT | REL_EQ;
+ p++;
+ if (flags && *p == '=')
+ {
+ if (p[-1] != '=')
+ flags ^= REL_EQ;
+ p++;
+ }
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ e = p;
+ while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '|')
+ p++;
+ ee = p;
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ }
+ name = pool_strn2id(pool, n, ne - n, 1);
+ if (e)
+ {
+ evr = pool_strn2id(pool, e, ee - e, 1);
+ name = pool_rel2id(pool, name, evr, flags, 1);
+ }
+ if (*p == '|')
+ {
+ Id id = parseonedep(pool, p + 1);
+ if (id)
+ name = pool_rel2id(pool, name, id, REL_OR, 1);
+ }
+ return name;
+}
+static unsigned int
+makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
+{
+ Pool *pool = repo->pool;
+ char *p;
+ Id id;
+
+ while ((p = strchr(deps, ',')) != 0)
+ {
+ *p = 0;
+ olddeps = makedeps(repo, deps, olddeps, marker);
+ *p = ',';
+ deps = p + 1;
+ }
+ id = parseonedep(pool, deps);
+ if (!id)
+ return olddeps;
+ return repo_addid_dep(repo, olddeps, id, marker);
+}
+
+static Offset
+copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
+{
+ Id *ida, *from;
+ int cc;
+ Offset off;
+
+ if (!fromoff)
+ return 0;
+ from = fromrepo->idarraydata + fromoff;
+ for (ida = from, cc = 0; *ida; ida++, cc++)
+ ;
+ if (cc == 0)
+ return 0;
+ off = repo_reserve_ids(repo, 0, cc);
+ memcpy(repo->idarraydata + off, from, (cc + 1) * sizeof(Id));
+ repo->idarraysize += cc + 1;
+ return off;
+}
+
+static void
+copysolvabledata(Pool *pool, Solvable *s, Repo *repo)
+{
+ Repo *srepo = s->repo;
+ if (srepo == repo)
+ return;
+ s->provides = copydeps(pool, repo, s->provides, srepo);
+ s->requires = copydeps(pool, repo, s->requires, srepo);
+ s->conflicts = copydeps(pool, repo, s->conflicts, srepo);
+ s->obsoletes = copydeps(pool, repo, s->obsoletes, srepo);
+ s->recommends = copydeps(pool, repo, s->recommends, srepo);
+ s->suggests = copydeps(pool, repo, s->suggests, srepo);
+ s->supplements = copydeps(pool, repo, s->supplements, srepo);
+ s->enhances = copydeps(pool, repo, s->enhances, srepo);
+}
+
+#define KEEP_VERSION 1
+#define KEEP_PACKAGE 2
+#define KEEP_FEATURE 3
+
+static void
+finishpackage(Pool *pool, Solvable *s, int keep, Queue *job)
+{
+ Id *idp, id, sid;
+ if (!s)
+ return;
+ if (!s->arch)
+ s->arch = ARCH_ANY;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ sid = pool_rel2id(pool, s->name, s->evr, REL_EQ, 1);
+ s->provides = repo_addid_dep(s->repo, s->provides, sid, 0);
+ if (!job || !pool->installed || s->repo != pool->installed)
+ return;
+ if (keep == KEEP_VERSION)
+ queue_push2(job, SOLVER_INSTALL|SOLVER_SOLVABLE_NAME, sid);
+ else if (keep == KEEP_PACKAGE)
+ queue_push2(job, SOLVER_INSTALL|SOLVER_SOLVABLE_NAME, s->name);
+ else if (keep == KEEP_FEATURE)
+ {
+ for (idp = s->repo->idarraydata + s->provides; (id = *idp) != 0; idp++)
+ {
+ if (id != sid) /* skip self-provides */
+ queue_push2(job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, id);
+ }
+ }
+}
+
+int
+repo_add_cudf(Repo *repo, Repo *installedrepo, FILE *fp, Queue *job, int flags)
+{
+ Pool *pool;
+ char *buf, *p;
+ int bufa, bufl, c;
+ Solvable *s;
+ int instanza = 0;
+ int inrequest = 0;
+ int isinstalled = 0;
+ int keep = 0;
+ Repo *xrepo;
+
+ xrepo = repo ? repo : installedrepo;
+ if (!xrepo)
+ return -1;
+ pool = xrepo->pool;
+
+ buf = solv_malloc(4096);
+ bufa = 4096;
+ bufl = 0;
+ s = 0;
+
+ while (fgets(buf + bufl, bufa - bufl, fp) > 0)
+ {
+ bufl += strlen(buf + bufl);
+ if (bufl && buf[bufl - 1] != '\n')
+ {
+ if (bufa - bufl < 256)
+ {
+ bufa += 4096;
+ buf = solv_realloc(buf, bufa);
+ }
+ continue;
+ }
+ buf[--bufl] = 0;
+ c = getc(fp);
+ if (c == ' ' || c == '\t')
+ {
+ /* continuation line */
+ buf[bufl++] = ' ';
+ continue;
+ }
+ if (c != EOF)
+ ungetc(c, fp);
+ bufl = 0;
+ if (*buf == '#')
+ continue;
+ if (!*buf)
+ {
+ if (s && !repo && !isinstalled)
+ {
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ s = 0;
+ }
+ if (s)
+ finishpackage(pool, s, keep, job);
+ s = 0;
+ keep = 0;
+ instanza = 0;
+ inrequest = 0;
+ continue;
+ }
+ p = strchr(buf, ':');
+ if (!p)
+ continue; /* hmm */
+ *p++ = 0;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (!instanza)
+ {
+ instanza = 1;
+ inrequest = 0;
+ if (!strcmp(buf, "request"))
+ {
+ inrequest = 1;
+ continue;
+ }
+ if (!strcmp(buf, "package"))
+ {
+ s = pool_id2solvable(pool, repo_add_solvable(xrepo));
+ isinstalled = 0;
+ keep = 0;
+ }
+ }
+ if (inrequest)
+ {
+ if (!job)
+ continue;
+ if (!strcmp(buf, "install"))
+ {
+ Id id, *idp;
+ Offset off = makedeps(xrepo, p, 0, 0);
+ for (idp = xrepo->idarraydata + off; (id = *idp) != 0; idp++)
+ queue_push2(job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, id);
+ }
+ else if (!strcmp(buf, "remove"))
+ {
+ Id id, *idp;
+ Offset off = makedeps(xrepo, p, 0, 0);
+ for (idp = xrepo->idarraydata + off; (id = *idp) != 0; idp++)
+ queue_push2(job, SOLVER_ERASE|SOLVER_SOLVABLE_PROVIDES, id);
+ }
+ else if (!strcmp(buf, "upgrade"))
+ {
+ Id id, *idp;
+ Offset off = makedeps(xrepo, p, 0, 0);
+ for (idp = xrepo->idarraydata + off; (id = *idp) != 0; idp++)
+ queue_push2(job, SOLVER_INSTALL|SOLVER_ORUPDATE|SOLVER_SOLVABLE_PROVIDES, id);
+ }
+ continue;
+ }
+ if (!s)
+ continue; /* we ignore the preamble for now */
+ switch (buf[0])
+ {
+ case 'c':
+ if (!strcmp(buf, "conflicts"))
+ {
+ s->conflicts = makedeps(s->repo, p, s->conflicts, 0);
+ continue;
+ }
+ case 'd':
+ if (!strcmp(buf, "depends"))
+ {
+ s->requires = makedeps(s->repo, p, s->requires, 0);
+ continue;
+ }
+ break;
+ case 'k':
+ if (!strcmp(buf, "keep"))
+ {
+ if (!job)
+ continue;
+ if (!strcmp(p, "version"))
+ keep = KEEP_VERSION;
+ else if (!strcmp(p, "package"))
+ keep = KEEP_PACKAGE;
+ else if (!strcmp(p, "feature"))
+ keep = KEEP_FEATURE;
+ continue;
+ }
+ break;
+ case 'i':
+ if (!strcmp(buf, "installed"))
+ {
+ if (!strcmp(p, "true"))
+ {
+ isinstalled = 1;
+ if (!installedrepo)
+ {
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ s = 0;
+ }
+ else if (s->repo != installedrepo)
+ {
+ copysolvabledata(pool, s, installedrepo);
+ s->repo->nsolvables--;
+ s->repo = installedrepo;
+ if (s - pool->solvables < s->repo->start)
+ s->repo->start = s - pool->solvables;
+ if (s - pool->solvables >= s->repo->end)
+ s->repo->end = s - pool->solvables + 1;
+ s->repo->nsolvables++;
+ }
+ }
+ continue;
+ }
+ break;
+ case 'p':
+ if (!strcmp(buf, "package"))
+ {
+ s->name = pool_str2id(pool, p, 1);
+ continue;
+ }
+ if (!strcmp(buf, "provides"))
+ {
+ s->provides = makedeps(s->repo, p, s->provides, 0);
+ continue;
+ }
+ break;
+ case 'r':
+ if (!strcmp(buf, "depends"))
+ {
+ s->recommends = makedeps(s->repo, p, s->recommends, 0);
+ continue;
+ }
+ break;
+ case 'v':
+ if (!strcmp(buf, "version"))
+ {
+ s->evr = pool_str2id(pool, p, 1);
+ continue;
+ }
+ break;
+ }
+ }
+ if (s && !repo && !isinstalled)
+ {
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ s = 0;
+ }
+ if (s)
+ finishpackage(pool, s, keep, job);
+ solv_free(buf);
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_cudf(Repo *repo, Repo *installedrepo, FILE *fp, Queue *job, int flags);
+
--- /dev/null
+/*
+ * Copyright (c) 2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <zlib.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "solver.h" /* for GET_USERINSTALLED_ flags */
+#include "chksum.h"
+#include "repo_deb.h"
+
+static unsigned char *
+decompress(unsigned char *in, int inl, int *outlp)
+{
+ z_stream strm;
+ int outl, ret;
+ unsigned char *out;
+
+ memset(&strm, 0, sizeof(strm));
+ strm.next_in = in;
+ strm.avail_in = inl;
+ out = solv_malloc(4096);
+ strm.next_out = out;
+ strm.avail_out = 4096;
+ outl = 0;
+ ret = inflateInit2(&strm, -MAX_WBITS);
+ if (ret != Z_OK)
+ {
+ free(out);
+ return 0;
+ }
+ for (;;)
+ {
+ if (strm.avail_out == 0)
+ {
+ outl += 4096;
+ out = solv_realloc(out, outl + 4096);
+ strm.next_out = out + outl;
+ strm.avail_out = 4096;
+ }
+ ret = inflate(&strm, Z_NO_FLUSH);
+ if (ret == Z_STREAM_END)
+ break;
+ if (ret != Z_OK)
+ {
+ free(out);
+ return 0;
+ }
+ }
+ outl += 4096 - strm.avail_out;
+ inflateEnd(&strm);
+ *outlp = outl;
+ return out;
+}
+
+static Id
+parseonedep(Pool *pool, char *p)
+{
+ char *n, *ne, *e, *ee;
+ Id name, evr;
+ int flags;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ if (!*p || *p == '(')
+ return 0;
+ n = p;
+ /* find end of name */
+ while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '(' && *p != '|')
+ p++;
+ ne = p;
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ evr = 0;
+ flags = 0;
+ e = ee = 0;
+ if (*p == '(')
+ {
+ p++;
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ if (*p == '>')
+ flags |= REL_GT;
+ else if (*p == '=')
+ flags |= REL_EQ;
+ else if (*p == '<')
+ flags |= REL_LT;
+ if (flags)
+ {
+ p++;
+ if (*p == '>')
+ flags |= REL_GT;
+ else if (*p == '=')
+ flags |= REL_EQ;
+ else if (*p == '<')
+ flags |= REL_LT;
+ else
+ p--;
+ p++;
+ }
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ e = p;
+ while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != ')')
+ p++;
+ ee = p;
+ while (*p && *p != ')')
+ p++;
+ if (*p)
+ p++;
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+ }
+ if (ne - n > 4 && ne[-4] == ':' && !strncmp(ne - 4, ":any", 4))
+ {
+ /* multiarch annotation */
+ name = pool_strn2id(pool, n, ne - n - 4, 1);
+ name = pool_rel2id(pool, name, ARCH_ANY, REL_MULTIARCH, 1);
+ }
+ else
+ name = pool_strn2id(pool, n, ne - n, 1);
+ if (e)
+ {
+ evr = pool_strn2id(pool, e, ee - e, 1);
+ name = pool_rel2id(pool, name, evr, flags, 1);
+ }
+ if (*p == '|')
+ {
+ Id id = parseonedep(pool, p + 1);
+ if (id)
+ name = pool_rel2id(pool, name, id, REL_OR, 1);
+ }
+ return name;
+}
+
+static unsigned int
+makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
+{
+ Pool *pool = repo->pool;
+ char *p;
+ Id id;
+
+ while ((p = strchr(deps, ',')) != 0)
+ {
+ *p = 0;
+ olddeps = makedeps(repo, deps, olddeps, marker);
+ *p = ',';
+ deps = p + 1;
+ }
+ id = parseonedep(pool, deps);
+ if (!id)
+ return olddeps;
+ return repo_addid_dep(repo, olddeps, id, marker);
+}
+
+
+/* put data from control file into the solvable */
+/* warning: does inplace changes */
+static void
+control2solvable(Solvable *s, Repodata *data, char *control)
+{
+ Repo *repo = s->repo;
+ Pool *pool = repo->pool;
+ char *p, *q, *end, *tag;
+ int x, l;
+ int havesource = 0;
+ char checksum[32 * 2 + 1];
+ Id checksumtype = 0;
+ Id newtype;
+
+ p = control;
+ while (*p)
+ {
+ p = strchr(p, '\n');
+ if (!p)
+ break;
+ if (p[1] == ' ' || p[1] == '\t')
+ {
+ char *q;
+ /* continuation line */
+ q = p - 1;
+ while (q >= control && *q == ' ' && *q == '\t')
+ q--;
+ l = q + 1 - control;
+ if (l)
+ memmove(p + 1 - l, control, l);
+ control = p + 1 - l;
+ p[1] = '\n';
+ p += 2;
+ continue;
+ }
+ end = p - 1;
+ if (*p)
+ *p++ = 0;
+ /* strip trailing space */
+ while (end >= control && (*end == ' ' || *end == '\t'))
+ *end-- = 0;
+ tag = control;
+ control = p;
+ q = strchr(tag, ':');
+ if (!q || q - tag < 4)
+ continue;
+ *q++ = 0;
+ while (*q == ' ' || *q == '\t')
+ q++;
+ x = '@' + (tag[0] & 0x1f);
+ x = (x << 8) + '@' + (tag[1] & 0x1f);
+ switch(x)
+ {
+ case 'A' << 8 | 'R':
+ if (!strcasecmp(tag, "architecture"))
+ s->arch = pool_str2id(pool, q, 1);
+ break;
+ case 'B' << 8 | 'R':
+ if (!strcasecmp(tag, "breaks"))
+ s->conflicts = makedeps(repo, q, s->conflicts, 0);
+ break;
+ case 'C' << 8 | 'O':
+ if (!strcasecmp(tag, "conflicts"))
+ s->conflicts = makedeps(repo, q, s->conflicts, 0);
+ break;
+ case 'D' << 8 | 'E':
+ if (!strcasecmp(tag, "depends"))
+ s->requires = makedeps(repo, q, s->requires, -SOLVABLE_PREREQMARKER);
+ else if (!strcasecmp(tag, "description"))
+ {
+ char *ld = strchr(q, '\n');
+ if (ld)
+ {
+ *ld++ = 0;
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, ld);
+ }
+ else
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, q);
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, q);
+ }
+ break;
+ case 'E' << 8 | 'N':
+ if (!strcasecmp(tag, "enhances"))
+ s->enhances = makedeps(repo, q, s->enhances, 0);
+ break;
+ case 'F' << 8 | 'I':
+ if (!strcasecmp(tag, "filename"))
+ repodata_set_location(data, s - pool->solvables, 0, 0, q);
+ break;
+ case 'H' << 8 | 'O':
+ if (!strcasecmp(tag, "homepage"))
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, q);
+ break;
+ case 'I' << 8 | 'N':
+ if (!strcasecmp(tag, "installed-size"))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(q, 0, 10) << 10);
+ break;
+ case 'M' << 8 | 'D':
+ if (!strcasecmp(tag, "md5sum") && !checksumtype && strlen(q) == 16 * 2)
+ {
+ strcpy(checksum, q);
+ checksumtype = REPOKEY_TYPE_MD5;
+ }
+ break;
+ case 'P' << 8 | 'A':
+ if (!strcasecmp(tag, "package"))
+ s->name = pool_str2id(pool, q, 1);
+ break;
+ case 'P' << 8 | 'R':
+ if (!strcasecmp(tag, "pre-depends"))
+ s->requires = makedeps(repo, q, s->requires, SOLVABLE_PREREQMARKER);
+ else if (!strcasecmp(tag, "provides"))
+ s->provides = makedeps(repo, q, s->provides, 0);
+ break;
+ case 'R' << 8 | 'E':
+ if (!strcasecmp(tag, "replaces"))
+ s->obsoletes = makedeps(repo, q, s->obsoletes, 0);
+ else if (!strcasecmp(tag, "recommends"))
+ s->recommends = makedeps(repo, q, s->recommends, 0);
+ break;
+ case 'S' << 8 | 'H':
+ newtype = solv_chksum_str2type(tag);
+ if (!newtype || solv_chksum_len(newtype) * 2 != strlen(q))
+ break;
+ if (!checksumtype || (newtype == REPOKEY_TYPE_SHA1 && checksumtype != REPOKEY_TYPE_SHA256) || newtype == REPOKEY_TYPE_SHA256)
+ {
+ strcpy(checksum, q);
+ checksumtype = newtype;
+ }
+ break;
+ case 'S' << 8 | 'O':
+ if (!strcasecmp(tag, "source"))
+ {
+ char *q2;
+ /* ignore version for now */
+ for (q2 = q; *q2; q2++)
+ if (*q2 == ' ' || *q2 == '\t')
+ {
+ *q2 = 0;
+ break;
+ }
+ if (s->name && !strcmp(q, pool_id2str(pool, s->name)))
+ repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
+ else
+ repodata_set_id(data, s - pool->solvables, SOLVABLE_SOURCENAME, pool_str2id(pool, q, 1));
+ havesource = 1;
+ }
+ break;
+ case 'S' << 8 | 'T':
+ if (!strcasecmp(tag, "status"))
+ repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_INSTALLSTATUS, q);
+ break;
+ case 'S' << 8 | 'U':
+ if (!strcasecmp(tag, "suggests"))
+ s->suggests = makedeps(repo, q, s->suggests, 0);
+ break;
+ case 'V' << 8 | 'E':
+ if (!strcasecmp(tag, "version"))
+ s->evr = pool_str2id(pool, q, 1);
+ break;
+ }
+ }
+ if (checksumtype)
+ repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, checksumtype, checksum);
+ if (!s->arch)
+ s->arch = ARCH_ALL;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ if (s->name)
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ if (s->name && !havesource)
+ repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
+ if (s->obsoletes)
+ {
+ /* obsoletes only count when the packages also conflict */
+ /* XXX: should not transcode here */
+ int i, j, k;
+ Id d, cid;
+ for (i = j = s->obsoletes; (d = repo->idarraydata[i]) != 0; i++)
+ {
+ if (!s->conflicts)
+ continue;
+ for (k = s->conflicts; (cid = repo->idarraydata[k]) != 0; k++)
+ {
+ if (repo->idarraydata[k] == cid)
+ break;
+ if (ISRELDEP(cid))
+ {
+ Reldep *rd = GETRELDEP(pool, cid);
+ if (rd->flags < 8 && rd->name == d)
+ break; /* specialize obsoletes */
+ }
+ }
+ if (cid)
+ repo->idarraydata[j++] = cid;
+ }
+ repo->idarraydata[j] = 0;
+ if (j == s->obsoletes)
+ s->obsoletes = 0;
+ }
+}
+
+int
+repo_add_debpackages(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ char *buf, *p;
+ int bufl, l, ll;
+ Solvable *s;
+
+ data = repo_add_repodata(repo, flags);
+ buf = solv_malloc(4096);
+ bufl = 4096;
+ l = 0;
+ buf[l] = 0;
+ p = buf;
+ for (;;)
+ {
+ if (!(p = strchr(p, '\n')))
+ {
+ int l3;
+ if (l + 1024 >= bufl)
+ {
+ buf = solv_realloc(buf, bufl + 4096);
+ bufl += 4096;
+ p = buf + l;
+ continue;
+ }
+ p = buf + l;
+ ll = fread(p, 1, bufl - l - 1, fp);
+ if (ll <= 0)
+ break;
+ p[ll] = 0;
+ while ((l3 = strlen(p)) < ll)
+ p[l3] = '\n';
+ l += ll;
+ continue;
+ }
+ p++;
+ if (*p != '\n')
+ continue;
+ *p = 0;
+ ll = p - buf + 1;
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ control2solvable(s, data, buf);
+ if (!s->name)
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ if (l > ll)
+ memmove(buf, p + 1, l - ll);
+ l -= ll;
+ p = buf;
+ buf[l] = 0;
+ }
+ if (l)
+ {
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ control2solvable(s, data, buf);
+ if (!s->name)
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ }
+ solv_free(buf);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+int
+repo_add_debdb(Repo *repo, int flags)
+{
+ FILE *fp;
+ const char *path = "/var/lib/dpkg/status";
+ if (flags & REPO_USE_ROOTDIR)
+ path = pool_prepend_rootdir_tmp(repo->pool, path);
+ if ((fp = fopen(path, "r")) == 0)
+ return pool_error(repo->pool, -1, "%s: %s", path, strerror(errno));
+ repo_add_debpackages(repo, fp, flags);
+ fclose(fp);
+ return 0;
+}
+
+Id
+repo_add_deb(Repo *repo, const char *deb, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ unsigned char buf[4096], *bp;
+ int l, l2, vlen, clen, ctarlen;
+ unsigned char *ctgz;
+ unsigned char pkgid[16];
+ unsigned char *ctar;
+ int gotpkgid;
+ FILE *fp;
+ Solvable *s;
+ struct stat stb;
+
+ data = repo_add_repodata(repo, flags);
+ if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, deb) : deb, "r")) == 0)
+ {
+ pool_error(pool, -1, "%s: %s", deb, strerror(errno));
+ return 0;
+ }
+ if (fstat(fileno(fp), &stb))
+ {
+ pool_error(pool, -1, "fstat: %s", strerror(errno));
+ fclose(fp);
+ return 0;
+ }
+ l = fread(buf, 1, sizeof(buf), fp);
+ if (l < 8 + 60 || (strncmp((char *)buf, "!<arch>\ndebian-binary ", 8 + 16) != 0 && strncmp((char *)buf, "!<arch>\ndebian-binary/ ", 8 + 16) != 0))
+ {
+ pool_error(pool, -1, "%s: not a deb package", deb);
+ fclose(fp);
+ return 0;
+ }
+ vlen = atoi((char *)buf + 8 + 48);
+ if (vlen < 0 || vlen > l)
+ {
+ pool_error(pool, -1, "%s: not a deb package", deb);
+ fclose(fp);
+ return 0;
+ }
+ vlen += vlen & 1;
+ if (l < 8 + 60 + vlen + 60)
+ {
+ pool_error(pool, -1, "%s: unhandled deb package", deb);
+ fclose(fp);
+ return 0;
+ }
+ if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz ", 16) != 0 && strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz/ ", 16) != 0)
+ {
+ pool_error(pool, -1, "%s: control.tar.gz is not second entry", deb);
+ fclose(fp);
+ return 0;
+ }
+ clen = atoi((char *)buf + 8 + 60 + vlen + 48);
+ if (clen <= 0 || clen >= 0x100000)
+ {
+ pool_error(pool, -1, "%s: control.tar.gz has illegal size", deb);
+ fclose(fp);
+ return 0;
+ }
+ ctgz = solv_calloc(1, clen + 4);
+ bp = buf + 8 + 60 + vlen + 60;
+ l -= 8 + 60 + vlen + 60;
+ if (l > clen)
+ l = clen;
+ if (l)
+ memcpy(ctgz, bp, l);
+ if (l < clen)
+ {
+ if (fread(ctgz + l, clen - l, 1, fp) != 1)
+ {
+ pool_error(pool, -1, "%s: unexpected EOF", deb);
+ solv_free(ctgz);
+ fclose(fp);
+ return 0;
+ }
+ }
+ fclose(fp);
+ gotpkgid = 0;
+ if (flags & DEBS_ADD_WITH_PKGID)
+ {
+ Chksum *chk = solv_chksum_create(REPOKEY_TYPE_MD5);
+ solv_chksum_add(chk, ctgz, clen);
+ solv_chksum_free(chk, pkgid);
+ gotpkgid = 1;
+ }
+ if (ctgz[0] != 0x1f || ctgz[1] != 0x8b)
+ {
+ pool_error(pool, -1, "%s: control.tar.gz is not gzipped", deb);
+ solv_free(ctgz);
+ return 0;
+ }
+ if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0)
+ {
+ pool_error(pool, -1, "%s: control.tar.gz is compressed in a strange way", deb);
+ solv_free(ctgz);
+ return 0;
+ }
+ bp = ctgz + 4;
+ bp += 6; /* skip time, xflags and OS code */
+ if (ctgz[3] & 0x04)
+ {
+ /* skip extra field */
+ l = bp[0] | bp[1] << 8;
+ bp += l + 2;
+ if (bp >= ctgz + clen)
+ {
+ pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
+ solv_free(ctgz);
+ return 0;
+ }
+ }
+ if (ctgz[3] & 0x08) /* orig filename */
+ while (*bp)
+ bp++;
+ if (ctgz[3] & 0x10) /* file comment */
+ while (*bp)
+ bp++;
+ if (ctgz[3] & 0x02) /* header crc */
+ bp += 2;
+ if (bp >= ctgz + clen)
+ {
+ pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
+ solv_free(ctgz);
+ return 0;
+ }
+ ctar = decompress(bp, ctgz + clen - bp, &ctarlen);
+ solv_free(ctgz);
+ if (!ctar)
+ {
+ pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
+ return 0;
+ }
+ bp = ctar;
+ l = ctarlen;
+ while (l > 512)
+ {
+ int j;
+ l2 = 0;
+ for (j = 124; j < 124 + 12; j++)
+ if (bp[j] >= '0' && bp[j] <= '7')
+ l2 = l2 * 8 + (bp[j] - '0');
+ if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control"))
+ break;
+ l2 = 512 + ((l2 + 511) & ~511);
+ l -= l2;
+ bp += l2;
+ }
+ if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
+ {
+ pool_error(pool, -1, "%s: control.tar.gz contains no control file", deb);
+ free(ctar);
+ return 0;
+ }
+ memmove(ctar, bp + 512, l2);
+ ctar = solv_realloc(ctar, l2 + 1);
+ ctar[l2] = 0;
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ control2solvable(s, data, (char *)ctar);
+ if (!(flags & REPO_NO_LOCATION))
+ repodata_set_location(data, s - pool->solvables, 0, 0, deb);
+ if (S_ISREG(stb.st_mode))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
+ if (gotpkgid)
+ repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
+ solv_free(ctar);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return s - pool->solvables;
+}
+
+void
+pool_deb_get_autoinstalled(Pool *pool, FILE *fp, Queue *q, int flags)
+{
+ Id name = 0, arch = 0;
+ int autoinstalled = -1;
+ char *buf, *bp;
+ int x, l, bufl, eof = 0;
+ Id p, pp;
+
+ queue_empty(q);
+ buf = solv_malloc(4096);
+ bufl = 4096;
+ l = 0;
+ while (!eof)
+ {
+ while (bufl - l < 1024)
+ {
+ bufl += 4096;
+ if (bufl > 1024 * 64)
+ break; /* hmm? */
+ buf = solv_realloc(buf, bufl);
+ }
+ if (!fgets(buf + l, bufl - l, fp))
+ {
+ eof = 1;
+ buf[l] = '\n';
+ buf[l + 1] = 0;
+ }
+ l = strlen(buf);
+ if (l && buf[l - 1] == '\n')
+ buf[--l] = 0;
+ if (!*buf || eof)
+ {
+ l = 0;
+ if (name && autoinstalled > 0)
+ {
+ if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+ queue_push2(q, name, arch);
+ else if ((flags & GET_USERINSTALLED_NAMES) != 0)
+ queue_push(q, name);
+ else
+ {
+ FOR_PROVIDES(p, pp, name)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->name != name)
+ continue;
+ if (arch && s->arch != arch)
+ continue;
+ queue_push(q, p);
+ }
+ }
+ }
+ name = arch = 0;
+ autoinstalled = -1;
+ continue;
+ }
+ /* strip trailing space */
+ while (l && (buf[l - 1] == ' ' || buf[l - 1] == '\t'))
+ buf[--l] = 0;
+ l = 0;
+
+ bp = strchr(buf, ':');
+ if (!bp || bp - buf < 4)
+ continue;
+ *bp++ = 0;
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ x = '@' + (buf[0] & 0x1f);
+ x = (x << 8) + '@' + (buf[1] & 0x1f);
+ switch(x)
+ {
+ case 'P' << 8 | 'A':
+ if (!strcasecmp(buf, "package"))
+ name = pool_str2id(pool, bp, 1);
+ break;
+ case 'A' << 8 | 'R':
+ if (!strcasecmp(buf, "architecture"))
+ arch = pool_str2id(pool, bp, 1);
+ break;
+ case 'A' << 8 | 'U':
+ if (!strcasecmp(buf, "auto-installed"))
+ autoinstalled = atoi(bp);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_debpackages(Repo *repo, FILE *fp, int flags);
+extern int repo_add_debdb(Repo *repo, int flags);
+extern Id repo_add_deb(Repo *repo, const char *deb, int flags);
+extern void pool_deb_get_autoinstalled(Pool *pool, FILE *fp, Queue *q, int flags);
+
+#define DEBS_ADD_WITH_PKGID (1 << 8)
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define DO_ARRAY 1
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_deltainfoxml.h"
+
+/*
+ * <deltainfo>
+ * <newpackage name="libtool" epoch="0" version="1.5.24" release="6.fc9" arch="i386">
+ * <delta oldepoch="0" oldversion="1.5.24" oldrelease="3.fc8">
+ * <filename>DRPMS/libtool-1.5.24-3.fc8_1.5.24-6.fc9.i386.drpm</filename>
+ * <sequence>libtool-1.5.24-3.fc8-d3571f98b048b1a870e40241bb46c67ab4</sequence>
+ * <size>22452</size>
+ * <checksum type="sha">8f05394695dee9399c204614e21e5f6848990ab7</checksum>
+ * </delta>
+ * <delta oldepoch="0" oldversion="1.5.22" oldrelease="11.fc7">
+ * <filename>DRPMS/libtool-1.5.22-11.fc7_1.5.24-6.fc9.i386.drpm</filename>
+ * <sequence>libtool-1.5.22-11.fc7-e82691677eee1e83b4812572c5c9ce8eb</sequence>
+ * <size>110362</size>
+ * <checksum type="sha">326658fee45c0baec1e70231046dbaf560f941ce</checksum>
+ * </delta>
+ * </newpackage>
+ * </deltainfo>
+ */
+
+enum state {
+ STATE_START,
+ STATE_NEWPACKAGE, /* 1 */
+ STATE_DELTA, /* 2 */
+ STATE_FILENAME, /* 3 */
+ STATE_SEQUENCE, /* 4 */
+ STATE_SIZE, /* 5 */
+ STATE_CHECKSUM, /* 6 */
+ STATE_LOCATION, /* 7 */
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+/* !! must be sorted by first column !! */
+static struct stateswitch stateswitches[] = {
+ /* compatibility with old yum-presto */
+ { STATE_START, "prestodelta", STATE_START, 0 },
+ { STATE_START, "deltainfo", STATE_START, 0 },
+ { STATE_START, "newpackage", STATE_NEWPACKAGE, 0 },
+ { STATE_NEWPACKAGE, "delta", STATE_DELTA, 0 },
+ /* compatibility with yum-presto */
+ { STATE_DELTA, "filename", STATE_FILENAME, 1 },
+ { STATE_DELTA, "location", STATE_LOCATION, 0 },
+ { STATE_DELTA, "sequence", STATE_SEQUENCE, 1 },
+ { STATE_DELTA, "size", STATE_SIZE, 1 },
+ { STATE_DELTA, "checksum", STATE_CHECKSUM, 1 },
+ { NUMSTATES }
+};
+
+/* Cumulated info about the current deltarpm or patchrpm */
+struct deltarpm {
+ char *location;
+ char *locbase;
+ unsigned int buildtime;
+ unsigned long long downloadsize;
+ char *filechecksum;
+ int filechecksumtype;
+ /* Baseversion. deltarpm only has one. */
+ Id *bevr;
+ unsigned nbevr;
+ Id seqname;
+ Id seqevr;
+ char *seqnum;
+};
+
+struct parsedata {
+ int ret;
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+ struct deltarpm delta;
+ Id newpkgevr;
+ Id newpkgname;
+ Id newpkgarch;
+
+ Id *handles;
+ int nhandles;
+};
+
+/*
+ * find attribute
+ */
+
+static const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ }
+ return 0;
+}
+
+
+/*
+ * create evr (as Id) from 'epoch', 'version' and 'release' attributes
+ */
+
+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, "oldepoch"))
+ e = atts[1];
+ else if (!strcmp(*atts, "epoch"))
+ e = atts[1];
+ else if (!strcmp(*atts, "version"))
+ v = atts[1];
+ else if (!strcmp(*atts, "oldversion"))
+ v = atts[1];
+ else if (!strcmp(*atts, "release"))
+ r = atts[1];
+ else if (!strcmp(*atts, "oldrelease"))
+ r = atts[1];
+ }
+ if (e && (!*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 = solv_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 pool_str2id(pool, pd->content, 1);
+}
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ struct stateswitch *sw;
+ const char *str;
+
+#if 0
+ fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ pd->depth++;
+ if (!pd->swtab[pd->state])
+ return;
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
+ if (!strcmp(sw->ename, name))
+ break;
+ if (sw->from != pd->state)
+ {
+#if 0
+ fprintf(stderr, "into unknown: [%d]%s (from: %d)\n", sw->to, name, sw->from);
+#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_START:
+ break;
+ case STATE_NEWPACKAGE:
+ if ((str = find_attr("name", atts)) != 0)
+ pd->newpkgname = pool_str2id(pool, str, 1);
+ pd->newpkgevr = makeevr_atts(pool, pd, atts);
+ if ((str = find_attr("arch", atts)) != 0)
+ pd->newpkgarch = pool_str2id(pool, str, 1);
+ break;
+
+ case STATE_DELTA:
+ memset(&pd->delta, 0, sizeof(pd->delta));
+ pd->delta.bevr = solv_extend(pd->delta.bevr, pd->delta.nbevr, 1, sizeof(Id), 7);
+ pd->delta.bevr[pd->delta.nbevr++] = makeevr_atts(pool, pd, atts);
+ break;
+ case STATE_FILENAME:
+ if ((str = find_attr("xml:base", atts)))
+ pd->delta.locbase = solv_strdup(str);
+ break;
+ case STATE_LOCATION:
+ pd->delta.location = solv_strdup(find_attr("href", atts));
+ if ((str = find_attr("xml:base", atts)))
+ pd->delta.locbase = solv_strdup(str);
+ break;
+ case STATE_SIZE:
+ break;
+ case STATE_CHECKSUM:
+ pd->delta.filechecksum = 0;
+ pd->delta.filechecksumtype = REPOKEY_TYPE_SHA1;
+ if ((str = find_attr("type", atts)) != 0)
+ {
+ pd->delta.filechecksumtype = solv_chksum_str2type(str);
+ if (!pd->delta.filechecksumtype)
+ pool_debug(pool, SOLV_ERROR, "unknown checksum type: '%s'\n", str);
+ }
+ break;
+ case STATE_SEQUENCE:
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ const char *str;
+
+#if 0
+ fprintf(stderr, "end: %s\n", name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+#if 0
+ fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+#endif
+ return;
+ }
+
+ pd->depth--;
+ pd->statedepth--;
+ switch (pd->state)
+ {
+ case STATE_START:
+ break;
+ case STATE_NEWPACKAGE:
+ break;
+ case STATE_DELTA:
+ {
+ /* read all data for a deltarpm. commit into attributes */
+ Id handle;
+ struct deltarpm *d = &pd->delta;
+
+ handle = repodata_new_handle(pd->data);
+ /* we commit all handles later on in one go so that the
+ * repodata code doesn't need to realloc every time */
+ pd->handles = solv_extend(pd->handles, pd->nhandles, 1, sizeof(Id), 63);
+ pd->handles[pd->nhandles++] = handle;
+ repodata_set_id(pd->data, handle, DELTA_PACKAGE_NAME, pd->newpkgname);
+ repodata_set_id(pd->data, handle, DELTA_PACKAGE_EVR, pd->newpkgevr);
+ repodata_set_id(pd->data, handle, DELTA_PACKAGE_ARCH, pd->newpkgarch);
+ if (d->location)
+ {
+ repodata_set_deltalocation(pd->data, handle, 0, 0, d->location);
+ if (d->locbase)
+ repodata_set_poolstr(pd->data, handle, DELTA_LOCATION_BASE, d->locbase);
+ }
+ if (d->downloadsize)
+ repodata_set_num(pd->data, handle, DELTA_DOWNLOADSIZE, d->downloadsize);
+ if (d->filechecksum)
+ repodata_set_checksum(pd->data, handle, DELTA_CHECKSUM, d->filechecksumtype, d->filechecksum);
+ if (d->seqnum)
+ {
+ repodata_set_id(pd->data, handle, DELTA_BASE_EVR, d->bevr[0]);
+ repodata_set_id(pd->data, handle, DELTA_SEQ_NAME, d->seqname);
+ repodata_set_id(pd->data, handle, DELTA_SEQ_EVR, d->seqevr);
+ /* should store as binary blob! */
+ repodata_set_str(pd->data, handle, DELTA_SEQ_NUM, d->seqnum);
+ }
+ }
+ pd->delta.filechecksum = solv_free(pd->delta.filechecksum);
+ pd->delta.bevr = solv_free(pd->delta.bevr);
+ pd->delta.nbevr = 0;
+ pd->delta.seqnum = solv_free(pd->delta.seqnum);
+ pd->delta.location = solv_free(pd->delta.location);
+ pd->delta.locbase = solv_free(pd->delta.locbase);
+ break;
+ case STATE_FILENAME:
+ pd->delta.location = solv_strdup(pd->content);
+ break;
+ case STATE_CHECKSUM:
+ pd->delta.filechecksum = solv_strdup(pd->content);
+ break;
+ case STATE_SIZE:
+ pd->delta.downloadsize = strtoull(pd->content, 0, 10);
+ break;
+ case STATE_SEQUENCE:
+ if ((str = pd->content))
+ {
+ const char *s1, *s2;
+ s1 = strrchr(str, '-');
+ if (s1)
+ {
+ for (s2 = s1 - 1; s2 > str; s2--)
+ if (*s2 == '-')
+ break;
+ if (*s2 == '-')
+ {
+ for (s2 = s2 - 1; s2 > str; s2--)
+ if (*s2 == '-')
+ break;
+ if (*s2 == '-')
+ {
+ pd->delta.seqevr = pool_strn2id(pool, s2 + 1, s1 - s2 - 1, 1);
+ pd->delta.seqname = pool_strn2id(pool, str, s2 - str, 1);
+ str = s1 + 1;
+ }
+ }
+ }
+ pd->delta.seqnum = solv_strdup(str);
+ }
+ default:
+ break;
+ }
+
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+}
+
+
+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 = solv_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
+
+int
+repo_add_deltainfoxml(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ struct parsedata pd;
+ char buf[BUFF_SIZE];
+ int i, l;
+ struct stateswitch *sw;
+ Repodata *data;
+ XML_Parser parser;
+
+ data = repo_add_repodata(repo, flags);
+
+ 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.repo = repo;
+ pd.data = data;
+
+ pd.content = solv_malloc(256);
+ pd.acontent = 256;
+ pd.lcontent = 0;
+
+ 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)
+ {
+ pd.ret = pool_error(pool, -1, "repo_updateinfoxml: %s at line %u:%u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ break;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+ solv_free(pd.content);
+
+ /* now commit all handles */
+ if (!pd.ret)
+ for (i = 0; i < pd.nhandles; i++)
+ repodata_add_flexarray(pd.data, SOLVID_META, REPOSITORY_DELTAINFO, pd.handles[i]);
+ solv_free(pd.handles);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return pd.ret;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_deltainfoxml(Repo *repo, FILE *fp, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2011-2013, Ingo Weinhold <ingo_weinhold@gmx.de>
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <package/PackageInfo.h>
+#include <package/PackageInfoSet.h>
+#include <package/PackageRoster.h>
+#include <package/PackageVersion.h>
+#include <package/RepositoryCache.h>
+#include <package/RepositoryConfig.h>
+
+#include "repo_haiku.h"
+
+using namespace BPackageKit;
+using namespace BPackageKit::BHPKG;
+
+static void add_dependency(Repo *repo, Offset &dependencies, const char *name,
+ const char *version, int flags, const char* compatVersion = NULL)
+{
+ Pool *pool = repo->pool;
+
+ Id dependency = pool_str2id(pool, name, 1);
+
+ if (version && version[0] != '\0')
+ {
+ Id versionId = pool_str2id(pool, version, 1);
+
+ if (compatVersion && compatVersion[0] != '\0')
+ {
+ versionId = pool_rel2id(pool, versionId, pool_str2id(pool, compatVersion, 1),
+ REL_COMPAT, 1);
+ }
+
+ dependency = pool_rel2id(pool, dependency, versionId, flags, 1);
+ }
+
+ dependencies = repo_addid_dep(repo, dependencies, dependency, 0);
+}
+
+static void add_dependency(Repo *repo, Offset &dependencies, const char *name,
+ const BPackageVersion &version, int flags)
+{
+ add_dependency(repo, dependencies, name, version.ToString(),
+ flags);
+}
+
+static void add_resolvables(Repo *repo, Offset &dependencies,
+ const BObjectList<BPackageResolvable> &resolvables)
+{
+ for (int32 i = 0; BPackageResolvable *resolvable = resolvables.ItemAt(i); i++)
+ {
+ add_dependency(repo, dependencies, resolvable->Name(),
+ resolvable->Version().ToString(), REL_EQ,
+ resolvable->CompatibleVersion().ToString());
+ }
+}
+
+static void add_resolvable_expressions(Repo *repo, Offset &dependencies,
+ const BObjectList<BPackageResolvableExpression> &expressions)
+{
+ for (int32 i = 0;
+ BPackageResolvableExpression *expression = expressions.ItemAt(i); i++)
+ {
+ // It is possible that no version is specified. In that case any version
+ // is acceptable.
+ if (expression->Version().InitCheck() != B_OK)
+ {
+ BPackageVersion version;
+ add_dependency(repo, dependencies, expression->Name(), NULL, 0);
+ continue;
+ }
+
+ int flags = 0;
+ switch (expression->Operator())
+ {
+ case B_PACKAGE_RESOLVABLE_OP_LESS:
+ flags |= REL_LT;
+ break;
+ case B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL:
+ flags |= REL_LT | REL_EQ;
+ break;
+ case B_PACKAGE_RESOLVABLE_OP_EQUAL:
+ flags |= REL_EQ;
+ break;
+ case B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL:
+ break;
+ case B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL:
+ flags |= REL_GT | REL_EQ;
+ break;
+ case B_PACKAGE_RESOLVABLE_OP_GREATER:
+ flags |= REL_GT;
+ break;
+ }
+
+ add_dependency(repo, dependencies, expression->Name(),
+ expression->Version(), flags);
+ }
+}
+
+static void add_replaces_list(Repo *repo, Offset &dependencies,
+ const BStringList &packageNames)
+{
+ int32 count = packageNames.CountStrings();
+ for (int32 i = 0; i < count; i++)
+ {
+ const BString &packageName = packageNames.StringAt(i);
+ add_dependency(repo, dependencies, packageName, BPackageVersion(), 0);
+ }
+}
+
+static Id add_package_info_to_repo(Repo *repo, Repodata *repoData,
+ const BPackageInfo &packageInfo)
+{
+ Pool *pool = repo->pool;
+
+ Id solvableId = repo_add_solvable(repo);
+ Solvable *solvable = pool_id2solvable(pool, solvableId);
+ // Prepend "pkg:" to package name, so "provides" don't match unless explicitly
+ // specified this way.
+ BString name("pkg:");
+ name << packageInfo.Name();
+ solvable->name = pool_str2id(pool, name, 1);
+ if (packageInfo.Architecture() == B_PACKAGE_ARCHITECTURE_ANY)
+ solvable->arch = ARCH_ANY;
+ else if (packageInfo.Architecture() == B_PACKAGE_ARCHITECTURE_SOURCE)
+ solvable->arch = ARCH_SRC;
+ else
+ solvable->arch = pool_str2id(pool,
+ BPackageInfo::kArchitectureNames[packageInfo.Architecture()], 1);
+ solvable->evr = pool_str2id(pool, packageInfo.Version().ToString(), 1);
+ solvable->vendor = pool_str2id(pool, packageInfo.Vendor(), 1);
+ repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_SUMMARY,
+ packageInfo.Summary());
+ repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_DESCRIPTION,
+ packageInfo.Description());
+ repodata_set_str(repoData, solvable - pool->solvables, SOLVABLE_PACKAGER,
+ packageInfo.Packager());
+
+ if (!packageInfo.Checksum().IsEmpty())
+ repodata_set_checksum(repoData, solvable - pool->solvables,
+ SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, packageInfo.Checksum());
+
+ solvable->provides = repo_addid_dep(repo, solvable->provides,
+ pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0);
+
+ add_resolvables(repo, solvable->provides, packageInfo.ProvidesList());
+ add_resolvable_expressions(repo, solvable->requires,
+ packageInfo.RequiresList());
+ add_resolvable_expressions(repo, solvable->supplements,
+ packageInfo.SupplementsList());
+ add_resolvable_expressions(repo, solvable->conflicts,
+ packageInfo.ConflictsList());
+ add_resolvable_expressions(repo, solvable->enhances,
+ packageInfo.FreshensList());
+ add_replaces_list(repo, solvable->obsoletes, packageInfo.ReplacesList());
+ // TODO: Check whether freshens and replaces does indeed work as intended
+ // here.
+
+ // TODO: copyrights, licenses, URLs, source URLs
+
+ return solvableId;
+}
+
+static void add_installed_packages(Repo *repo, Repodata *repoData,
+ BPackageInstallationLocation location)
+{
+ BPackageRoster roster;
+ BPackageInfoSet packageInfos;
+ if (roster.GetActivePackages(location, packageInfos) == B_OK)
+ {
+ BRepositoryCache::Iterator it = packageInfos.GetIterator();
+ while (const BPackageInfo *packageInfo = it.Next())
+ add_package_info_to_repo(repo, repoData, *packageInfo);
+ }
+}
+
+int repo_add_haiku_installed_packages(Repo *repo, const char *rootdir,
+ int flags)
+{
+ Repodata *repoData = repo_add_repodata(repo, flags);
+
+ add_installed_packages(repo, repoData,
+ B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
+ add_installed_packages(repo, repoData, B_PACKAGE_INSTALLATION_LOCATION_HOME);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(repoData);
+
+ return 0;
+}
+
+Id repo_add_haiku_package(Repo *repo, const char *hpkgPath, int flags)
+{
+ BPackageInfo packageInfo;
+ if (packageInfo.ReadFromPackageFile(hpkgPath) != B_OK)
+ return 0;
+
+ return repo_add_haiku_package_info(repo, packageInfo, flags);
+}
+
+int repo_add_haiku_packages(Repo *repo, const char *repoName, int flags)
+{
+ BPackageRoster roster;
+ BRepositoryCache cache;
+ if (roster.GetRepositoryCache(repoName, &cache) != B_OK)
+ return 0;
+
+ Repodata *repoData = repo_add_repodata(repo, flags);
+
+ BRepositoryCache::Iterator it = cache.GetIterator();
+ while (const BPackageInfo *packageInfo = it.Next())
+ add_package_info_to_repo(repo, repoData, *packageInfo);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(repoData);
+
+ return 0;
+}
+
+Id repo_add_haiku_package_info(Repo *repo,
+ const BPackageKit::BPackageInfo &packageInfo, int flags)
+{
+ if (packageInfo.InitCheck() != B_OK)
+ return 0;
+
+ Repodata *repoData = repo_add_repodata(repo, flags);
+
+ Id id = add_package_info_to_repo(repo, repoData, packageInfo);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(repoData);
+
+ return id;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011-2013, Ingo Weinhold <ingo_weinhold@gmx.de>
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef REPO_HAIKU_H
+#define REPO_HAIKU_H
+
+#include "repo.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int repo_add_haiku_installed_packages(Repo *repo, const char *rootdir,
+ int flags);
+Id repo_add_haiku_package(Repo *repo, const char *hpkgPath, int flags);
+int repo_add_haiku_packages(Repo *repo, const char *repoName, int flags);
+
+#ifdef __cplusplus
+
+namespace BPackageKit {
+ class BPackageInfo;
+}
+
+Id repo_add_haiku_package_info(Repo *repo,
+ const BPackageKit::BPackageInfo &packageInfo, int flags);
+
+} /* extern "C" */
+
+#endif /*__cplusplus*/
+
+#endif /* REPO_HAIKU_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_helix.c
+ *
+ * Parse 'helix' XML representation
+ * and create 'repo'
+ *
+ * A bit of history: "Helix Code" was the name of the company that
+ * wrote Red Carpet. The company was later renamed to Ximian.
+ * The Red Carpet solver was merged into the ZYPP project, the
+ * library used both by ZENworks and YaST for package management.
+ * Red Carpet came with solver testcases in its own repository
+ * format, the 'helix' format.
+ *
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "repo_helix.h"
+#include "evr.h"
+
+
+/* XML parser states */
+
+enum state {
+ STATE_START,
+ STATE_CHANNEL,
+ STATE_SUBCHANNEL,
+ STATE_PACKAGE,
+ STATE_NAME,
+ STATE_VENDOR,
+ STATE_BUILDTIME,
+ STATE_HISTORY,
+ STATE_UPDATE,
+ STATE_EPOCH,
+ STATE_VERSION,
+ STATE_RELEASE,
+ STATE_ARCH,
+ STATE_PROVIDES,
+ STATE_PROVIDESENTRY,
+ STATE_REQUIRES,
+ STATE_REQUIRESENTRY,
+ STATE_PREREQUIRES,
+ STATE_PREREQUIRESENTRY,
+ 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_PRODUCT,
+
+ STATE_PEPOCH,
+ STATE_PVERSION,
+ STATE_PRELEASE,
+ STATE_PARCH,
+
+ NUMSTATES
+};
+
+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, "srcpackage", 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_SUBCHANNEL, "product", STATE_PACKAGE, 0 },
+ { STATE_SUBCHANNEL, "application", STATE_PACKAGE, 0 },
+ { STATE_PACKAGE, "name", STATE_NAME, 1 },
+ { STATE_PACKAGE, "vendor", STATE_VENDOR, 1 },
+ { STATE_PACKAGE, "buildtime", STATE_BUILDTIME, 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, "prerequires", STATE_PREREQUIRES, 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_PREREQUIRES, "dep", STATE_PREREQUIRESENTRY, 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 }
+
+};
+
+/*
+ * parser data
+ */
+
+typedef struct _parsedata {
+ int ret;
+ /* 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 */
+
+ /* repo data */
+ Pool *pool; /* current pool */
+ Repo *repo; /* current repo */
+ Repodata *data; /* current repo data */
+ Solvable *solvable; /* current solvable */
+ Offset freshens; /* current freshens vector */
+
+ /* 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 && (!*e || !strcmp(e, "0")))
+ e = 0;
+
+ 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 pool_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, Id marker)
+{
+ 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 (marker && !strcmp(*a, "pre") && a[1][0] == '1')
+ marker = SOLVABLE_PREREQMARKER;
+ }
+ if (!n) /* quit if no name found */
+ return olddeps;
+
+ /* kind, name */
+ if (k && !strcmp(k, "package"))
+ k = NULL; /* package is default */
+
+ if (k) /* if kind!=package, intern <kind>:<name> */
+ {
+ int l = strlen(k) + 1 + strlen(n) + 1;
+ if (l > pd->acontent) /* extend buffer if needed */
+ {
+ pd->content = (char *)realloc(pd->content, l + 256);
+ pd->acontent = l + 256;
+ }
+ sprintf(pd->content, "%s:%s", k, n);
+ name = pool_str2id(pool, pd->content, 1);
+ }
+ else
+ {
+ name = pool_str2id(pool, n, 1); /* package: just intern <name> */
+ }
+
+ if (f) /* operator ? */
+ {
+ /* intern e:v-r */
+ Id evr = evr_atts2id(pool, pd, atts);
+ /* parser operator to flags */
+ int flags;
+ for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
+ if (!strcmp(f, flagtab[flags].from))
+ {
+ flags = flagtab[flags].to;
+ break;
+ }
+ if (flags > 7)
+ flags = 0;
+ /* intern rel */
+ id = pool_rel2id(pool, name, evr, flags, 1);
+ }
+ else
+ id = name; /* no operator */
+
+ /* add new dependency to repo */
+ return repo_addid_dep(pd->repo, olddeps, id, marker);
+}
+
+
+/*----------------------------------------------------------------*/
+
+/*
+ * XML callback
+ * <name>
+ *
+ */
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ Parsedata *pd = (Parsedata *)userData;
+ struct stateswitch *sw;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+
+ 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 */
+ if (!pd->swtab[pd->state])
+ return;
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
+ {
+ if (!strcmp(sw->ename, name))
+ break;
+ }
+
+ /* check if we're at the right level */
+ if (sw->from != pd->state)
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s\n", name);
+#endif
+ return;
+ }
+
+ /* set new state */
+ pd->state = sw->to;
+
+ pd->docontent = sw->docontent;
+ pd->statedepth = pd->depth;
+
+ /* start with empty content */
+ /* (will collect data until end element) */
+ pd->lcontent = 0;
+ *pd->content = 0;
+
+ switch (pd->state)
+ {
+
+ case STATE_NAME:
+ if (pd->kind) /* if kind is set (non package) */
+ {
+ strcpy(pd->content, pd->kind);
+ pd->lcontent = strlen(pd->content);
+ pd->content[pd->lcontent++] = ':'; /* prefix name with '<kind>:' */
+ pd->content[pd->lcontent] = 0;
+ }
+ break;
+
+ case STATE_PACKAGE: /* solvable name */
+ pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+ 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, "product"))
+ pd->kind = "product";
+ else if (!strcmp(name, "patch"))
+ pd->kind = "patch";
+ else if (!strcmp(name, "application"))
+ pd->kind = "application";
+ else
+ pd->kind = NULL; /* default is package */
+ pd->levrspace = 1;
+ pd->epoch = 0;
+ pd->version = 0;
+ pd->release = 0;
+ pd->freshens = 0;
+#if 0
+ fprintf(stderr, "package #%d\n", s - pool->solvables);
+#endif
+ break;
+
+ case STATE_UPDATE:
+ pd->levrspace = 1;
+ pd->epoch = 0;
+ pd->version = 0;
+ pd->release = 0;
+ break;
+
+ case STATE_PROVIDES: /* start of provides */
+ s->provides = 0;
+ break;
+ case STATE_PROVIDESENTRY: /* entry within provides */
+ s->provides = adddep(pool, pd, s->provides, atts, 0);
+ break;
+ case STATE_REQUIRESENTRY:
+ s->requires = adddep(pool, pd, s->requires, atts, -SOLVABLE_PREREQMARKER);
+ break;
+ case STATE_PREREQUIRESENTRY:
+ s->requires = adddep(pool, pd, s->requires, atts, SOLVABLE_PREREQMARKER);
+ break;
+ case STATE_OBSOLETES:
+ s->obsoletes = 0;
+ break;
+ case STATE_OBSOLETESENTRY:
+ s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
+ break;
+ case STATE_CONFLICTS:
+ s->conflicts = 0;
+ break;
+ case STATE_CONFLICTSENTRY:
+ s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
+ break;
+ case STATE_RECOMMENDS:
+ s->recommends = 0;
+ break;
+ case STATE_RECOMMENDSENTRY:
+ s->recommends = adddep(pool, pd, s->recommends, atts, 0);
+ break;
+ case STATE_SUPPLEMENTS:
+ s->supplements= 0;
+ break;
+ case STATE_SUPPLEMENTSENTRY:
+ s->supplements = adddep(pool, pd, s->supplements, atts, 0);
+ break;
+ case STATE_SUGGESTS:
+ s->suggests = 0;
+ break;
+ case STATE_SUGGESTSENTRY:
+ s->suggests = adddep(pool, pd, s->suggests, atts, 0);
+ break;
+ case STATE_ENHANCES:
+ s->enhances = 0;
+ break;
+ case STATE_ENHANCESENTRY:
+ s->enhances = adddep(pool, pd, s->enhances, atts, 0);
+ break;
+ case STATE_FRESHENS:
+ pd->freshens = 0;
+ break;
+ case STATE_FRESHENSENTRY:
+ pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static const char *findKernelFlavor(Parsedata *pd, Solvable *s)
+{
+ Pool *pool = pd->pool;
+ Id pid, *pidp;
+
+ if (s->provides)
+ {
+ pidp = pd->repo->idarraydata + s->provides;
+ while ((pid = *pidp++) != 0)
+ {
+ Reldep *prd;
+ const char *depname;
+
+ if (!ISRELDEP(pid))
+ continue; /* wrong provides name */
+ prd = GETRELDEP(pool, pid);
+ depname = pool_id2str(pool, prd->name);
+ if (!strncmp(depname, "kernel-", 7))
+ return depname + 7;
+ }
+ }
+
+ if (s->requires)
+ {
+ pidp = pd->repo->idarraydata + s->requires;
+ while ((pid = *pidp++) != 0)
+ {
+ const char *depname;
+
+ if (!ISRELDEP(pid))
+ {
+ depname = pool_id2str(pool, pid);
+ }
+ else
+ {
+ Reldep *prd = GETRELDEP(pool, pid);
+ depname = pool_id2str(pool, prd->name);
+ }
+ if (!strncmp(depname, "kernel-", 7))
+ return depname + 7;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * XML callback
+ * </name>
+ *
+ * create Solvable from collected data
+ */
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ Parsedata *pd = (Parsedata *)userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ Id evr;
+ unsigned int t = 0;
+ const char *flavor;
+
+ 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 (name[0] == 's' && name[1] == 'r' && name[2] == 'c' && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->arch = ARCH_SRC;
+ 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->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, pd->freshens);
+ s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
+ pd->freshens = 0;
+
+ /* see bugzilla bnc#190163 */
+ flavor = findKernelFlavor(pd, s);
+ if (flavor)
+ {
+ char *cflavor = solv_strdup(flavor); /* make pointer safe */
+
+ Id npr;
+ Id pid;
+
+ /* this is either a kernel package or a kmp */
+ if (s->provides)
+ {
+ Offset prov = s->provides;
+ npr = 0;
+ while ((pid = pd->repo->idarraydata[prov++]) != 0)
+ {
+ const char *depname = 0;
+ Reldep *prd = 0;
+
+ if (ISRELDEP(pid))
+ {
+ prd = GETRELDEP(pool, pid);
+ depname = pool_id2str(pool, prd->name);
+ }
+ else
+ {
+ depname = pool_id2str(pool, pid);
+ }
+
+
+ if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
+ {
+ char newdep[100];
+ snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
+ pid = pool_str2id(pool, newdep, 1);
+ if (prd)
+ pid = pool_rel2id(pool, pid, prd->evr, prd->flags, 1);
+ }
+
+ npr = repo_addid_dep(pd->repo, npr, pid, 0);
+ }
+ s->provides = npr;
+ }
+#if 1
+
+ if (s->requires)
+ {
+ Offset reqs = s->requires;
+ npr = 0;
+ while ((pid = pd->repo->idarraydata[reqs++]) != 0)
+ {
+ const char *depname = 0;
+ Reldep *prd = 0;
+
+ if (ISRELDEP(pid))
+ {
+ prd = GETRELDEP(pool, pid);
+ depname = pool_id2str(pool, prd->name);
+ }
+ else
+ {
+ depname = pool_id2str(pool, pid);
+ }
+
+ if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
+ {
+ char newdep[100];
+ snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
+ pid = pool_str2id(pool, newdep, 1);
+ if (prd)
+ pid = pool_rel2id(pool, pid, prd->evr, prd->flags, 1);
+ }
+ npr = repo_addid_dep(pd->repo, npr, pid, 0);
+ }
+ s->requires = npr;
+ }
+#endif
+ free(cflavor);
+ }
+ break;
+ case STATE_NAME:
+ s->name = pool_str2id(pool, pd->content, 1);
+ break;
+ case STATE_VENDOR:
+ s->vendor = pool_str2id(pool, pd->content, 1);
+ break;
+ case STATE_BUILDTIME:
+ t = atoi (pd->content);
+ if (t)
+ repodata_set_num(pd->data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
+ 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 || pool_evrcmp(pool, s->evr, evr, EVRCMP_COMPARE) <= 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 = pool_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/repo
+ *
+ */
+
+int
+repo_add_helix(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ Parsedata pd;
+ Repodata *data;
+ char buf[BUFF_SIZE];
+ int i, l;
+ struct stateswitch *sw;
+ unsigned int now;
+ XML_Parser parser;
+
+ now = solv_timems(0);
+ data = repo_add_repodata(repo, flags);
+
+ /* 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.repo = repo;
+
+ 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;
+ pd.data = data;
+
+ /* set up 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)
+ {
+ pd.ret = pool_error(pool, -1, "%s at line %u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
+ break;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+ free(pd.content);
+ free(pd.evrspace);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_helix took %d ms\n", solv_timems(now));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
+ return pd.ret;
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_helix.h
+ *
+ */
+
+#ifndef LIBSOLV_REPO_HELIX_H
+#define LIBSOLV_REPO_HELIX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include "pool.h"
+#include "repo.h"
+
+extern int repo_add_helix(Repo *repo, FILE *fp, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* LIBSOLV_REPO_HELIX_H */
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "chksum.h"
+#include "repo_mdk.h"
+
+static Offset
+parse_deps(Solvable *s, char *bp, Id marker)
+{
+ Pool *pool = s->repo->pool;
+ Offset deps = 0;
+ char *nbp, *ebp;
+ for (; bp; bp = nbp)
+ {
+ int ispre = 0;
+ Id id, evr = 0;
+ int flags = 0;
+
+ nbp = strchr(bp, '@');
+ if (!nbp)
+ ebp = bp + strlen(bp);
+ else
+ {
+ ebp = nbp;
+ *nbp++ = 0;
+ }
+ if (ebp[-1] == ']')
+ {
+ char *sbp = ebp - 1;
+ while (sbp >= bp && *sbp != '[')
+ sbp--;
+ if (sbp >= bp && sbp[1] != '*')
+ {
+ char *fbp;
+ for (fbp = sbp + 1;; fbp++)
+ {
+ if (*fbp == '>')
+ flags |= REL_GT;
+ else if (*fbp == '=')
+ flags |= REL_EQ;
+ else if (*fbp == '<')
+ flags |= REL_LT;
+ else
+ break;
+ }
+ if (*fbp == ' ')
+ fbp++;
+ evr = pool_strn2id(pool, fbp, ebp - 1 - fbp, 1);
+ ebp = sbp;
+ }
+ }
+ if (ebp[-1] == ']' && ebp >= bp + 3 && !strncmp(ebp - 3, "[*]", 3))
+ {
+ ispre = 1;
+ ebp -= 3;
+ }
+ id = pool_strn2id(pool, bp, ebp - bp, 1);
+ if (evr)
+ id = pool_rel2id(pool, id, evr, flags, 1);
+ deps = repo_addid_dep(s->repo, deps, id, ispre ? marker : 0);
+ bp = nbp;
+ }
+ return deps;
+}
+
+int
+repo_add_mdk(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ Solvable *s;
+ char *buf;
+ int bufa, bufl;
+
+ data = repo_add_repodata(repo, flags);
+ bufa = 4096;
+ buf = solv_malloc(bufa);
+ bufl = 0;
+ s = 0;
+ while (fgets(buf + bufl, bufa - bufl, fp) > 0)
+ {
+ bufl += strlen(buf + bufl);
+ if (!bufl)
+ continue;
+ if (buf[bufl - 1] != '\n')
+ {
+ if (bufa - bufl < 256)
+ {
+ bufa += 4096;
+ buf = solv_realloc(buf, bufa);
+ }
+ continue;
+ }
+ buf[bufl - 1] = 0;
+ bufl = 0;
+ if (buf[0] != '@')
+ {
+ pool_debug(pool, SOLV_ERROR, "bad line <%s>\n", buf);
+ continue;
+ }
+ if (!s)
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ if (!strncmp(buf + 1, "filesize@", 9))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(buf + 10, 0, 10));
+ else if (!strncmp(buf + 1, "summary@", 8))
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, buf + 9);
+ else if (!strncmp(buf + 1, "provides@", 9))
+ s->provides = parse_deps(s, buf + 10, 0);
+ else if (!strncmp(buf + 1, "requires@", 9))
+ s->requires = parse_deps(s, buf + 10, SOLVABLE_PREREQMARKER);
+ else if (!strncmp(buf + 1, "recommends@", 11))
+ s->recommends = parse_deps(s, buf + 10, 0);
+ else if (!strncmp(buf + 1, "suggests@", 9))
+ s->suggests = parse_deps(s, buf + 10, 0);
+ else if (!strncmp(buf + 1, "obsoletes@", 10))
+ s->obsoletes = parse_deps(s, buf + 11, 0);
+ else if (!strncmp(buf + 1, "conflicts@", 10))
+ s->conflicts = parse_deps(s, buf + 11, 0);
+ else if (!strncmp(buf + 1, "info@", 5))
+ {
+ char *nvra = buf + 6;
+ char *epochstr;
+ char *arch;
+ char *version;
+ char *filename;
+ char *disttag = 0;
+ char *distepoch = 0;
+ if ((epochstr = strchr(nvra, '@')) != 0)
+ {
+ char *sizestr;
+ *epochstr++ = 0;
+ if ((sizestr = strchr(epochstr, '@')) != 0)
+ {
+ char *groupstr;
+ *sizestr++ = 0;
+ if ((groupstr = strchr(sizestr, '@')) != 0)
+ {
+ *groupstr++ = 0;
+ if ((disttag = strchr(groupstr, '@')) != 0)
+ {
+ *disttag++ = 0;
+ if ((distepoch = strchr(disttag, '@')) != 0)
+ {
+ char *n;
+ *distepoch++ = 0;
+ if ((n = strchr(distepoch, '@')) != 0)
+ *n = 0;
+ }
+ }
+ if (*groupstr)
+ repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_GROUP, groupstr);
+ }
+ if (*sizestr)
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(sizestr, 0, 10));
+ }
+ }
+ filename = pool_tmpjoin(pool, nvra, ".rpm", 0);
+ arch = strrchr(nvra, '.');
+ if (arch)
+ {
+ *arch++ = 0;
+ s->arch = pool_str2id(pool, arch, 1);
+ }
+ if (disttag && *disttag)
+ {
+ /* strip disttag from release */
+ char *n = strrchr(nvra, '-');
+ if (n && !strncmp(n + 1, disttag, strlen(disttag)))
+ *n = 0;
+ }
+ if (distepoch && *distepoch)
+ {
+ /* add distepoch */
+ int le = strlen(distepoch);
+ int ln = strlen(nvra);
+ nvra[ln++] = ':';
+ memmove(nvra + ln, distepoch, le); /* may overlap */
+ nvra[le + ln] = 0;
+ }
+ version = strrchr(nvra, '-');
+ if (version)
+ {
+ char *release = version;
+ *release = 0;
+ version = strrchr(nvra, '-');
+ *release = '-';
+ if (!version)
+ version = release;
+ *version++ = 0;
+ }
+ else
+ version = "";
+ s->name = pool_str2id(pool, nvra, 1);
+ if (epochstr && *epochstr && strcmp(epochstr, "0") != 0)
+ {
+ char *evr = pool_tmpjoin(pool, epochstr, ":", version);
+ s->evr = pool_str2id(pool, evr, 1);
+ }
+ else
+ s->evr = pool_str2id(pool, version, 1);
+ repodata_set_location(data, s - pool->solvables, 0, 0, filename);
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ s = 0;
+ }
+ else
+ {
+ char *tagend = strchr(buf + 1, '@');
+ if (tagend)
+ *tagend = 0;
+ pool_debug(pool, SOLV_ERROR, "unknown tag <%s>\n", buf + 1);
+ continue;
+ }
+ }
+ if (s)
+ {
+ pool_debug(pool, SOLV_ERROR, "unclosed package at EOF\n");
+ repo_free_solvable(s->repo, s - pool->solvables, 1);
+ }
+ solv_free(buf);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+enum state {
+ STATE_START,
+ STATE_MEDIA_INFO,
+ STATE_INFO,
+ STATE_FILES,
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+/* must be sorted by first column */
+static struct stateswitch stateswitches[] = {
+ { STATE_START, "media_info", STATE_MEDIA_INFO, 0 },
+ { STATE_MEDIA_INFO, "info", STATE_INFO, 1 },
+ { STATE_MEDIA_INFO, "files", STATE_FILES, 1 },
+ { NUMSTATES }
+};
+
+struct parsedata {
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+ Solvable *solvable;
+ Hashtable joinhash;
+ Hashval joinhashmask;
+};
+
+static inline const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ }
+ return 0;
+}
+
+static Hashtable
+joinhash_init(Repo *repo, Hashval *hmp)
+{
+ Hashval hm = mkmask(repo->nsolvables);
+ Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
+ Hashval h, hh;
+ Solvable *s;
+ int i;
+
+ FOR_REPO_SOLVABLES(repo, i, s)
+ {
+ hh = HASHCHAIN_START;
+ h = s->name & hm;
+ while (ht[h])
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ ht[h] = i;
+ }
+ *hmp = hm;
+ return ht;
+}
+
+static Solvable *
+joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, const char *fn, const char *distepoch)
+{
+ Hashval h, hh;
+ const char *p, *vrstart, *vrend;
+ Id name, arch;
+
+ if (!fn || !*fn)
+ return 0;
+ if (distepoch && !*distepoch)
+ distepoch = 0;
+ p = fn + strlen(fn);
+ while (--p > fn)
+ if (*p == '.')
+ break;
+ if (p == fn)
+ return 0;
+ arch = pool_str2id(repo->pool, p + 1, 0);
+ if (!arch)
+ return 0;
+ if (distepoch)
+ {
+ while (--p > fn)
+ if (*p == '-')
+ break;
+ if (p == fn)
+ return 0;
+ }
+ vrend = p;
+ while (--p > fn)
+ if (*p == '-')
+ break;
+ if (p == fn)
+ return 0;
+ while (--p > fn)
+ if (*p == '-')
+ break;
+ if (p == fn)
+ return 0;
+ vrstart = p + 1;
+ name = pool_strn2id(repo->pool, fn, p - fn, 0);
+ if (!name)
+ return 0;
+ hh = HASHCHAIN_START;
+ h = name & hm;
+ while (ht[h])
+ {
+ Solvable *s = repo->pool->solvables + ht[h];
+ if (s->name == name && s->arch == arch)
+ {
+ /* too bad we don't know the epoch... */
+ const char *evr = pool_id2str(repo->pool, s->evr);
+ for (p = evr; *p >= '0' && *p <= '9'; p++)
+ ;
+ if (p > evr && *p == ':')
+ evr = p + 1;
+ if (distepoch)
+ {
+ if (!strncmp(evr, vrstart, vrend - vrstart) && evr[vrend - vrstart] == ':' && !strcmp(distepoch, evr + (vrend - vrstart + 1)))
+ return s;
+ }
+ else if (!strncmp(evr, vrstart, vrend - vrstart) && evr[vrend - vrstart] == 0)
+ return s;
+ }
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ return 0;
+}
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ struct stateswitch *sw;
+
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+ pd->depth++;
+ if (!pd->swtab[pd->state])
+ return;
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
+ if (!strcmp(sw->ename, name))
+ break;
+ if (sw->from != pd->state)
+ return;
+ pd->state = sw->to;
+ pd->docontent = sw->docontent;
+ pd->statedepth = pd->depth;
+ pd->lcontent = 0;
+ *pd->content = 0;
+ switch (pd->state)
+ {
+ case STATE_INFO:
+ {
+ const char *fn = find_attr("fn", atts);
+ const char *distepoch = find_attr("distepoch", atts);
+ const char *str;
+ pd->solvable = joinhash_lookup(pd->repo, pd->joinhash, pd->joinhashmask, fn, distepoch);
+ if (!pd->solvable)
+ break;
+ str = find_attr("url", atts);
+ if (str && *str)
+ repodata_set_str(pd->data, pd->solvable - pool->solvables, SOLVABLE_URL, str);
+ str = find_attr("license", atts);
+ if (str && *str)
+ repodata_set_poolstr(pd->data, pd->solvable - pool->solvables, SOLVABLE_LICENSE, str);
+ str = find_attr("sourcerpm", atts);
+ if (str && *str)
+ repodata_set_sourcepkg(pd->data, pd->solvable - pool->solvables, str);
+ break;
+ }
+ case STATE_FILES:
+ {
+ const char *fn = find_attr("fn", atts);
+ const char *distepoch = find_attr("distepoch", atts);
+ pd->solvable = joinhash_lookup(pd->repo, pd->joinhash, pd->joinhashmask, fn, distepoch);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ Solvable *s = pd->solvable;
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+ return;
+ }
+ pd->depth--;
+ pd->statedepth--;
+ switch (pd->state)
+ {
+ case STATE_INFO:
+ if (s && *pd->content)
+ repodata_set_str(pd->data, s - pd->pool->solvables, SOLVABLE_DESCRIPTION, pd->content);
+ break;
+ case STATE_FILES:
+ if (s && *pd->content)
+ {
+ char *np, *p, *sl;
+ for (p = pd->content; p && *p; p = np)
+ {
+ Id id;
+ np = strchr(p, '\n');
+ if (np)
+ *np++ = 0;
+ if (!*p)
+ continue;
+ sl = strrchr(p, '/');
+ if (sl)
+ {
+ *sl++ = 0;
+ id = repodata_str2dir(pd->data, p, 1);
+ }
+ else
+ {
+ sl = p;
+ id = 0;
+ }
+ if (!id)
+ id = repodata_str2dir(pd->data, "/", 1);
+ repodata_add_dirstr(pd->data, s - pd->pool->solvables, SOLVABLE_FILELIST, id, sl);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+}
+
+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 = solv_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
+
+int
+repo_add_mdk_info(Repo *repo, FILE *fp, int flags)
+{
+ Repodata *data;
+ struct parsedata pd;
+ char buf[BUFF_SIZE];
+ int i, l;
+ struct stateswitch *sw;
+ XML_Parser parser;
+
+ if (!(flags & REPO_EXTEND_SOLVABLES))
+ {
+ pool_debug(repo->pool, SOLV_ERROR, "repo_add_mdk_info: can only extend existing solvables\n");
+ return -1;
+ }
+
+ data = repo_add_repodata(repo, flags);
+
+ memset(&pd, 0, sizeof(pd));
+ pd.repo = repo;
+ pd.pool = repo->pool;
+ pd.data = data;
+
+ pd.content = solv_malloc(256);
+ pd.acontent = 256;
+
+ pd.joinhash = joinhash_init(repo, &pd.joinhashmask);
+
+ 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;
+ }
+
+ 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)
+ {
+ pool_debug(pd.pool, SOLV_ERROR, "%s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ break;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+ solv_free(pd.content);
+ solv_free(pd.joinhash);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_mdk(Repo *repo, FILE *fp, int flags);
+extern int repo_add_mdk_info(Repo *repo, FILE *fp, int flags);
+
--- /dev/null
+/*
+ * repo_products.c
+ *
+ * Parses all files below 'proddir'
+ * See http://en.opensuse.org/Product_Management/Code11
+ *
+ *
+ * Copyright (c) 2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <dirent.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#define DISABLE_SPLIT
+#include "tools_util.h"
+#include "repo_content.h"
+#include "repo_zyppdb.h"
+#include "repo_products.h"
+#include "repo_releasefile_products.h"
+
+
+enum state {
+ STATE_START,
+ STATE_PRODUCT,
+ STATE_VENDOR,
+ STATE_NAME,
+ STATE_VERSION,
+ STATE_RELEASE,
+ STATE_ARCH,
+ STATE_SUMMARY,
+ STATE_SHORTSUMMARY,
+ STATE_DESCRIPTION,
+ STATE_UPDATEREPOKEY,
+ STATE_CPEID,
+ STATE_URLS,
+ STATE_URL,
+ STATE_RUNTIMECONFIG,
+ STATE_LINGUAS,
+ STATE_LANG,
+ STATE_REGISTER,
+ STATE_TARGET,
+ STATE_REGRELEASE,
+ STATE_REGFLAVOR,
+ STATE_PRODUCTLINE,
+ STATE_REGUPDATES,
+ STATE_REGUPDREPO,
+ STATE_ENDOFLIFE,
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+/* !! must be sorted by first column !! */
+static struct stateswitch stateswitches[] = {
+ { STATE_START, "product", STATE_PRODUCT, 0 },
+ { STATE_PRODUCT, "vendor", STATE_VENDOR, 1 },
+ { STATE_PRODUCT, "name", STATE_NAME, 1 },
+ { STATE_PRODUCT, "version", STATE_VERSION, 1 },
+ { STATE_PRODUCT, "release", STATE_RELEASE, 1 },
+ { STATE_PRODUCT, "arch", STATE_ARCH, 1 },
+ { STATE_PRODUCT, "productline", STATE_PRODUCTLINE, 1 },
+ { STATE_PRODUCT, "summary", STATE_SUMMARY, 1 },
+ { STATE_PRODUCT, "shortsummary", STATE_SHORTSUMMARY, 1 },
+ { STATE_PRODUCT, "description", STATE_DESCRIPTION, 1 },
+ { STATE_PRODUCT, "register", STATE_REGISTER, 0 },
+ { STATE_PRODUCT, "urls", STATE_URLS, 0 },
+ { STATE_PRODUCT, "runtimeconfig", STATE_RUNTIMECONFIG, 0 },
+ { STATE_PRODUCT, "linguas", STATE_LINGUAS, 0 },
+ { STATE_PRODUCT, "updaterepokey", STATE_UPDATEREPOKEY, 1 },
+ { STATE_PRODUCT, "cpeid", STATE_CPEID, 1 },
+ { STATE_PRODUCT, "endoflife", STATE_ENDOFLIFE, 1 },
+ { STATE_URLS, "url", STATE_URL, 1 },
+ { STATE_LINGUAS, "lang", STATE_LANG, 0 },
+ { STATE_REGISTER, "target", STATE_TARGET, 1 },
+ { STATE_REGISTER, "release", STATE_REGRELEASE, 1 },
+ { STATE_REGISTER, "flavor", STATE_REGFLAVOR, 1 },
+ { STATE_REGISTER, "updates", STATE_REGUPDATES, 0 },
+ { STATE_REGUPDATES, "repository", STATE_REGUPDREPO, 0 },
+ { NUMSTATES }
+};
+
+struct parsedata {
+ const char *filename;
+ const char *basename;
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+ struct joindata jd;
+
+ const char *tmplang;
+
+ const char *tmpvers;
+ const char *tmprel;
+ Id urltype;
+
+ unsigned int ctime;
+
+ Solvable *solvable;
+ Id handle;
+
+ ino_t baseproduct;
+ ino_t currentproduct;
+ int productscheme;
+};
+
+
+/*
+ * find_attr
+ * find value for xml attribute
+ * I: txt, name of attribute
+ * I: atts, list of key/value attributes
+ * O: pointer to value of matching key, or NULL
+ *
+ */
+
+static inline const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ }
+ return 0;
+}
+
+static time_t
+datestr2timestamp(const char *date)
+{
+ const char *p;
+ struct tm tm;
+
+ if (!date || !*date)
+ return 0;
+ for (p = date; *p >= '0' && *p <= '9'; p++)
+ ;
+ if (!*p)
+ return atoi(date);
+ memset(&tm, 0, sizeof(tm));
+ p = strptime(date, "%F%T", &tm);
+ if (!p)
+ {
+ memset(&tm, 0, sizeof(tm));
+ p = strptime(date, "%F", &tm);
+ if (!p || *p)
+ return 0;
+ }
+ return timegm(&tm);
+}
+
+/*
+ * XML callback: startElement
+ */
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ struct stateswitch *sw;
+
+#if 0
+ fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ pd->depth++;
+ if (!pd->swtab[pd->state]) /* no statetable -> no substates */
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#endif
+ return;
+ }
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
+ if (!strcmp(sw->ename, name))
+ break;
+
+ if (sw->from != pd->state)
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#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_PRODUCT:
+ /* parse 'schemeversion' and store in global variable */
+ {
+ const char * scheme = find_attr("schemeversion", atts);
+ pd->productscheme = (scheme && *scheme) ? atoi(scheme) : -1;
+ }
+ if (!s)
+ {
+ s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+ pd->handle = s - pool->solvables;
+ }
+ break;
+
+ /* <summary lang="xy">... */
+ case STATE_SUMMARY:
+ case STATE_DESCRIPTION:
+ pd->tmplang = join_dup(&pd->jd, find_attr("lang", atts));
+ break;
+ case STATE_URL:
+ pd->urltype = pool_str2id(pd->pool, find_attr("name", atts), 1);
+ break;
+ case STATE_REGUPDREPO:
+ {
+ const char *repoid = find_attr("repoid", atts);
+ if (repoid && *repoid)
+ {
+ Id h = repodata_new_handle(pd->data);
+ repodata_set_str(pd->data, h, PRODUCT_UPDATES_REPOID, repoid);
+ repodata_add_flexarray(pd->data, pd->handle, PRODUCT_UPDATES, h);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ Solvable *s = pd->solvable;
+
+#if 0
+ fprintf(stderr, "end: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+#if 0
+ fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+#endif
+ return;
+ }
+
+ pd->depth--;
+ pd->statedepth--;
+
+ switch (pd->state)
+ {
+ case STATE_PRODUCT:
+ /* product done, finish solvable */
+ if (pd->ctime)
+ repodata_set_num(pd->data, pd->handle, SOLVABLE_INSTALLTIME, pd->ctime);
+
+ if (pd->basename)
+ repodata_set_str(pd->data, pd->handle, PRODUCT_REFERENCEFILE, pd->basename);
+
+ /* this is where <productsdir>/baseproduct points to */
+ if (pd->currentproduct == pd->baseproduct)
+ repodata_set_str(pd->data, pd->handle, PRODUCT_TYPE, "base");
+
+ if (pd->tmprel)
+ {
+ if (pd->tmpvers)
+ s->evr = makeevr(pd->pool, join2(&pd->jd, pd->tmpvers, "-", pd->tmprel));
+ else
+ {
+ fprintf(stderr, "Seen <release> but no <version>\n");
+ }
+ }
+ else if (pd->tmpvers)
+ s->evr = makeevr(pd->pool, pd->tmpvers); /* just version, no release */
+ pd->tmpvers = solv_free((void *)pd->tmpvers);
+ pd->tmprel = solv_free((void *)pd->tmprel);
+ if (!s->arch)
+ s->arch = ARCH_NOARCH;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
+ pd->solvable = 0;
+ break;
+ case STATE_VENDOR:
+ s->vendor = pool_str2id(pd->pool, pd->content, 1);
+ break;
+ case STATE_NAME:
+ s->name = pool_str2id(pd->pool, join2(&pd->jd, "product", ":", pd->content), 1);
+ break;
+ case STATE_VERSION:
+ pd->tmpvers = solv_strdup(pd->content);
+ break;
+ case STATE_RELEASE:
+ pd->tmprel = solv_strdup(pd->content);
+ break;
+ case STATE_ARCH:
+ s->arch = pool_str2id(pd->pool, pd->content, 1);
+ break;
+ case STATE_PRODUCTLINE:
+ repodata_set_str(pd->data, pd->handle, PRODUCT_PRODUCTLINE, pd->content);
+ break;
+ case STATE_UPDATEREPOKEY:
+ /** obsolete **/
+ break;
+ case STATE_SUMMARY:
+ repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_SUMMARY, pd->tmplang, 1), pd->content);
+ break;
+ case STATE_SHORTSUMMARY:
+ repodata_set_str(pd->data, pd->handle, PRODUCT_SHORTLABEL, pd->content);
+ break;
+ case STATE_DESCRIPTION:
+ repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_DESCRIPTION, pd->tmplang, 1), pd->content);
+ break;
+ case STATE_URL:
+ if (pd->urltype)
+ {
+ repodata_add_poolstr_array(pd->data, pd->handle, PRODUCT_URL, pd->content);
+ repodata_add_idarray(pd->data, pd->handle, PRODUCT_URL_TYPE, pd->urltype);
+ }
+ break;
+ case STATE_TARGET:
+ repodata_set_str(pd->data, pd->handle, PRODUCT_REGISTER_TARGET, pd->content);
+ break;
+ case STATE_REGRELEASE:
+ repodata_set_str(pd->data, pd->handle, PRODUCT_REGISTER_RELEASE, pd->content);
+ break;
+ case STATE_REGFLAVOR:
+ repodata_set_str(pd->data, pd->handle, PRODUCT_REGISTER_FLAVOR, pd->content);
+ break;
+ case STATE_CPEID:
+ if (*pd->content)
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_CPEID, pd->content);
+ break;
+ case STATE_ENDOFLIFE:
+ if (*pd->content)
+ {
+ time_t t = datestr2timestamp(pd->content);
+ if (t)
+ repodata_set_num(pd->data, pd->handle, PRODUCT_ENDOFLIFE, (unsigned long long)t);
+ }
+ break;
+ default:
+ break;
+ }
+
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+
+#if 0
+ fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
+#endif
+}
+
+
+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 = solv_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
+
+
+/*
+ * add single product to repo
+ *
+ */
+
+static void
+add_code11_product(struct parsedata *pd, FILE *fp)
+{
+ char buf[BUFF_SIZE];
+ int l;
+ struct stat st;
+ XML_Parser parser;
+
+ if (!fstat(fileno(fp), &st))
+ {
+ pd->currentproduct = st.st_ino;
+ pd->ctime = (unsigned int)st.st_ctime;
+ }
+ else
+ {
+ pd->currentproduct = pd->baseproduct + 1; /* make it != baseproduct if stat fails */
+ pool_error(pd->pool, 0, "fstat: %s", strerror(errno));
+ pd->ctime = 0;
+ }
+
+ 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)
+ {
+ pool_debug(pd->pool, SOLV_ERROR, "%s: %s at line %u:%u\n", pd->filename, XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ XML_ParserFree(parser);
+ if (pd->solvable)
+ {
+ repo_free_solvable(pd->repo, pd->solvable - pd->pool->solvables, 1);
+ pd->solvable = 0;
+ }
+ return;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+}
+
+
+int
+repo_add_code11_products(Repo *repo, const char *dirpath, int flags)
+{
+ Repodata *data;
+ struct parsedata pd;
+ struct stateswitch *sw;
+ DIR *dir;
+ int i;
+
+ data = repo_add_repodata(repo, flags);
+
+ memset(&pd, 0, sizeof(pd));
+ pd.repo = repo;
+ pd.pool = repo->pool;
+ pd.data = data;
+
+ pd.content = solv_malloc(256);
+ pd.acontent = 256;
+
+ 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;
+ }
+
+ if (flags & REPO_USE_ROOTDIR)
+ dirpath = pool_prepend_rootdir(repo->pool, dirpath);
+ dir = opendir(dirpath);
+ if (dir)
+ {
+ struct dirent *entry;
+ struct stat st;
+ char *fullpath;
+
+ /* check for <productsdir>/baseproduct on code11 and remember its target inode */
+ if (stat(join2(&pd.jd, dirpath, "/", "baseproduct"), &st) == 0) /* follow symlink */
+ pd.baseproduct = st.st_ino;
+ else
+ pd.baseproduct = 0;
+
+ while ((entry = readdir(dir)))
+ {
+ int len = strlen(entry->d_name);
+ FILE *fp;
+ if (len <= 5 || strcmp(entry->d_name + len - 5, ".prod") != 0)
+ continue;
+ fullpath = join2(&pd.jd, dirpath, "/", entry->d_name);
+ fp = fopen(fullpath, "r");
+ if (!fp)
+ {
+ pool_error(repo->pool, 0, "%s: %s", fullpath, strerror(errno));
+ continue;
+ }
+ pd.filename = fullpath;
+ pd.basename = entry->d_name;
+ add_code11_product(&pd, fp);
+ fclose(fp);
+ }
+ closedir(dir);
+ }
+ solv_free(pd.content);
+ join_freemem(&pd.jd);
+ if (flags & REPO_USE_ROOTDIR)
+ solv_free((char *)dirpath);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+
+/******************************************************************************************/
+
+
+/*
+ * read all installed products
+ *
+ * try proddir (reading all .xml files from this directory) first
+ * if not available, assume non-code11 layout and parse /etc/xyz-release
+ *
+ * parse each one as a product
+ */
+
+/* Oh joy! Three parsers for the price of one! */
+
+int
+repo_add_products(Repo *repo, const char *proddir, int flags)
+{
+ const char *fullpath;
+ DIR *dir;
+
+ if (proddir)
+ {
+ dir = opendir(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(repo->pool, proddir) : proddir);
+ if (dir)
+ {
+ /* assume code11 stype products */
+ closedir(dir);
+ return repo_add_code11_products(repo, proddir, flags);
+ }
+ }
+
+ /* code11 didn't work, try old code10 zyppdb */
+ fullpath = "/var/lib/zypp/db/products";
+ if (flags & REPO_USE_ROOTDIR)
+ fullpath = pool_prepend_rootdir_tmp(repo->pool, fullpath);
+ dir = opendir(fullpath);
+ if (dir)
+ {
+ closedir(dir);
+ /* assume code10 style products */
+ return repo_add_zyppdb_products(repo, "/var/lib/zypp/db/products", flags);
+ }
+
+ /* code10/11 didn't work, try -release files parsing */
+ fullpath = "/etc";
+ if (flags & REPO_USE_ROOTDIR)
+ fullpath = pool_prepend_rootdir_tmp(repo->pool, fullpath);
+ dir = opendir(fullpath);
+ if (dir)
+ {
+ closedir(dir);
+ return repo_add_releasefile_products(repo, "/etc", flags);
+ }
+
+ /* no luck. check if the rootdir exists */
+ fullpath = pool_get_rootdir(repo->pool);
+ if (fullpath && *fullpath)
+ {
+ dir = opendir(fullpath);
+ if (!dir)
+ return pool_error(repo->pool, -1, "%s: %s", fullpath, strerror(errno));
+ closedir(dir);
+ }
+
+ /* the least we can do... */
+ if (!(flags & REPO_NO_INTERNALIZE) && (flags & REPO_REUSE_REPODATA) != 0)
+ repodata_internalize(repo_last_repodata(repo));
+ return 0;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_code11_products(Repo *repo, const char *dirpath, int flags);
+extern int repo_add_products(Repo *repo, const char *proddir, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2007-2013, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_pubkey
+ *
+ * support for pubkey reading
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdint.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include <rpm/rpmio.h>
+#include <rpm/rpmpgp.h>
+#ifndef RPM5
+#include <rpm/header.h>
+#endif
+#include <rpm/rpmdb.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "hash.h"
+#include "util.h"
+#include "queue.h"
+#include "chksum.h"
+#include "repo_rpmdb.h"
+#include "repo_pubkey.h"
+#ifdef ENABLE_PGPVRFY
+#include "solv_pgpvrfy.h"
+#endif
+
+static void
+setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
+{
+ if (str[solv_validutf8(str)])
+ {
+ char *ustr = solv_latin1toutf8(str); /* not utf8, assume latin1 */
+ repodata_set_str(repodata, handle, tag, ustr);
+ solv_free(ustr);
+ }
+ else
+ repodata_set_str(repodata, handle, tag, str);
+}
+
+static char *
+r64dec1(char *p, unsigned int *vp, int *eofp)
+{
+ int i, x;
+ unsigned int v = 0;
+
+ for (i = 0; i < 4; )
+ {
+ x = *p++;
+ if (!x)
+ return 0;
+ if (x >= 'A' && x <= 'Z')
+ x -= 'A';
+ else if (x >= 'a' && x <= 'z')
+ x -= 'a' - 26;
+ else if (x >= '0' && x <= '9')
+ x -= '0' - 52;
+ else if (x == '+')
+ x = 62;
+ else if (x == '/')
+ x = 63;
+ else if (x == '=')
+ {
+ x = 0;
+ if (i == 0)
+ {
+ *eofp = 3;
+ *vp = 0;
+ return p - 1;
+ }
+ *eofp += 1;
+ }
+ else
+ continue;
+ v = v << 6 | x;
+ i++;
+ }
+ *vp = v;
+ return p;
+}
+
+static unsigned int
+crc24(unsigned char *p, int len)
+{
+ unsigned int crc = 0xb704ce;
+ int i;
+
+ while (len--)
+ {
+ crc ^= (*p++) << 16;
+ for (i = 0; i < 8; i++)
+ if ((crc <<= 1) & 0x1000000)
+ crc ^= 0x1864cfb;
+ }
+ return crc & 0xffffff;
+}
+
+static int
+unarmor(char *pubkey, unsigned char **pktp, int *pktlp, const char *startstr, const char *endstr)
+{
+ char *p, *pubkeystart = pubkey;
+ int l, eof;
+ unsigned char *buf, *bp;
+ unsigned int v;
+
+ *pktp = 0;
+ *pktlp = 0;
+ if (!pubkey)
+ return 0;
+ l = strlen(startstr);
+ while (strncmp(pubkey, startstr, l) != 0)
+ {
+ pubkey = strchr(pubkey, '\n');
+ if (!pubkey)
+ return 0;
+ pubkey++;
+ }
+ pubkey = strchr(pubkey, '\n');
+ if (!pubkey++)
+ return 0;
+ /* skip header lines */
+ for (;;)
+ {
+ while (*pubkey == ' ' || *pubkey == '\t')
+ pubkey++;
+ if (*pubkey == '\n')
+ break;
+ pubkey = strchr(pubkey, '\n');
+ if (!pubkey++)
+ return 0;
+ }
+ pubkey++;
+ p = strchr(pubkey, '=');
+ if (!p)
+ return 0;
+ l = p - pubkey;
+ bp = buf = solv_malloc(l * 3 / 4 + 4);
+ eof = 0;
+ while (!eof)
+ {
+ pubkey = r64dec1(pubkey, &v, &eof);
+ if (!pubkey)
+ {
+ solv_free(buf);
+ return 0;
+ }
+ *bp++ = v >> 16;
+ *bp++ = v >> 8;
+ *bp++ = v;
+ }
+ while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
+ pubkey++;
+ bp -= eof;
+ if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
+ {
+ solv_free(buf);
+ return 0;
+ }
+ if (v != crc24(buf, bp - buf))
+ {
+ solv_free(buf);
+ return 0;
+ }
+ while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
+ pubkey++;
+ if (strncmp(pubkey, endstr, strlen(endstr)) != 0)
+ {
+ solv_free(buf);
+ return 0;
+ }
+ p = strchr(pubkey, '\n');
+ if (!p)
+ p = pubkey + strlen(pubkey);
+ *pktp = buf;
+ *pktlp = bp - buf;
+ return (p ? p + 1 : pubkey + strlen(pubkey)) - pubkeystart;
+}
+
+#define ARMOR_NLAFTER 16
+
+static char *
+armor(unsigned char *pkt, int pktl, const char *startstr, const char *endstr, const char *version)
+{
+ static const char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ char *str = solv_malloc(strlen(startstr) + strlen(endstr) + strlen(version) + (pktl / 3) * 4 + (pktl / (ARMOR_NLAFTER * 3)) + 30);
+ char *p = str;
+ int a, b, c, i;
+ unsigned int v;
+
+ v = crc24(pkt, pktl);
+ sprintf(p, "%s\nVersion: %s\n\n", startstr, version);
+ p += strlen(p);
+ for (i = -1; pktl > 0; pktl -= 3)
+ {
+ if (++i == ARMOR_NLAFTER)
+ {
+ i = 0;
+ *p++ = '\n';
+ }
+ a = *pkt++;
+ b = pktl > 1 ? *pkt++ : 0;
+ c = pktl > 2 ? *pkt++ : 0;
+ *p++ = bintoasc[a >> 2];
+ *p++ = bintoasc[(a & 3) << 4 | b >> 4];
+ *p++ = pktl > 1 ? bintoasc[(b & 15) << 2 | c >> 6] : '=';
+ *p++ = pktl > 2 ? bintoasc[c & 63] : '=';
+ }
+ *p++ = '\n';
+ *p++ = '=';
+ *p++ = bintoasc[v >> 18 & 0x3f];
+ *p++ = bintoasc[v >> 12 & 0x3f];
+ *p++ = bintoasc[v >> 6 & 0x3f];
+ *p++ = bintoasc[v & 0x3f];
+ sprintf(p, "\n%s\n", endstr);
+ return str;
+}
+
+/* internal representation of a signature */
+struct pgpsig {
+ int type;
+ Id hashalgo;
+ unsigned char issuer[8];
+ int haveissuer;
+ unsigned int created;
+ unsigned int expires;
+ unsigned int keyexpires;
+ unsigned char *sigdata;
+ int sigdatal;
+ int mpioff;
+};
+
+static Id
+pgphashalgo2type(int algo)
+{
+ if (algo == 1)
+ return REPOKEY_TYPE_MD5;
+ if (algo == 2)
+ return REPOKEY_TYPE_SHA1;
+ if (algo == 8)
+ return REPOKEY_TYPE_SHA256;
+ if (algo == 9)
+ return REPOKEY_TYPE_SHA384;
+ if (algo == 10)
+ return REPOKEY_TYPE_SHA512;
+ if (algo == 11)
+ return REPOKEY_TYPE_SHA224;
+ return 0;
+}
+
+/* hash the pubkey/userid data for self-sig verification
+ * hash the final trailer
+ * create a "sigdata" block suitable for a call to solv_pgpverify */
+static void
+pgpsig_makesigdata(struct pgpsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl, Chksum *h)
+{
+ int type = sig->type;
+ unsigned char b[6];
+ const unsigned char *cs;
+ int csl;
+
+ if (!h || sig->mpioff < 2 || l <= sig->mpioff)
+ return;
+ if ((type >= 0x10 && type <= 0x13) || type == 0x1f || type == 0x18 || type == 0x20 || type == 0x28)
+ {
+ b[0] = 0x99;
+ b[1] = pubkeyl >> 8;
+ b[2] = pubkeyl;
+ solv_chksum_add(h, b, 3);
+ solv_chksum_add(h, pubkey, pubkeyl);
+ }
+ if ((type >= 0x10 && type <= 0x13))
+ {
+ if (p[0] != 3)
+ {
+ b[0] = 0xb4;
+ b[1] = useridl >> 24;
+ b[2] = useridl >> 16;
+ b[3] = useridl >> 8;
+ b[4] = useridl;
+ solv_chksum_add(h, b, 5);
+ }
+ solv_chksum_add(h, userid, useridl);
+ }
+ /* add trailer */
+ if (p[0] == 3)
+ solv_chksum_add(h, p + 2, 5);
+ else
+ {
+ int hl = 6 + (p[4] << 8 | p[5]);
+ solv_chksum_add(h, p, hl);
+ b[0] = 4;
+ b[1] = 0xff;
+ b[2] = hl >> 24;
+ b[3] = hl >> 16;
+ b[4] = hl >> 8;
+ b[5] = hl;
+ solv_chksum_add(h, b, 6);
+ }
+ cs = solv_chksum_get(h, &csl);
+ if (cs[0] == p[sig->mpioff - 2] && cs[1] == p[sig->mpioff - 1])
+ {
+ int ml = l - sig->mpioff;
+ sig->sigdata = solv_malloc(2 + csl + ml);
+ sig->sigdatal = 2 + csl + ml;
+ sig->sigdata[0] = p[0] == 3 ? p[15] : p[2];
+ sig->sigdata[1] = p[0] == 3 ? p[16] : p[3];
+ memcpy(sig->sigdata + 2, cs, csl);
+ memcpy(sig->sigdata + 2 + csl, p + sig->mpioff, ml);
+ }
+}
+
+/* parse the header of a subpacket contained in a signature packet
+ * returns: length of the packet header, 0 if there was an error
+ * *pktlp is set to the packet length, the tag is the first byte.
+ */
+static inline int
+parsesubpkglength(unsigned char *q, int ql, int *pktlp)
+{
+ int x, sl, hl;
+ /* decode sub-packet length, ql must be > 0 */
+ x = *q++;
+ if (x < 192)
+ {
+ sl = x;
+ hl = 1;
+ }
+ else if (x == 255)
+ {
+ if (ql < 5 || q[0] != 0)
+ return 0;
+ sl = q[1] << 16 | q[2] << 8 | q[3];
+ hl = 5;
+ }
+ else
+ {
+ if (ql < 2)
+ return 0;
+ sl = ((x - 192) << 8) + q[0] + 192;
+ hl = 2;
+ }
+ if (!sl || ql < sl + hl) /* sub pkg tag is included in length, i.e. sl must not be zero */
+ return 0;
+ *pktlp = sl;
+ return hl;
+}
+
+/* parse a signature packet, initializing the pgpsig struct */
+static void
+pgpsig_init(struct pgpsig *sig, unsigned char *p, int l)
+{
+ memset(sig, 0, sizeof(*sig));
+ sig->type = -1;
+ if (p[0] == 3)
+ {
+ /* printf("V3 signature packet\n"); */
+ if (l <= 19 || p[1] != 5)
+ return;
+ sig->type = p[2];
+ sig->haveissuer = 1;
+ memcpy(sig->issuer, p + 7, 8);
+ sig->created = p[3] << 24 | p[4] << 16 | p[5] << 8 | p[6];
+ sig->hashalgo = p[16];
+ sig->mpioff = 19;
+ }
+ else if (p[0] == 4)
+ {
+ int j, ql, x;
+ unsigned char *q;
+
+ /* printf("V4 signature packet\n"); */
+ if (l < 6)
+ return;
+ sig->type = p[1];
+ sig->hashalgo = p[3];
+ q = p + 4;
+ sig->keyexpires = -1;
+ for (j = 0; q && j < 2; j++)
+ {
+ if (q + 2 > p + l)
+ {
+ q = 0;
+ break;
+ }
+ ql = q[0] << 8 | q[1];
+ q += 2;
+ if (q + ql > p + l)
+ {
+ q = 0;
+ break;
+ }
+ while (ql > 0)
+ {
+ int sl, hl;
+ hl = parsesubpkglength(q, ql, &sl);
+ if (!hl)
+ {
+ q = 0;
+ break;
+ }
+ q += hl;
+ ql -= hl;
+ x = q[0] & 127; /* strip critical bit */
+ /* printf("%d SIGSUB %d %d\n", j, x, sl); */
+ if (x == 16 && sl == 9 && !sig->haveissuer)
+ {
+ sig->haveissuer = 1;
+ memcpy(sig->issuer, q + 1, 8);
+ }
+ if (x == 2 && j == 0)
+ sig->created = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
+ if (x == 3 && j == 0)
+ sig->expires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
+ if (x == 9 && j == 0)
+ sig->keyexpires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
+ q += sl;
+ ql -= sl;
+ }
+ }
+ if (q && q - p + 2 < l)
+ sig->mpioff = q - p + 2;
+ }
+}
+
+/* parse a pgp packet header
+ * returns: length of the packet header, 0 if there was an error
+ * *tagp and *pktlp is set to the packet tag and the packet length
+ */
+static int
+parsepkgheader(unsigned char *p, int pl, int *tagp, int *pktlp)
+{
+ unsigned char *op = p;
+ int x, l;
+
+ if (!pl)
+ return 0;
+ x = *p++;
+ pl--;
+ if (!(x & 128) || pl <= 0)
+ return 0;
+ if ((x & 64) == 0)
+ {
+ *tagp = (x & 0x3c) >> 2; /* old format */
+ x = 1 << (x & 3);
+ if (x > 4 || pl < x || (x == 4 && p[0]))
+ return 0;
+ pl -= x;
+ for (l = 0; x--;)
+ l = l << 8 | *p++;
+ }
+ else
+ {
+ *tagp = (x & 0x3f); /* new format */
+ x = *p++;
+ pl--;
+ if (x < 192)
+ l = x;
+ else if (x >= 192 && x < 224)
+ {
+ if (pl <= 0)
+ return 0;
+ l = ((x - 192) << 8) + *p++ + 192;
+ pl--;
+ }
+ else if (x == 255)
+ {
+ if (pl <= 4 || p[0] != 0) /* sanity: p[0] must be zero */
+ return 0;
+ l = p[1] << 16 | p[2] << 8 | p[3];
+ p += 4;
+ pl -= 4;
+ }
+ else
+ return 0;
+ }
+ if (l > pl)
+ return 0;
+ *pktlp = l;
+ return p - op;
+}
+
+/* parse the first pubkey (possible creating new packages for the subkeys)
+ * returns the number of parsed bytes.
+ * if flags contains ADD_WITH_SUBKEYS, all subkeys will be added as new
+ * solvables as well */
+static int
+parsepubkey(Solvable *s, Repodata *data, unsigned char *p, int pl, int flags)
+{
+ Repo *repo = s->repo;
+ unsigned char *pstart = p;
+ int tag, l;
+ unsigned char keyid[8];
+ char subkeyofstr[17];
+ unsigned int kcr = 0, maxex = 0, maxsigcr = 0;
+ unsigned char *pubkey = 0;
+ int pubkeyl = 0;
+ int insubkey = 0;
+ unsigned char *userid = 0;
+ int useridl = 0;
+ unsigned char *pubdata = 0;
+ int pubdatal = 0;
+
+ *subkeyofstr = 0;
+ for (; ; p += l, pl -= l)
+ {
+ int hl = parsepkgheader(p, pl, &tag, &l);
+ if (!hl || (pubkey && (tag == 6 || tag == 14)))
+ {
+ /* finish old key */
+ if (kcr)
+ repodata_set_num(data, s - repo->pool->solvables, SOLVABLE_BUILDTIME, kcr);
+ if (maxex && maxex != -1)
+ repodata_set_num(data, s - repo->pool->solvables, PUBKEY_EXPIRES, maxex);
+ s->name = pool_str2id(s->repo->pool, insubkey ? "gpg-subkey" : "gpg-pubkey", 1);
+ s->evr = 1;
+ s->arch = 1;
+ if (userid && useridl)
+ {
+ char *useridstr = solv_malloc(useridl + 1);
+ memcpy(useridstr, userid, useridl);
+ useridstr[useridl] = 0;
+ setutf8string(data, s - repo->pool->solvables, SOLVABLE_SUMMARY, useridstr);
+ free(useridstr);
+ }
+ if (pubdata)
+ {
+ char keyidstr[17];
+ char evr[8 + 1 + 8 + 1];
+ solv_bin2hex(keyid, 8, keyidstr);
+ repodata_set_str(data, s - repo->pool->solvables, PUBKEY_KEYID, keyidstr);
+ /* build rpm-style evr */
+ strcpy(evr, keyidstr + 8);
+ sprintf(evr + 8, "-%08x", maxsigcr);
+ s->evr = pool_str2id(repo->pool, evr, 1);
+ }
+ if (insubkey && *subkeyofstr)
+ repodata_set_str(data, s - repo->pool->solvables, PUBKEY_SUBKEYOF, subkeyofstr);
+ if (pubdata) /* set data blob */
+ repodata_set_binary(data, s - repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
+ if (!pl)
+ break;
+ if (!hl)
+ {
+ p = 0; /* parse error */
+ break;
+ }
+ if (tag == 6 || (tag == 14 && !(flags & ADD_WITH_SUBKEYS)))
+ break;
+ if (tag == 14 && pubdata && !insubkey)
+ solv_bin2hex(keyid, 8, subkeyofstr);
+ /* create new solvable for subkey */
+ s = pool_id2solvable(repo->pool, repo_add_solvable(repo));
+ }
+ p += hl;
+ pl -= hl;
+ if (!pubkey && tag != 6)
+ continue;
+ if (tag == 6 || (tag == 14 && (flags & ADD_WITH_SUBKEYS) != 0)) /* Public-Key Packet */
+ {
+ if (tag == 6)
+ {
+ pubkey = solv_memdup(p, l);
+ pubkeyl = l;
+ }
+ else
+ insubkey = 1;
+ pubdata = 0;
+ pubdatal = 0;
+ if (p[0] == 3 && l >= 10)
+ {
+ unsigned int ex;
+ Chksum *h;
+ maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
+ ex = 0;
+ if (p[5] || p[6])
+ {
+ ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
+ if (ex > maxex)
+ maxex = ex;
+ }
+ memset(keyid, 0, 8);
+ if (p[7] == 1) /* RSA */
+ {
+ int ql, ql2;
+ unsigned char fp[16];
+ char fpx[32 + 1];
+ unsigned char *q;
+
+ ql = ((p[8] << 8 | p[9]) + 7) / 8; /* length of public modulus */
+ if (ql >= 8 && 10 + ql + 2 <= l)
+ {
+ memcpy(keyid, p + 10 + ql - 8, 8); /* keyid is last 64 bits of public modulus */
+ q = p + 10 + ql;
+ ql2 = ((q[0] << 8 | q[1]) + 7) / 8; /* length of encryption exponent */
+ if (10 + ql + 2 + ql2 <= l)
+ {
+ /* fingerprint is the md5 over the two MPI bodies */
+ h = solv_chksum_create(REPOKEY_TYPE_MD5);
+ solv_chksum_add(h, p + 10, ql);
+ solv_chksum_add(h, q + 2, ql2);
+ solv_chksum_free(h, fp);
+ solv_bin2hex(fp, 16, fpx);
+ repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
+ }
+ }
+ pubdata = p + 7;
+ pubdatal = l - 7;
+ }
+ }
+ else if (p[0] == 4 && l >= 6)
+ {
+ Chksum *h;
+ unsigned char hdr[3];
+ unsigned char fp[20];
+ char fpx[40 + 1];
+
+ maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
+ hdr[0] = 0x99;
+ hdr[1] = l >> 8;
+ hdr[2] = l;
+ /* fingerprint is the sha1 over the packet */
+ h = solv_chksum_create(REPOKEY_TYPE_SHA1);
+ solv_chksum_add(h, hdr, 3);
+ solv_chksum_add(h, p, l);
+ solv_chksum_free(h, fp);
+ solv_bin2hex(fp, 20, fpx);
+ repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
+ memcpy(keyid, fp + 12, 8); /* keyid is last 64 bits of fingerprint */
+ pubdata = p + 5;
+ pubdatal = l - 5;
+ }
+ }
+ if (tag == 2) /* Signature Packet */
+ {
+ struct pgpsig sig;
+ Id htype;
+ if (!pubdata)
+ continue;
+ pgpsig_init(&sig, p, l);
+ if (!sig.haveissuer || !((sig.type >= 0x10 && sig.type <= 0x13) || sig.type == 0x1f))
+ continue;
+ if (sig.type >= 0x10 && sig.type <= 0x13 && !userid)
+ continue;
+ htype = pgphashalgo2type(sig.hashalgo);
+ if (htype && sig.mpioff)
+ {
+ Chksum *h = solv_chksum_create(htype);
+ pgpsig_makesigdata(&sig, p, l, pubkey, pubkeyl, userid, useridl, h);
+ solv_chksum_free(h, 0);
+ }
+ if (!memcmp(keyid, sig.issuer, 8))
+ {
+#ifdef ENABLE_PGPVRFY
+ /* found self sig, verify */
+ if (solv_pgpvrfy(pubdata, pubdatal, sig.sigdata, sig.sigdatal))
+#endif
+ {
+ if (sig.keyexpires && maxex != -1)
+ {
+ if (sig.keyexpires == -1)
+ maxex = -1;
+ else if (sig.keyexpires + kcr > maxex)
+ maxex = sig.keyexpires + kcr;
+ }
+ if (sig.created > maxsigcr)
+ maxsigcr = sig.created;
+ }
+ }
+ else if (flags & ADD_WITH_KEYSIGNATURES)
+ {
+ char issuerstr[17];
+ Id shandle = repodata_new_handle(data);
+ solv_bin2hex(sig.issuer, 8, issuerstr);
+ repodata_set_str(data, shandle, SIGNATURE_ISSUER, issuerstr);
+ if (sig.created)
+ repodata_set_num(data, shandle, SIGNATURE_TIME, sig.created);
+ if (sig.expires)
+ repodata_set_num(data, shandle, SIGNATURE_EXPIRES, sig.created + sig.expires);
+ if (sig.sigdata)
+ repodata_set_binary(data, shandle, SIGNATURE_DATA, sig.sigdata, sig.sigdatal);
+ repodata_add_flexarray(data, s - s->repo->pool->solvables, PUBKEY_SIGNATURES, shandle);
+ }
+ solv_free(sig.sigdata);
+ }
+ if (tag == 13 && !insubkey) /* User ID Packet */
+ {
+ userid = solv_realloc(userid, l);
+ if (l)
+ memcpy(userid, p, l);
+ useridl = l;
+ }
+ }
+ solv_free(pubkey);
+ solv_free(userid);
+ return p ? p - pstart : 0;
+}
+
+
+#ifdef ENABLE_RPMDB
+
+/* this is private to rpm, but rpm lacks an interface to retrieve
+ * the values. Sigh. */
+struct pgpDigParams_s {
+ const char * userid;
+ const unsigned char * hash;
+#ifndef HAVE_PGPDIGGETPARAMS
+ const char * params[4];
+#endif
+ unsigned char tag;
+ unsigned char version; /*!< version number. */
+ unsigned char time[4]; /*!< time that the key was created. */
+ unsigned char pubkey_algo; /*!< public key algorithm. */
+ unsigned char hash_algo;
+ unsigned char sigtype;
+ unsigned char hashlen;
+ unsigned char signhash16[2];
+ unsigned char signid[8];
+ unsigned char saved;
+};
+
+#ifndef HAVE_PGPDIGGETPARAMS
+struct pgpDig_s {
+ struct pgpDigParams_s signature;
+ struct pgpDigParams_s pubkey;
+};
+#endif
+
+
+/* only rpm knows how to do the release calculation, we don't dare
+ * to recreate all the bugs in libsolv */
+static void
+parsepubkey_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
+{
+ Pool *pool = s->repo->pool;
+ struct pgpDigParams_s *digpubkey;
+ pgpDig dig = 0;
+ char keyid[16 + 1];
+ char evrbuf[8 + 1 + 8 + 1];
+ unsigned int btime;
+
+#ifndef RPM5
+ dig = pgpNewDig();
+#else
+ dig = pgpDigNew(RPMVSF_DEFAULT, 0);
+#endif
+ (void) pgpPrtPkts(pkts, pktsl, dig, 0);
+#ifdef HAVE_PGPDIGGETPARAMS
+ digpubkey = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY);
+#else
+ digpubkey = &dig->pubkey;
+#endif
+ if (digpubkey)
+ {
+ btime = digpubkey->time[0] << 24 | digpubkey->time[1] << 16 | digpubkey->time[2] << 8 | digpubkey->time[3];
+ solv_bin2hex(digpubkey->signid, 8, keyid);
+ solv_bin2hex(digpubkey->signid + 4, 4, evrbuf);
+ evrbuf[8] = '-';
+ solv_bin2hex(digpubkey->time, 4, evrbuf + 9);
+ s->evr = pool_str2id(pool, evrbuf, 1);
+ repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyid);
+ if (digpubkey->userid)
+ setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, digpubkey->userid);
+ if (btime)
+ repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
+ }
+#ifndef RPM5
+ (void)pgpFreeDig(dig);
+#else
+ (void)pgpDigFree(dig);
+#endif
+}
+
+#endif /* ENABLE_RPMDB */
+
+/* parse an ascii armored pubkey
+ * adds multiple pubkeys if ADD_MULTIPLE_PUBKEYS is set */
+static int
+pubkey2solvable(Pool *pool, Id p, Repodata *data, char *pubkey, int flags)
+{
+ unsigned char *pkts, *pkts_orig;
+ int pktsl, pl = 0, tag, l, hl;
+
+ if (!unarmor(pubkey, &pkts, &pktsl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----"))
+ {
+ pool_error(pool, 0, "unarmor failure");
+ return 0;
+ }
+ pkts_orig = pkts;
+ tag = 6;
+ while (pktsl)
+ {
+ if (tag == 6)
+ {
+ setutf8string(data, p, SOLVABLE_DESCRIPTION, pubkey);
+ pl = parsepubkey(pool->solvables + p, data, pkts, pktsl, flags);
+#ifdef ENABLE_RPMDB
+ parsepubkey_rpm(pool->solvables + p, data, pkts, pktsl);
+#endif
+ if (!pl || !(flags & ADD_MULTIPLE_PUBKEYS))
+ break;
+ }
+ pkts += pl;
+ pktsl -= pl;
+ hl = parsepkgheader(pkts, pktsl, &tag, &l);
+ if (!hl)
+ break;
+ pl = l + hl;
+ if (tag == 6)
+ p = repo_add_solvable(pool->solvables[p].repo);
+ }
+ solv_free((void *)pkts_orig);
+ return 1;
+}
+
+#ifdef ENABLE_RPMDB
+
+int
+repo_add_rpmdb_pubkeys(Repo *repo, int flags)
+{
+ Pool *pool = repo->pool;
+ Queue q;
+ int i;
+ char *str;
+ Repodata *data;
+ const char *rootdir = 0;
+ void *state;
+
+ data = repo_add_repodata(repo, flags);
+ if (flags & REPO_USE_ROOTDIR)
+ rootdir = pool_get_rootdir(pool);
+ state = rpm_state_create(repo->pool, rootdir);
+ queue_init(&q);
+ rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
+ for (i = 0; i < q.count; i++)
+ {
+ Id p, p2;
+ void *handle;
+ unsigned long long itime;
+
+ handle = rpm_byrpmdbid(state, q.elements[i]);
+ if (!handle)
+ continue;
+ str = rpm_query(handle, SOLVABLE_DESCRIPTION);
+ if (!str)
+ continue;
+ p = repo_add_solvable(repo);
+ if (!pubkey2solvable(pool, p, data, str, flags))
+ {
+ solv_free(str);
+ repo_free_solvable(repo, p, 1);
+ continue;
+ }
+ solv_free(str);
+ itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
+ for (p2 = p; p2 < pool->nsolvables; p2++)
+ {
+ if (itime)
+ repodata_set_num(data, p2, SOLVABLE_INSTALLTIME, itime);
+ if (!repo->rpmdbid)
+ repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
+ repo->rpmdbid[p2 - repo->start] = q.elements[i];
+ }
+ }
+ queue_free(&q);
+ rpm_state_free(state);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+#endif
+
+static char *
+solv_slurp(FILE *fp, int *lenp)
+{
+ int l, ll;
+ char *buf = 0;
+ int bufl = 0;
+
+ for (l = 0; ; l += ll)
+ {
+ if (bufl - l < 4096)
+ {
+ bufl += 4096;
+ buf = solv_realloc(buf, bufl);
+ }
+ ll = fread(buf + l, 1, bufl - l, fp);
+ if (ll < 0)
+ {
+ buf = solv_free(buf);
+ l = 0;
+ break;
+ }
+ if (ll == 0)
+ {
+ buf[l] = 0; /* always zero-terminate */
+ break;
+ }
+ }
+ if (lenp)
+ *lenp = l;
+ return buf;
+}
+
+Id
+repo_add_pubkey(Repo *repo, const char *keyfile, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ Id p;
+ char *buf;
+ FILE *fp;
+
+ data = repo_add_repodata(repo, flags);
+ buf = 0;
+ if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, keyfile) : keyfile, "r")) == 0)
+ {
+ pool_error(pool, -1, "%s: %s", keyfile, strerror(errno));
+ return 0;
+ }
+ if ((buf = solv_slurp(fp, 0)) == 0)
+ {
+ pool_error(pool, -1, "%s: %s", keyfile, strerror(errno));
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+ p = repo_add_solvable(repo);
+ if (!pubkey2solvable(pool, p, data, buf, flags))
+ {
+ repo_free_solvable(repo, p, 1);
+ solv_free(buf);
+ return 0;
+ }
+ if (!(flags & REPO_NO_LOCATION))
+ {
+ Id p2;
+ for (p2 = p; p2 < pool->nsolvables; p2++)
+ repodata_set_location(data, p2, 0, 0, keyfile);
+ }
+ solv_free(buf);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return p;
+}
+
+static int
+is_sig_packet(unsigned char *sig, int sigl)
+{
+ if (!sigl)
+ return 0;
+ if ((sig[0] & 0x80) == 0 || (sig[0] & 0x40 ? sig[0] & 0x3f : sig[0] >> 2 & 0x0f) != 2)
+ return 0;
+ return 1;
+}
+
+static int
+is_pubkey_packet(unsigned char *pkt, int pktl)
+{
+ if (!pktl)
+ return 0;
+ if ((pkt[0] & 0x80) == 0 || (pkt[0] & 0x40 ? pkt[0] & 0x3f : pkt[0] >> 2 & 0x0f) != 6)
+ return 0;
+ return 1;
+}
+
+static void
+add_one_pubkey(Pool *pool, Repo *repo, Repodata *data, unsigned char *pbuf, int pbufl, int flags)
+{
+ Id p = repo_add_solvable(repo);
+ char *solvversion = pool_tmpjoin(pool, "libsolv-", LIBSOLV_VERSION_STRING, 0);
+ char *descr = armor(pbuf, pbufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----", solvversion);
+ setutf8string(data, p, SOLVABLE_DESCRIPTION, descr);
+ parsepubkey(pool->solvables + p, data, pbuf, pbufl, flags);
+#ifdef ENABLE_RPMDB
+ parsepubkey_rpm(pool->solvables + p, data, pbuf, pbufl);
+#endif
+}
+
+int
+repo_add_keyring(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ unsigned char *buf, *p, *pbuf;
+ int bufl, l, pl, pbufl;
+
+ data = repo_add_repodata(repo, flags);
+ buf = (unsigned char *)solv_slurp(fp, &bufl);
+ if (buf && !is_pubkey_packet(buf, bufl))
+ {
+ /* assume ascii armored */
+ unsigned char *nbuf = 0, *ubuf;
+ int nl, ubufl;
+ bufl = 0;
+ for (l = 0; (nl = unarmor((char *)buf + l, &ubuf, &ubufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----")) != 0; l += nl)
+ {
+ /* found another block. concat. */
+ nbuf = solv_realloc(nbuf, bufl + ubufl);
+ if (ubufl)
+ memcpy(nbuf + bufl, ubuf, ubufl);
+ bufl += ubufl;
+ solv_free(ubuf);
+ }
+ solv_free(buf);
+ buf = nbuf;
+ }
+ /* now split into pubkey parts, ignoring the packets we don't know */
+ pbuf = 0;
+ pbufl = 0;
+ for (p = buf; bufl; p += pl, bufl -= pl)
+ {
+ int tag;
+ int hl = parsepkgheader(p, bufl, &tag, &pl);
+ if (!hl)
+ break;
+ pl += hl;
+ if (tag == 6)
+ {
+ /* found new pubkey! flush old */
+ if (pbufl)
+ {
+ add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
+ pbuf = solv_free(pbuf);
+ pbufl = 0;
+ }
+ }
+ if (tag != 6 && !pbufl)
+ continue;
+ if (tag != 6 && tag != 2 && tag != 13 && tag != 14 && tag != 17)
+ continue;
+ /* we want that packet. concat. */
+ pbuf = solv_realloc(pbuf, pbufl + pl);
+ memcpy(pbuf + pbufl, p, pl);
+ pbufl += pl;
+ }
+ if (pbufl)
+ add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
+ solv_free(pbuf);
+ solv_free(buf);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+int
+repo_add_keydir(Repo *repo, const char *keydir, const char *suffix, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i, nent, sl;
+ struct dirent **namelist;
+ char *rkeydir;
+
+ data = repo_add_repodata(repo, flags);
+ nent = scandir(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, keydir) : keydir, &namelist, 0, alphasort);
+ if (nent == -1)
+ return pool_error(pool, -1, "%s: %s", keydir, strerror(errno));
+ rkeydir = pool_prepend_rootdir(pool, keydir);
+ sl = suffix ? strlen(suffix) : 0;
+ for (i = 0; i < nent; i++)
+ {
+ const char *dn = namelist[i]->d_name;
+ int l;
+ if (*dn == '.' && !(flags & ADD_KEYDIR_WITH_DOTFILES))
+ continue;
+ l = strlen(dn);
+ if (sl && (l < sl || strcmp(dn + l - sl, suffix) != 0))
+ continue;
+ repo_add_pubkey(repo, pool_tmpjoin(pool, rkeydir, "/", dn), flags | REPO_REUSE_REPODATA);
+ }
+ solv_free(rkeydir);
+ for (i = 0; i < nent; i++)
+ solv_free(namelist[i]);
+ solv_free(namelist);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+Solvsig *
+solvsig_create(FILE *fp)
+{
+ Solvsig *ss;
+ unsigned char *sig;
+ int sigl, hl, tag, pktl;
+ struct pgpsig pgpsig;
+
+ if ((sig = (unsigned char *)solv_slurp(fp, &sigl)) == 0)
+ return 0;
+ if (!is_sig_packet(sig, sigl))
+ {
+ /* not a raw sig, check armored */
+ unsigned char *nsig;
+ if (!unarmor((char *)sig, &nsig, &sigl, "-----BEGIN PGP SIGNATURE-----", "-----END PGP SIGNATURE-----"))
+ {
+ solv_free(sig);
+ return 0;
+ }
+ solv_free(sig);
+ sig = nsig;
+ if (!is_sig_packet(sig, sigl))
+ {
+ solv_free(sig);
+ return 0;
+ }
+ }
+ hl = parsepkgheader(sig, sigl, &tag, &pktl);
+ if (!hl || tag != 2 || !pktl)
+ {
+ solv_free(sig);
+ return 0;
+ }
+ pgpsig_init(&pgpsig, sig + hl, pktl);
+ if (pgpsig.type != 0 || !pgpsig.haveissuer)
+ {
+ solv_free(sig);
+ return 0;
+ }
+ ss = solv_calloc(1, sizeof(*ss));
+ ss->sigpkt = solv_memdup(sig + hl, pktl);
+ ss->sigpktl = pktl;
+ solv_free(sig);
+ solv_bin2hex(pgpsig.issuer, 8, ss->keyid);
+ ss->htype = pgphashalgo2type(pgpsig.hashalgo);
+ ss->created = pgpsig.created;
+ ss->expires = pgpsig.expires;
+ return ss;
+}
+
+void
+solvsig_free(Solvsig *ss)
+{
+ solv_free(ss->sigpkt);
+ solv_free(ss);
+}
+
+static int
+repo_find_all_pubkeys_cmp(const void *va, const void *vb, void *dp)
+{
+ Pool *pool = dp;
+ Id a = *(Id *)va;
+ Id b = *(Id *)vb;
+ /* cannot use evrcmp, as rpm says '0' > 'a' */
+ return strcmp(pool_id2str(pool, pool->solvables[b].evr), pool_id2str(pool, pool->solvables[a].evr));
+}
+
+void
+repo_find_all_pubkeys(Repo *repo, const char *keyid, Queue *q)
+{
+ Id p;
+ Solvable *s;
+
+ queue_empty(q);
+ if (!keyid)
+ return;
+ queue_init(q);
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ const char *kidstr, *evr = pool_id2str(s->repo->pool, s->evr);
+
+ if (!evr || strncmp(evr, keyid + 8, 8) != 0)
+ continue;
+ kidstr = solvable_lookup_str(s, PUBKEY_KEYID);
+ if (kidstr && !strcmp(kidstr, keyid))
+ queue_push(q, p);
+ }
+ if (q->count > 1)
+ solv_sort(q->elements, q->count, sizeof(Id), repo_find_all_pubkeys_cmp, repo->pool);
+}
+
+Id
+repo_find_pubkey(Repo *repo, const char *keyid)
+{
+ Queue q;
+ Id p;
+ queue_init(&q);
+ repo_find_all_pubkeys(repo, keyid, &q);
+ p = q.count ? q.elements[0] : 0;
+ queue_free(&q);
+ return p;
+}
+
+#ifdef ENABLE_PGPVRFY
+
+/* warning: does not check key expiry/revokation, same as with gpgv or rpm */
+/* returns the Id of the pubkey that verified the signature */
+Id
+repo_verify_sigdata(Repo *repo, unsigned char *sigdata, int sigdatal, const char *keyid)
+{
+ Id p;
+ Queue q;
+ int i;
+
+ if (!sigdata || !keyid)
+ return 0;
+ queue_init(&q);
+ repo_find_all_pubkeys(repo, keyid, &q);
+ for (i = 0; i < q.count; i++)
+ {
+ int pubdatal;
+ const unsigned char *pubdata = repo_lookup_binary(repo, q.elements[i], PUBKEY_DATA, &pubdatal);
+ if (pubdata && solv_pgpvrfy(pubdata, pubdatal, sigdata, sigdatal))
+ break;
+ }
+ p = i < q.count? q.elements[i] : 0;
+ queue_free(&q);
+ return p;
+}
+
+Id
+solvsig_verify(Solvsig *ss, Repo *repo, Chksum *chk)
+{
+ struct pgpsig pgpsig;
+ void *chk2;
+ Id p;
+
+ if (!chk || solv_chksum_isfinished(chk))
+ return 0;
+ pgpsig_init(&pgpsig, ss->sigpkt, ss->sigpktl);
+ chk2 = solv_chksum_create_clone(chk);
+ pgpsig_makesigdata(&pgpsig, ss->sigpkt, ss->sigpktl, 0, 0, 0, 0, chk2);
+ solv_chksum_free(chk2, 0);
+ if (!pgpsig.sigdata)
+ return 0;
+ p = repo_verify_sigdata(repo, pgpsig.sigdata, pgpsig.sigdatal, ss->keyid);
+ solv_free(pgpsig.sigdata);
+ return p;
+}
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2013, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include "repo.h"
+#include "chksum.h"
+
+#define ADD_KEYDIR_WITH_DOTFILES (1 << 8)
+#define ADD_WITH_SUBKEYS (1 << 9)
+#define ADD_MULTIPLE_PUBKEYS (1 << 10)
+#define ADD_WITH_KEYSIGNATURES (1 << 11)
+
+extern int repo_add_rpmdb_pubkeys(Repo *repo, int flags);
+extern Id repo_add_pubkey(Repo *repo, const char *keyfile, int flags);
+extern int repo_add_keyring(Repo *repo, FILE *fp, int flags);
+extern int repo_add_keydir(Repo *repo, const char *keydir, const char *suffix, int flags);
+
+/* signature parsing */
+typedef struct _solvsig {
+ unsigned char *sigpkt;
+ int sigpktl;
+ Id htype;
+ unsigned int created;
+ unsigned int expires;
+ char keyid[17];
+} Solvsig;
+
+Solvsig *solvsig_create(FILE *fp);
+void solvsig_free(Solvsig *ss);
+Id solvsig_verify(Solvsig *ss, Repo *repo, Chksum *chk);
+
+Id repo_verify_sigdata(Repo *repo, unsigned char *sigdata, int sigdatal, const char *keyid);
+Id repo_find_pubkey(Repo *repo, const char *keyid);
+void repo_find_all_pubkeys(Repo *repo, const char *keyid, Queue *q);
+
--- /dev/null
+/*
+ * repo_products.c
+ *
+ * Parses all files below 'proddir'
+ * See http://en.opensuse.org/Product_Management/Code11
+ *
+ *
+ * Copyright (c) 2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#define DISABLE_SPLIT
+#include "tools_util.h"
+#include "repo_releasefile_products.h"
+
+#define BUFF_SIZE 8192
+
+struct parsedata {
+ Repo *repo;
+ struct joindata jd;
+};
+
+static void
+add_releasefile_product(struct parsedata *pd, FILE *fp)
+{
+ Repo *repo = pd->repo;
+ Pool *pool = repo->pool;
+ char buf[BUFF_SIZE];
+ Id name = 0;
+ Id arch = 0;
+ Id version = 0;
+ int lnum = 0; /* line number */
+ char *ptr, *ptr1;
+
+ /* parse /etc/<xyz>-release file */
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ /* remove trailing \n */
+ int l = strlen(buf);
+ if (l && buf[l - 1] == '\n')
+ buf[--l] = 0;
+ ++lnum;
+
+ if (lnum == 1)
+ {
+ /* 1st line, <name> [(<arch>)] */
+ ptr = strchr(buf, '(');
+ if (ptr)
+ {
+ ptr1 = ptr - 1;
+ *ptr++ = 0;
+ }
+ else
+ ptr1 = buf + l - 1;
+
+ /* track back until non-blank, non-digit */
+ while (ptr1 > buf
+ && (*ptr1 == ' ' || isdigit(*ptr1) || *ptr1 == '.'))
+ --ptr1;
+ *(++ptr1) = 0;
+ name = pool_str2id(pool, join2(&pd->jd, "product", ":", buf), 1);
+
+ if (ptr)
+ {
+ /* have arch */
+ char *ptr1 = strchr(ptr, ')');
+ if (ptr1)
+ {
+ *ptr1 = 0;
+ /* downcase arch */
+ ptr1 = ptr;
+ while (*ptr1)
+ {
+ if (isupper(*ptr1))
+ *ptr1 = tolower(*ptr1);
+ ++ptr1;
+ }
+ arch = pool_str2id(pool, ptr, 1);
+ }
+ }
+ }
+ else if (strncmp(buf, "VERSION", 7) == 0)
+ {
+ ptr = strchr(buf + 7, '=');
+ if (ptr)
+ {
+ while (*++ptr == ' ')
+ ;
+ version = makeevr(pool, ptr);
+ }
+ }
+ }
+ if (name)
+ {
+ Solvable *s = pool_id2solvable(pool, repo_add_solvable(repo));
+ s->name = name;
+ s->evr = version ? version : ID_EMPTY;
+ s->arch = arch ? arch : ARCH_NOARCH;
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ }
+}
+
+
+int
+repo_add_releasefile_products(Repo *repo, const char *dirpath, int flags)
+{
+ DIR *dir;
+ struct dirent *entry;
+ FILE *fp;
+ char *fullpath;
+ struct parsedata pd;
+
+ if (!dirpath)
+ dirpath = "/etc";
+ if (flags & REPO_USE_ROOTDIR)
+ dirpath = pool_prepend_rootdir(repo->pool, dirpath);
+ dir = opendir(dirpath);
+ if (!dir)
+ {
+ if (flags & REPO_USE_ROOTDIR)
+ solv_free((char *)dirpath);
+ return 0;
+ }
+
+ memset(&pd, 0, sizeof(pd));
+ pd.repo = repo;
+ while ((entry = readdir(dir)))
+ {
+ int len = strlen(entry->d_name);
+ if (len > 8 && !strcmp(entry->d_name + len - 8, "-release"))
+ {
+ /* skip /etc/lsb-release, thats not a product per-se */
+ if (strcmp(entry->d_name, "lsb-release") == 0)
+ continue;
+ fullpath = join2(&pd.jd, dirpath, "/", entry->d_name);
+ if ((fp = fopen(fullpath, "r")) == 0)
+ {
+ pool_error(repo->pool, 0, "%s: %s", fullpath, strerror(errno));
+ continue;
+ }
+ add_releasefile_product(&pd, fp);
+ fclose(fp);
+ }
+ }
+ closedir(dir);
+ join_freemem(&pd.jd);
+ if (flags & REPO_USE_ROOTDIR)
+ solv_free((char *)dirpath);
+
+ if (!(flags & REPO_NO_INTERNALIZE) && (flags & REPO_REUSE_REPODATA) != 0)
+ repodata_internalize(repo_last_repodata(repo));
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_releasefile_products(Repo *repo, const char *dirpath, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define DO_ARRAY 1
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_repomdxml.h"
+
+/*
+<repomd>
+
+ <!-- these tags are available in create repo > 0.9.6 -->
+ <revision>timestamp_or_arbitrary_user_supplied_string</revision>
+ <tags>
+ <content>opensuse</content>
+ <content>i386</content>
+ <content>other string</content>
+ <distro cpeid="cpe://o:opensuse_project:opensuse:11">openSUSE 11.0</distro>
+ </tags>
+ <!-- end -->
+
+ <data type="primary">
+ <location href="repodata/primary.xml.gz"/>
+ <checksum type="sha">e9162516fa25fec8d60caaf4682d2e49967786cc</checksum>
+ <timestamp>1215708444</timestamp>
+ <open-checksum type="sha">c796c48184cd5abc260e4ba929bdf01be14778a7</open-checksum>
+ </data>
+ <data type="filelists">
+ <location href="repodata/filelists.xml.gz"/>
+ <checksum type="sha">1c638295c49e9707c22810004ebb0799791fcf45</checksum>
+ <timestamp>1215708445</timestamp>
+ <open-checksum type="sha">54a40d5db3df0813b8acbe58cea616987eb9dc16</open-checksum>
+ </data>
+ <data type="other">
+ <location href="repodata/other.xml.gz"/>
+ <checksum type="sha">a81ef39eaa70e56048f8351055119d8c82af2491</checksum>
+ <timestamp>1215708447</timestamp>
+ <open-checksum type="sha">4d1ee867c8864025575a2fb8fde3b85371d51978</open-checksum>
+ </data>
+ <data type="deltainfo">
+ <location href="repodata/deltainfo.xml.gz"/>
+ <checksum type="sha">5880cfa5187026a24a552d3c0650904a44908c28</checksum>
+ <timestamp>1215708447</timestamp>
+ <open-checksum type="sha">7c964a2c3b17df5bfdd962c3be952c9ca6978d8b</open-checksum>
+ </data>
+ <data type="updateinfo">
+ <location href="repodata/updateinfo.xml.gz"/>
+ <checksum type="sha">4097f7e25c7bb0770ae31b2471a9c8c077ee904b</checksum>
+ <timestamp>1215708447</timestamp>
+ <open-checksum type="sha">24f8252f3dd041e37e7c3feb2d57e02b4422d316</open-checksum>
+ </data>
+ <data type="diskusage">
+ <location href="repodata/diskusage.xml.gz"/>
+ <checksum type="sha">4097f7e25c7bb0770ae31b2471a9c8c077ee904b</checksum>
+ <timestamp>1215708447</timestamp>
+ <open-checksum type="sha">24f8252f3dd041e37e7c3feb2d57e02b4422d316</open-checksum>
+ </data>
+</repomd>
+
+support also extension suseinfo format
+<suseinfo>
+ <expire>timestamp</expire>
+ <products>
+ <id>...</id>
+ </products>
+ <kewwords>
+ <k>...</k>
+ </keywords>
+</suseinfo>
+
+*/
+
+enum state {
+ STATE_START,
+ /* extension tags */
+ STATE_SUSEINFO,
+ STATE_EXPIRE,
+ STATE_KEYWORDS,
+ STATE_KEYWORD,
+
+ /* normal repomd.xml */
+ STATE_REPOMD,
+ STATE_REVISION,
+ STATE_TAGS,
+ STATE_REPO,
+ STATE_CONTENT,
+ STATE_DISTRO,
+ STATE_UPDATES,
+ STATE_DATA,
+ STATE_LOCATION,
+ STATE_CHECKSUM,
+ STATE_TIMESTAMP,
+ STATE_OPENCHECKSUM,
+ STATE_SIZE,
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+/* !! must be sorted by first column !! */
+static struct stateswitch stateswitches[] = {
+ /* suseinfo tags */
+ { STATE_START, "repomd", STATE_REPOMD, 0 },
+ { STATE_START, "suseinfo", STATE_SUSEINFO, 0 },
+ /* we support the tags element in suseinfo in case
+ createrepo version does not support it yet */
+ { STATE_SUSEINFO, "tags", STATE_TAGS, 0 },
+ { STATE_SUSEINFO, "expire", STATE_EXPIRE, 1 },
+ { STATE_SUSEINFO, "keywords", STATE_KEYWORDS, 0 },
+ /* keywords is the suse extension equivalent of
+ tags/content when this one was not yet available.
+ therefore we parse both */
+ { STATE_KEYWORDS, "k", STATE_KEYWORD, 1 },
+ /* standard tags */
+ { STATE_REPOMD, "revision", STATE_REVISION, 1 },
+ { STATE_REPOMD, "tags", STATE_TAGS, 0 },
+ { STATE_REPOMD, "data", STATE_DATA, 0 },
+
+ { STATE_TAGS, "repo", STATE_REPO, 1 },
+ { STATE_TAGS, "content", STATE_CONTENT, 1 },
+ { STATE_TAGS, "distro", STATE_DISTRO, 1 },
+ /* this tag is only valid in suseinfo.xml for now */
+ { STATE_TAGS, "updates", STATE_UPDATES, 1 },
+
+ { STATE_DATA, "location", STATE_LOCATION, 0 },
+ { STATE_DATA, "checksum", STATE_CHECKSUM, 1 },
+ { STATE_DATA, "timestamp", STATE_TIMESTAMP, 1 },
+ { STATE_DATA, "open-checksum", STATE_OPENCHECKSUM, 1 },
+ { STATE_DATA, "size", STATE_SIZE, 1 },
+ { NUMSTATES }
+};
+
+
+struct parsedata {
+ int ret;
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+
+ XML_Parser *parser;
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+ int timestamp;
+ /* handles for collection
+ structures */
+ /* repo updates */
+ Id ruhandle;
+ /* repo products */
+ Id rphandle;
+ /* repo data handle */
+ Id rdhandle;
+
+ Id chksumtype;
+};
+
+/*
+ * find attribute
+ */
+
+static inline const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ }
+ return 0;
+}
+
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ /*Pool *pool = pd->pool;*/
+ struct stateswitch *sw;
+
+#if 0
+ fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ pd->depth++;
+ if (!pd->swtab[pd->state])
+ return;
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
+ if (!strcmp(sw->ename, name))
+ break;
+
+ if (sw->from != pd->state)
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#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_REPOMD:
+ {
+ const char *updstr;
+
+ /* this should be OBSOLETE soon */
+ updstr = find_attr("updates", atts);
+ if (updstr)
+ {
+ char *value = solv_strdup(updstr);
+ char *fvalue = value; /* save the first */
+ while (value)
+ {
+ char *p = strchr(value, ',');
+ if (*p)
+ *p++ = 0;
+ if (*value)
+ repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_UPDATES, value);
+ value = p;
+ }
+ free(fvalue);
+ }
+ break;
+ }
+ case STATE_DISTRO:
+ {
+ /* this is extra metadata about the product this repository
+ was designed for */
+ const char *cpeid = find_attr("cpeid", atts);
+ pd->rphandle = repodata_new_handle(pd->data);
+ /* set the cpeid for the product
+ the label is set in the content of the tag */
+ if (cpeid)
+ repodata_set_poolstr(pd->data, pd->rphandle, REPOSITORY_PRODUCT_CPEID, cpeid);
+ break;
+ }
+ case STATE_UPDATES:
+ {
+ /* this is extra metadata about the product this repository
+ was designed for */
+ const char *cpeid = find_attr("cpeid", atts);
+ pd->ruhandle = repodata_new_handle(pd->data);
+ /* set the cpeid for the product
+ the label is set in the content of the tag */
+ if (cpeid)
+ repodata_set_poolstr(pd->data, pd->ruhandle, REPOSITORY_PRODUCT_CPEID, cpeid);
+ break;
+ }
+ case STATE_DATA:
+ {
+ const char *type= find_attr("type", atts);
+ pd->rdhandle = repodata_new_handle(pd->data);
+ if (type)
+ repodata_set_poolstr(pd->data, pd->rdhandle, REPOSITORY_REPOMD_TYPE, type);
+ break;
+ }
+ case STATE_LOCATION:
+ {
+ const char *href = find_attr("href", atts);
+ if (href)
+ repodata_set_str(pd->data, pd->rdhandle, REPOSITORY_REPOMD_LOCATION, href);
+ break;
+ }
+ case STATE_CHECKSUM:
+ case STATE_OPENCHECKSUM:
+ {
+ const char *type= find_attr("type", atts);
+ pd->chksumtype = type && *type ? solv_chksum_str2type(type) : 0;
+ if (!pd->chksumtype)
+ pd->ret = pool_error(pd->pool, -1, "line %d: unknown checksum type: %s", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), type ? type : "NULL");
+ break;
+ }
+ default:
+ break;
+ }
+ return;
+}
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ /* Pool *pool = pd->pool; */
+
+#if 0
+ fprintf(stderr, "endElement: %s\n", name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+#if 0
+ fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+#endif
+ return;
+ }
+
+ pd->depth--;
+ pd->statedepth--;
+ switch (pd->state)
+ {
+ case STATE_REPOMD:
+ if (pd->timestamp > 0)
+ repodata_set_num(pd->data, SOLVID_META, REPOSITORY_TIMESTAMP, pd->timestamp);
+ break;
+ case STATE_DATA:
+ if (pd->rdhandle)
+ repodata_add_flexarray(pd->data, SOLVID_META, REPOSITORY_REPOMD, pd->rdhandle);
+ pd->rdhandle = 0;
+ break;
+
+ case STATE_CHECKSUM:
+ case STATE_OPENCHECKSUM:
+ if (!pd->chksumtype)
+ break;
+ if (strlen(pd->content) != 2 * solv_chksum_len(pd->chksumtype))
+ pd->ret = pool_error(pd->pool, -1, "line %d: invalid checksum length for %s", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), solv_chksum_type2str(pd->chksumtype));
+ else
+ repodata_set_checksum(pd->data, pd->rdhandle, pd->state == STATE_CHECKSUM ? REPOSITORY_REPOMD_CHECKSUM : REPOSITORY_REPOMD_OPENCHECKSUM, pd->chksumtype, pd->content);
+ break;
+
+ case STATE_TIMESTAMP:
+ {
+ /**
+ * we want to look for the newest timestamp
+ * of all resources to save it as the time
+ * the metadata was generated
+ */
+ int timestamp = atoi(pd->content);
+ if (timestamp)
+ repodata_set_num(pd->data, pd->rdhandle, REPOSITORY_REPOMD_TIMESTAMP, timestamp);
+ if (timestamp > pd->timestamp)
+ pd->timestamp = timestamp;
+ break;
+ }
+ case STATE_EXPIRE:
+ {
+ int expire = atoi(pd->content);
+ if (expire > 0)
+ repodata_set_num(pd->data, SOLVID_META, REPOSITORY_EXPIRE, expire);
+ break;
+ }
+ /* repomd.xml content and suseinfo.xml keywords are equivalent */
+ case STATE_CONTENT:
+ case STATE_KEYWORD:
+ if (*pd->content)
+ repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_KEYWORDS, pd->content);
+ break;
+ case STATE_REVISION:
+ if (*pd->content)
+ repodata_set_str(pd->data, SOLVID_META, REPOSITORY_REVISION, pd->content);
+ break;
+ case STATE_DISTRO:
+ /* distro tag is used in repomd.xml to say the product this repo is
+ made for */
+ if (*pd->content)
+ repodata_set_str(pd->data, pd->rphandle, REPOSITORY_PRODUCT_LABEL, pd->content);
+ repodata_add_flexarray(pd->data, SOLVID_META, REPOSITORY_DISTROS, pd->rphandle);
+ break;
+ case STATE_UPDATES:
+ /* updates tag is used in suseinfo.xml to say the repo updates a product
+ however it s not yet a tag standarized for repomd.xml */
+ if (*pd->content)
+ repodata_set_str(pd->data, pd->ruhandle, REPOSITORY_PRODUCT_LABEL, pd->content);
+ repodata_add_flexarray(pd->data, SOLVID_META, REPOSITORY_UPDATES, pd->ruhandle);
+ break;
+ case STATE_REPO:
+ if (*pd->content)
+ repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_REPOID, pd->content);
+ break;
+ case STATE_SIZE:
+ if (*pd->content)
+ repodata_set_num(pd->data, pd->rdhandle, REPOSITORY_REPOMD_SIZE, strtoull(pd->content, 0, 10));
+ break;
+ default:
+ break;
+ }
+
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+
+ return;
+}
+
+
+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
+
+int
+repo_add_repomdxml(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ struct parsedata pd;
+ Repodata *data;
+ char buf[BUFF_SIZE];
+ int i, l;
+ struct stateswitch *sw;
+ XML_Parser parser;
+
+ data = repo_add_repodata(repo, flags);
+
+ memset(&pd, 0, sizeof(pd));
+ pd.timestamp = 0;
+ 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.repo = repo;
+ pd.data = data;
+
+ pd.content = malloc(256);
+ pd.acontent = 256;
+ pd.lcontent = 0;
+ parser = XML_ParserCreate(NULL);
+ XML_SetUserData(parser, &pd);
+ pd.parser = &parser;
+ 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)
+ {
+ pd.ret = pool_error(pool, -1, "repo_repomdxml: %s at line %u:%u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ break;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+
+ free(pd.content);
+ return pd.ret;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_repomdxml(Repo *repo, FILE *fp, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2007-2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_rpmdb
+ *
+ * convert rpm db to repo
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <rpm/rpmio.h>
+#include <rpm/rpmpgp.h>
+#ifndef RPM5
+#include <rpm/header.h>
+#endif
+#include <rpm/rpmdb.h>
+
+#ifndef DB_CREATE
+# if defined(SUSE) || defined(HAVE_RPM_DB_H)
+# include <rpm/db.h>
+# else
+# include <db.h>
+# endif
+#endif
+
+#include "pool.h"
+#include "repo.h"
+#include "hash.h"
+#include "util.h"
+#include "queue.h"
+#include "chksum.h"
+#include "repo_rpmdb.h"
+#include "repo_solv.h"
+#ifdef ENABLE_COMPLEX_DEPS
+#include "pool_parserpmrichdep.h"
+#endif
+
+/* 3: added triggers */
+/* 4: fixed triggers */
+/* 5: fixed checksum copying */
+#define RPMDB_COOKIE_VERSION 5
+
+#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_BUILDHOST 1007
+#define TAG_INSTALLTIME 1008
+#define TAG_SIZE 1009
+#define TAG_DISTRIBUTION 1010
+#define TAG_VENDOR 1011
+#define TAG_LICENSE 1014
+#define TAG_PACKAGER 1015
+#define TAG_GROUP 1016
+#define TAG_URL 1020
+#define TAG_ARCH 1022
+#define TAG_FILESIZES 1028
+#define TAG_FILEMODES 1030
+#define TAG_FILEMD5S 1035
+#define TAG_FILELINKTOS 1036
+#define TAG_FILEFLAGS 1037
+#define TAG_SOURCERPM 1044
+#define TAG_PROVIDENAME 1047
+#define TAG_REQUIREFLAGS 1048
+#define TAG_REQUIRENAME 1049
+#define TAG_REQUIREVERSION 1050
+#define TAG_NOSOURCE 1051
+#define TAG_NOPATCH 1052
+#define TAG_CONFLICTFLAGS 1053
+#define TAG_CONFLICTNAME 1054
+#define TAG_CONFLICTVERSION 1055
+#define TAG_TRIGGERNAME 1066
+#define TAG_TRIGGERVERSION 1067
+#define TAG_TRIGGERFLAGS 1068
+#define TAG_CHANGELOGTIME 1080
+#define TAG_CHANGELOGNAME 1081
+#define TAG_CHANGELOGTEXT 1082
+#define TAG_OBSOLETENAME 1090
+#define TAG_FILEDEVICES 1095
+#define TAG_FILEINODES 1096
+#define TAG_SOURCEPACKAGE 1106
+#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_PAYLOADFORMAT 1124
+#define TAG_PATCHESNAME 1133
+#define TAG_FILECOLORS 1140
+#define TAG_OLDSUGGESTSNAME 1156
+#define TAG_OLDSUGGESTSVERSION 1157
+#define TAG_OLDSUGGESTSFLAGS 1158
+#define TAG_OLDENHANCESNAME 1159
+#define TAG_OLDENHANCESVERSION 1160
+#define TAG_OLDENHANCESFLAGS 1161
+
+/* rpm5 tags */
+#define TAG_DISTEPOCH 1218
+
+/* rpm4 tags */
+#define TAG_LONGFILESIZES 5008
+#define TAG_LONGSIZE 5009
+#define TAG_RECOMMENDNAME 5046
+#define TAG_RECOMMENDVERSION 5047
+#define TAG_RECOMMENDFLAGS 5048
+#define TAG_SUGGESTNAME 5049
+#define TAG_SUGGESTVERSION 5050
+#define TAG_SUGGESTFLAGS 5051
+#define TAG_SUPPLEMENTNAME 5052
+#define TAG_SUPPLEMENTVERSION 5053
+#define TAG_SUPPLEMENTFLAGS 5054
+#define TAG_ENHANCENAME 5055
+#define TAG_ENHANCEVERSION 5056
+#define TAG_ENHANCEFLAGS 5057
+
+/* signature tags */
+#define TAG_SIGBASE 256
+#define TAG_SIGMD5 (TAG_SIGBASE + 5)
+#define TAG_SHA1HEADER (TAG_SIGBASE + 13)
+
+#define SIGTAG_SIZE 1000
+#define SIGTAG_PGP 1002 /* RSA signature */
+#define SIGTAG_MD5 1004 /* header+payload md5 checksum */
+#define SIGTAG_GPG 1005 /* DSA signature */
+
+#define DEP_LESS (1 << 1)
+#define DEP_GREATER (1 << 2)
+#define DEP_EQUAL (1 << 3)
+#define DEP_STRONG (1 << 27)
+#define DEP_PRE_IN ((1 << 6) | (1 << 9) | (1 << 10))
+#define DEP_PRE_UN ((1 << 6) | (1 << 11) | (1 << 12))
+
+#define FILEFLAG_GHOST (1 << 6)
+
+
+#ifdef RPM5
+# define RPM_INDEX_SIZE 4 /* just the rpmdbid */
+#else
+# define RPM_INDEX_SIZE 8 /* rpmdbid + array index */
+#endif
+
+
+typedef struct rpmhead {
+ int cnt;
+ int dcnt;
+ unsigned char *dp;
+ int forcebinary; /* sigh, see rh#478907 */
+ unsigned char data[1];
+} RpmHead;
+
+
+static inline unsigned char *
+headfindtag(RpmHead *h, int tag)
+{
+ unsigned int i;
+ 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])
+ return d;
+ return 0;
+}
+
+static int
+headexists(RpmHead *h, int tag)
+{
+ return headfindtag(h, tag) ? 1 : 0;
+}
+
+static unsigned int *
+headint32array(RpmHead *h, int tag, int *cnt)
+{
+ unsigned int i, o, *r;
+ unsigned char *d = headfindtag(h, tag);
+
+ if (!d || 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 = solv_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;
+}
+
+/* returns the first entry of an integer array */
+static unsigned int
+headint32(RpmHead *h, int tag)
+{
+ unsigned int i, o;
+ unsigned char *d = headfindtag(h, tag);
+
+ if (!d || 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 (i == 0 || o + 4 * i > h->dcnt)
+ return 0;
+ d = h->dp + o;
+ return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+}
+
+static unsigned long long *
+headint64array(RpmHead *h, int tag, int *cnt)
+{
+ unsigned int i, o;
+ unsigned long long *r;
+ unsigned char *d = headfindtag(h, tag);
+
+ if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 5)
+ 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 + 8 * i > h->dcnt)
+ return 0;
+ d = h->dp + o;
+ r = solv_calloc(i ? i : 1, sizeof(unsigned long long));
+ if (cnt)
+ *cnt = i;
+ for (o = 0; o < i; o++, d += 8)
+ {
+ unsigned int x = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+ r[o] = (unsigned long long)x << 32 | (d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
+ }
+ return r;
+}
+
+/* returns the first entry of an 64bit integer array */
+static unsigned long long
+headint64(RpmHead *h, int tag)
+{
+ unsigned int i, o;
+ unsigned char *d = headfindtag(h, tag);
+ if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 5)
+ return 0;
+ o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
+ i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
+ if (i == 0 || o + 8 * i > h->dcnt)
+ return 0;
+ d = h->dp + o;
+ i = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+ return (unsigned long long)i << 32 | (d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
+}
+
+static unsigned int *
+headint16array(RpmHead *h, int tag, int *cnt)
+{
+ unsigned int i, o, *r;
+ unsigned char *d = headfindtag(h, tag);
+
+ if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 3)
+ 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 = solv_calloc(i ? i : 1, sizeof(unsigned int));
+ if (cnt)
+ *cnt = i;
+ for (o = 0; o < i; o++, d += 2)
+ r[o] = d[0] << 8 | d[1];
+ return r;
+}
+
+static char *
+headstring(RpmHead *h, int tag)
+{
+ unsigned int o;
+ unsigned char *d = headfindtag(h, tag);
+ /* 6: STRING, 9: I18NSTRING */
+ if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || (d[7] != 6 && d[7] != 9))
+ return 0;
+ o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
+ if (o >= h->dcnt)
+ return 0;
+ return (char *)h->dp + o;
+}
+
+static char **
+headstringarray(RpmHead *h, int tag, int *cnt)
+{
+ unsigned int i, o;
+ unsigned char *d = headfindtag(h, tag);
+ char **r;
+
+ if (!d || 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 = solv_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)
+ {
+ solv_free(r);
+ return 0;
+ }
+ }
+ return r;
+}
+
+static unsigned char *
+headbinary(RpmHead *h, int tag, unsigned int *sizep)
+{
+ unsigned int i, o;
+ unsigned char *d = headfindtag(h, tag);
+ if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 7)
+ 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 > h->dcnt || o + i < o || o + i > h->dcnt)
+ return 0;
+ if (sizep)
+ *sizep = i;
+ return h->dp + o;
+}
+
+static char *headtoevr(RpmHead *h)
+{
+ unsigned int epoch;
+ char *version, *v;
+ char *release;
+ char *evr;
+ char *distepoch;
+
+ version = headstring(h, TAG_VERSION);
+ release = headstring(h, TAG_RELEASE);
+ epoch = headint32(h, TAG_EPOCH);
+ if (!version || !release)
+ {
+ fprintf(stderr, "headtoevr: bad rpm header\n");
+ return 0;
+ }
+ for (v = version; *v >= '0' && *v <= '9'; v++)
+ ;
+ if (epoch || (v != version && *v == ':'))
+ {
+ char epochbuf[11]; /* 32bit decimal will fit in */
+ sprintf(epochbuf, "%u", epoch);
+ evr = solv_malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
+ sprintf(evr, "%s:%s-%s", epochbuf, version, release);
+ }
+ else
+ {
+ evr = solv_malloc(strlen(version) + 1 + strlen(release) + 1);
+ sprintf(evr, "%s-%s", version, release);
+ }
+ distepoch = headstring(h, TAG_DISTEPOCH);
+ if (distepoch && *distepoch)
+ {
+ int l = strlen(evr);
+ evr = solv_realloc(evr, l + strlen(distepoch) + 2);
+ evr[l++] = ':';
+ strcpy(evr + l, distepoch);
+ }
+ return evr;
+}
+
+
+static void
+setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
+{
+ if (str[solv_validutf8(str)])
+ {
+ char *ustr = solv_latin1toutf8(str); /* not utf8, assume latin1 */
+ repodata_set_str(repodata, handle, tag, ustr);
+ solv_free(ustr);
+ }
+ else
+ repodata_set_str(repodata, handle, tag, str);
+}
+
+/*
+ * strong: 0: ignore strongness
+ * 1: filter to strong
+ * 2: filter to weak
+ */
+static unsigned int
+makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf, int flags)
+{
+ char **n, **v;
+ unsigned int *f;
+ int i, cc, nc, vc, fc;
+ int haspre, premask;
+ unsigned int olddeps;
+ Id *ida;
+ int strong = 0;
+
+ n = headstringarray(rpmhead, tagn, &nc);
+ if (!n)
+ {
+ switch (tagn)
+ {
+ case TAG_SUGGESTNAME:
+ tagn = TAG_OLDSUGGESTSNAME;
+ tagv = TAG_OLDSUGGESTSVERSION;
+ tagf = TAG_OLDSUGGESTSFLAGS;
+ strong = -1;
+ break;
+ case TAG_ENHANCENAME:
+ tagn = TAG_OLDENHANCESNAME;
+ tagv = TAG_OLDENHANCESVERSION;
+ tagf = TAG_OLDENHANCESFLAGS;
+ strong = -1;
+ break;
+ case TAG_RECOMMENDNAME:
+ tagn = TAG_OLDSUGGESTSNAME;
+ tagv = TAG_OLDSUGGESTSVERSION;
+ tagf = TAG_OLDSUGGESTSFLAGS;
+ strong = 1;
+ break;
+ case TAG_SUPPLEMENTNAME:
+ tagn = TAG_OLDENHANCESNAME;
+ tagv = TAG_OLDENHANCESVERSION;
+ tagf = TAG_OLDENHANCESFLAGS;
+ strong = 1;
+ break;
+ default:
+ return 0;
+ }
+ n = headstringarray(rpmhead, tagn, &nc);
+ }
+ if (!n || !nc)
+ return 0;
+ vc = fc = 0;
+ v = headstringarray(rpmhead, tagv, &vc);
+ f = headint32array(rpmhead, tagf, &fc);
+ if (!v || !f || nc != vc || nc != fc)
+ {
+ char *pkgname = rpm_query(rpmhead, 0);
+ pool_error(pool, 0, "bad dependency entries for %s: %d %d %d", pkgname ? pkgname : "<NULL>", nc, vc, fc);
+ solv_free(pkgname);
+ solv_free(n);
+ solv_free(v);
+ solv_free(f);
+ return 0;
+ }
+
+ cc = nc;
+ haspre = 0; /* add no prereq marker */
+ premask = tagn == TAG_REQUIRENAME ? DEP_PRE_IN | DEP_PRE_UN : 0;
+ if ((flags & RPM_ADD_NO_RPMLIBREQS) || strong)
+ {
+ /* we do filtering */
+ cc = 0;
+ for (i = 0; i < nc; i++)
+ {
+ if (strong && (f[i] & DEP_STRONG) != (strong < 0 ? 0 : DEP_STRONG))
+ continue;
+ if ((flags & RPM_ADD_NO_RPMLIBREQS) != 0)
+ if (!strncmp(n[i], "rpmlib(", 7))
+ continue;
+ if ((f[i] & premask) != 0)
+ haspre = 1;
+ cc++;
+ }
+ }
+ else if (premask)
+ {
+ /* no filtering, just look for the first prereq */
+ for (i = 0; i < nc; i++)
+ if ((f[i] & premask) != 0)
+ {
+ haspre = 1;
+ break;
+ }
+ }
+ if (cc == 0)
+ {
+ solv_free(n);
+ solv_free(v);
+ solv_free(f);
+ return 0;
+ }
+ cc += haspre; /* add slot for the prereq marker */
+ olddeps = repo_reserve_ids(repo, 0, cc);
+ ida = repo->idarraydata + olddeps;
+ for (i = 0; ; i++)
+ {
+ Id id;
+ if (i == nc)
+ {
+ if (haspre != 1)
+ break;
+ haspre = 2; /* pass two: prereqs */
+ i = 0;
+ *ida++ = SOLVABLE_PREREQMARKER;
+ }
+ if (strong && (f[i] & DEP_STRONG) != (strong < 0 ? 0 : DEP_STRONG))
+ continue;
+ if (haspre)
+ {
+ if (haspre == 1 && (f[i] & premask) != 0)
+ continue;
+ if (haspre == 2 && (f[i] & premask) == 0)
+ continue;
+ }
+ if ((flags & RPM_ADD_NO_RPMLIBREQS) != 0)
+ if (!strncmp(n[i], "rpmlib(", 7))
+ continue;
+#ifdef ENABLE_COMPLEX_DEPS
+ if ((f[i] & (DEP_LESS|DEP_EQUAL|DEP_GREATER)) == 0 && n[i][0] == '(')
+ {
+ id = pool_parserpmrichdep(pool, n[i]);
+ if (id)
+ *ida++ = id;
+ else
+ cc--;
+ continue;
+ }
+#endif
+ id = pool_str2id(pool, n[i], 1);
+ if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
+ {
+ Id evr;
+ int fl = 0;
+ if ((f[i] & DEP_LESS) != 0)
+ fl |= REL_LT;
+ if ((f[i] & DEP_EQUAL) != 0)
+ fl |= REL_EQ;
+ if ((f[i] & DEP_GREATER) != 0)
+ fl |= REL_GT;
+ if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
+ evr = pool_str2id(pool, v[i] + 2, 1);
+ else
+ evr = pool_str2id(pool, v[i], 1);
+ id = pool_rel2id(pool, id, evr, fl, 1);
+ }
+ *ida++ = id;
+ }
+ *ida++ = 0;
+ repo->idarraysize += cc + 1;
+ solv_free(n);
+ solv_free(v);
+ solv_free(f);
+ return olddeps;
+}
+
+
+static void
+adddudata(Repodata *data, Id handle, RpmHead *rpmhead, char **dn, unsigned int *di, int fc, int dc)
+{
+ Id did;
+ int i, fszc;
+ unsigned int *fkb, *fn, *fsz, *fm, *fino;
+ unsigned long long *fsz64;
+ unsigned int inotest[256], inotestok;
+
+ if (!fc)
+ return;
+ if ((fsz64 = headint64array(rpmhead, TAG_LONGFILESIZES, &fszc)) != 0)
+ {
+ /* convert to kbyte */
+ fsz = solv_malloc2(fszc, sizeof(*fsz));
+ for (i = 0; i < fszc; i++)
+ fsz[i] = fsz64[i] ? fsz64[i] / 1024 + 1 : 0;
+ solv_free(fsz64);
+ }
+ else if ((fsz = headint32array(rpmhead, TAG_FILESIZES, &fszc)) != 0)
+ {
+ /* convert to kbyte */
+ for (i = 0; i < fszc; i++)
+ if (fsz[i])
+ fsz[i] = fsz[i] / 1024 + 1;
+ }
+ else
+ return;
+ if (fc != fszc)
+ {
+ solv_free(fsz);
+ return;
+ }
+
+ /* stupid rpm records sizes of directories, so we have to check the mode */
+ fm = headint16array(rpmhead, TAG_FILEMODES, &fszc);
+ if (!fm || fc != fszc)
+ {
+ solv_free(fsz);
+ solv_free(fm);
+ return;
+ }
+ fino = headint32array(rpmhead, TAG_FILEINODES, &fszc);
+ if (!fino || fc != fszc)
+ {
+ solv_free(fsz);
+ solv_free(fm);
+ solv_free(fino);
+ return;
+ }
+
+ /* kill hardlinked entries */
+ inotestok = 0;
+ if (fc < sizeof(inotest))
+ {
+ /* quick test just hashing the inode numbers */
+ memset(inotest, 0, sizeof(inotest));
+ for (i = 0; i < fc; i++)
+ {
+ int off, bit;
+ if (fsz[i] == 0 || !S_ISREG(fm[i]))
+ continue; /* does not matter */
+ off = (fino[i] >> 5) & (sizeof(inotest)/sizeof(*inotest) - 1);
+ bit = 1 << (fino[i] & 31);
+ if ((inotest[off] & bit) != 0)
+ break;
+ inotest[off] |= bit;
+ }
+ if (i == fc)
+ inotestok = 1; /* no conflict found */
+ }
+ if (!inotestok)
+ {
+ /* hardlinked files are possible, check ino/dev pairs */
+ unsigned int *fdev = headint32array(rpmhead, TAG_FILEDEVICES, &fszc);
+ unsigned int *fx, j;
+ unsigned int mask, hash, hh;
+ if (!fdev || fc != fszc)
+ {
+ solv_free(fsz);
+ solv_free(fm);
+ solv_free(fdev);
+ solv_free(fino);
+ return;
+ }
+ mask = fc;
+ while ((mask & (mask - 1)) != 0)
+ mask = mask & (mask - 1);
+ mask <<= 2;
+ if (mask > sizeof(inotest)/sizeof(*inotest))
+ fx = solv_calloc(mask, sizeof(unsigned int));
+ else
+ {
+ fx = inotest;
+ memset(fx, 0, mask * sizeof(unsigned int));
+ }
+ mask--;
+ for (i = 0; i < fc; i++)
+ {
+ if (fsz[i] == 0 || !S_ISREG(fm[i]))
+ continue;
+ hash = (fino[i] + fdev[i] * 31) & mask;
+ hh = 7;
+ while ((j = fx[hash]) != 0)
+ {
+ if (fino[j - 1] == fino[i] && fdev[j - 1] == fdev[i])
+ {
+ fsz[i] = 0; /* kill entry */
+ break;
+ }
+ hash = (hash + hh++) & mask;
+ }
+ if (!j)
+ fx[hash] = i + 1;
+ }
+ if (fx != inotest)
+ solv_free(fx);
+ solv_free(fdev);
+ }
+ solv_free(fino);
+
+ /* sum up inode count and kbytes for each directory */
+ fn = solv_calloc(dc, sizeof(unsigned int));
+ fkb = solv_calloc(dc, sizeof(unsigned int));
+ for (i = 0; i < fc; i++)
+ {
+ if (di[i] >= dc)
+ continue; /* corrupt entry */
+ fn[di[i]]++;
+ if (fsz[i] == 0 || !S_ISREG(fm[i]))
+ continue;
+ fkb[di[i]] += fsz[i];
+ }
+ solv_free(fsz);
+ solv_free(fm);
+ /* commit */
+ for (i = 0; i < dc; i++)
+ {
+ if (!fn[i])
+ continue;
+ if (!*dn[i])
+ {
+ Solvable *s = data->repo->pool->solvables + handle;
+ if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+ did = repodata_str2dir(data, "/usr/src", 1);
+ else
+ continue; /* work around rpm bug */
+ }
+ else
+ did = repodata_str2dir(data, dn[i], 1);
+ repodata_add_dirnumnum(data, handle, SOLVABLE_DISKUSAGE, did, fkb[i], fn[i]);
+ }
+ solv_free(fn);
+ solv_free(fkb);
+}
+
+static int
+is_filtered(const char *dir)
+{
+ if (!dir)
+ return 1;
+ /* the dirs always have a trailing / in rpm */
+ if (strstr(dir, "bin/"))
+ return 0;
+ if (!strncmp(dir, "/etc/", 5))
+ return 0;
+ if (!strcmp(dir, "/usr/lib/"))
+ return 2;
+ return 1;
+}
+
+static void
+addfilelist(Repodata *data, Id handle, RpmHead *rpmhead, int flags)
+{
+ char **bn;
+ char **dn;
+ unsigned int *di;
+ int bnc, dnc, dic;
+ int i;
+ Id lastdid = 0;
+ unsigned int lastdii = -1;
+ int lastfiltered = 0;
+
+ if (!data)
+ return;
+ bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
+ if (!bn)
+ return;
+ dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
+ if (!dn)
+ {
+ solv_free(bn);
+ return;
+ }
+ di = headint32array(rpmhead, TAG_DIRINDEXES, &dic);
+ if (!di)
+ {
+ solv_free(bn);
+ solv_free(dn);
+ return;
+ }
+ if (bnc != dic)
+ {
+ pool_error(data->repo->pool, 0, "bad filelist");
+ return;
+ }
+
+ adddudata(data, handle, rpmhead, dn, di, bnc, dnc);
+
+ for (i = 0; i < bnc; i++)
+ {
+ Id did;
+ char *b = bn[i];
+
+ if (di[i] == lastdii)
+ did = lastdid;
+ else
+ {
+ if (di[i] >= dnc)
+ continue; /* corrupt entry */
+ lastdii = di[i];
+ if ((flags & RPM_ADD_FILTERED_FILELIST) != 0)
+ {
+ lastfiltered = is_filtered(dn[di[i]]);
+ if (lastfiltered == 1)
+ continue;
+ }
+ did = repodata_str2dir(data, dn[lastdii], 1);
+ if (!did)
+ did = repodata_str2dir(data, "/", 1);
+ lastdid = did;
+ }
+ if (b && *b == '/') /* work around rpm bug */
+ b++;
+ if (lastfiltered)
+ {
+ if (lastfiltered != 2 || strcmp(b, "sendmail"))
+ continue;
+ }
+ repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, b);
+ }
+ solv_free(bn);
+ solv_free(dn);
+ solv_free(di);
+}
+
+static void
+addchangelog(Repodata *data, Id handle, RpmHead *rpmhead)
+{
+ char **cn;
+ char **cx;
+ unsigned int *ct;
+ int i, cnc, cxc, ctc;
+ Queue hq;
+
+ ct = headint32array(rpmhead, TAG_CHANGELOGTIME, &ctc);
+ cx = headstringarray(rpmhead, TAG_CHANGELOGTEXT, &cxc);
+ cn = headstringarray(rpmhead, TAG_CHANGELOGNAME, &cnc);
+ if (!ct || !cx || !cn || !ctc || ctc != cxc || ctc != cnc)
+ {
+ solv_free(ct);
+ solv_free(cx);
+ solv_free(cn);
+ return;
+ }
+ queue_init(&hq);
+ for (i = 0; i < ctc; i++)
+ {
+ Id h = repodata_new_handle(data);
+ if (ct[i])
+ repodata_set_num(data, h, SOLVABLE_CHANGELOG_TIME, ct[i]);
+ if (cn[i])
+ setutf8string(data, h, SOLVABLE_CHANGELOG_AUTHOR, cn[i]);
+ if (cx[i])
+ setutf8string(data, h, SOLVABLE_CHANGELOG_TEXT, cx[i]);
+ queue_push(&hq, h);
+ }
+ for (i = 0; i < hq.count; i++)
+ repodata_add_flexarray(data, handle, SOLVABLE_CHANGELOG, hq.elements[i]);
+ queue_free(&hq);
+ solv_free(ct);
+ solv_free(cx);
+ solv_free(cn);
+}
+
+static void
+set_description_author(Repodata *data, Id handle, char *str)
+{
+ char *aut, *p;
+ for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
+ if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
+ break;
+ if (aut)
+ {
+ /* oh my, found SUSE special author section */
+ int l = aut - str;
+ str = solv_strdup(str);
+ aut = str + l;
+ str[l] = 0;
+ while (l > 0 && str[l - 1] == '\n')
+ str[--l] = 0;
+ if (l)
+ setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
+ p = aut + 19;
+ aut = str; /* copy over */
+ while (*p == ' ' || *p == '\n')
+ p++;
+ while (*p)
+ {
+ if (*p == '\n')
+ {
+ *aut++ = *p++;
+ while (*p == ' ')
+ p++;
+ continue;
+ }
+ *aut++ = *p++;
+ }
+ while (aut != str && aut[-1] == '\n')
+ aut--;
+ *aut = 0;
+ if (*str)
+ setutf8string(data, handle, SOLVABLE_AUTHORS, str);
+ free(str);
+ }
+ else if (*str)
+ setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
+}
+
+static int
+rpm2solv(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead, int flags)
+{
+ char *name;
+ char *evr;
+ char *sourcerpm;
+
+ name = headstring(rpmhead, TAG_NAME);
+ if (!name)
+ {
+ pool_error(pool, 0, "package has no name");
+ return 0;
+ }
+ if (!strcmp(name, "gpg-pubkey"))
+ return 0;
+ s->name = pool_str2id(pool, name, 1);
+ sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
+ if (sourcerpm || (rpmhead->forcebinary && !headexists(rpmhead, TAG_SOURCEPACKAGE)))
+ s->arch = pool_str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
+ else
+ {
+ if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
+ s->arch = ARCH_NOSRC;
+ else
+ s->arch = ARCH_SRC;
+ }
+ if (!s->arch)
+ s->arch = ARCH_NOARCH;
+ evr = headtoevr(rpmhead);
+ s->evr = pool_str2id(pool, evr, 1);
+ s->vendor = pool_str2id(pool, headstring(rpmhead, TAG_VENDOR), 1);
+
+ s->provides = makedeps(pool, repo, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
+ if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ s->requires = makedeps(pool, repo, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, flags);
+ s->conflicts = makedeps(pool, repo, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
+ s->obsoletes = makedeps(pool, repo, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
+
+ s->recommends = makedeps(pool, repo, rpmhead, TAG_RECOMMENDNAME, TAG_RECOMMENDVERSION, TAG_RECOMMENDFLAGS, 0);
+ s->suggests = makedeps(pool, repo, rpmhead, TAG_SUGGESTNAME, TAG_SUGGESTVERSION, TAG_SUGGESTFLAGS, 0);
+ s->supplements = makedeps(pool, repo, rpmhead, TAG_SUPPLEMENTNAME, TAG_SUPPLEMENTVERSION, TAG_SUPPLEMENTFLAGS, 0);
+ s->enhances = makedeps(pool, repo, rpmhead, TAG_ENHANCENAME, TAG_ENHANCEVERSION, TAG_ENHANCEFLAGS, 0);
+
+ s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
+ s->conflicts = repo_fix_conflicts(repo, s->conflicts);
+
+ if (data)
+ {
+ Id handle;
+ char *str;
+ unsigned int u32;
+ unsigned long long u64;
+
+ handle = s - pool->solvables;
+ str = headstring(rpmhead, TAG_SUMMARY);
+ if (str)
+ setutf8string(data, handle, SOLVABLE_SUMMARY, str);
+ str = headstring(rpmhead, TAG_DESCRIPTION);
+ if (str)
+ set_description_author(data, handle, str);
+ str = headstring(rpmhead, TAG_GROUP);
+ if (str)
+ repodata_set_poolstr(data, handle, SOLVABLE_GROUP, str);
+ str = headstring(rpmhead, TAG_LICENSE);
+ if (str)
+ repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, str);
+ str = headstring(rpmhead, TAG_URL);
+ if (str)
+ repodata_set_str(data, handle, SOLVABLE_URL, str);
+ str = headstring(rpmhead, TAG_DISTRIBUTION);
+ if (str)
+ repodata_set_poolstr(data, handle, SOLVABLE_DISTRIBUTION, str);
+ str = headstring(rpmhead, TAG_PACKAGER);
+ if (str)
+ repodata_set_poolstr(data, handle, SOLVABLE_PACKAGER, str);
+ if ((flags & RPM_ADD_WITH_PKGID) != 0)
+ {
+ unsigned char *chksum;
+ unsigned int chksumsize;
+ chksum = headbinary(rpmhead, TAG_SIGMD5, &chksumsize);
+ if (chksum && chksumsize == 16)
+ repodata_set_bin_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, chksum);
+ }
+ if ((flags & RPM_ADD_WITH_HDRID) != 0)
+ {
+ str = headstring(rpmhead, TAG_SHA1HEADER);
+ if (str && strlen(str) == 40)
+ repodata_set_checksum(data, handle, SOLVABLE_HDRID, REPOKEY_TYPE_SHA1, str);
+ else if (str && strlen(str) == 64)
+ repodata_set_checksum(data, handle, SOLVABLE_HDRID, REPOKEY_TYPE_SHA256, str);
+ }
+ u32 = headint32(rpmhead, TAG_BUILDTIME);
+ if (u32)
+ repodata_set_num(data, handle, SOLVABLE_BUILDTIME, u32);
+ u32 = headint32(rpmhead, TAG_INSTALLTIME);
+ if (u32)
+ repodata_set_num(data, handle, SOLVABLE_INSTALLTIME, u32);
+ u64 = headint64(rpmhead, TAG_LONGSIZE);
+ if (u64)
+ repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, u64);
+ else
+ {
+ u32 = headint32(rpmhead, TAG_SIZE);
+ if (u32)
+ repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, u32);
+ }
+ if (sourcerpm)
+ repodata_set_sourcepkg(data, handle, sourcerpm);
+ if ((flags & RPM_ADD_TRIGGERS) != 0)
+ {
+ Id id, lastid;
+ unsigned int ida = makedeps(pool, repo, rpmhead, TAG_TRIGGERNAME, TAG_TRIGGERVERSION, TAG_TRIGGERFLAGS, 0);
+
+ lastid = 0;
+ for (; (id = repo->idarraydata[ida]) != 0; ida++)
+ {
+ /* we currently do not support rel ids in incore data, so
+ * strip off versioning information */
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ id = rd->name;
+ }
+ if (id == lastid)
+ continue;
+ repodata_add_idarray(data, handle, SOLVABLE_TRIGGERS, id);
+ lastid = id;
+ }
+ }
+ if ((flags & RPM_ADD_NO_FILELIST) == 0)
+ addfilelist(data, handle, rpmhead, flags);
+ if ((flags & RPM_ADD_WITH_CHANGELOG) != 0)
+ addchangelog(data, handle, rpmhead);
+ }
+ solv_free(evr);
+ return 1;
+}
+
+
+/******************************************************************/
+/* Rpm Database stuff
+ */
+
+struct rpmdbstate {
+ Pool *pool;
+ char *rootdir;
+
+ RpmHead *rpmhead; /* header storage space */
+ int rpmheadsize;
+
+ int dbopened;
+ DB_ENV *dbenv; /* database environment */
+ DB *db; /* packages database */
+ int byteswapped; /* endianess of packages database */
+ int is_ostree; /* read-only db that lives in /usr/share/rpm */
+};
+
+struct rpmdbentry {
+ Id rpmdbid;
+ Id nameoff;
+};
+
+#define ENTRIES_BLOCK 255
+#define NAMEDATA_BLOCK 1023
+
+
+static inline Id db2rpmdbid(unsigned char *db, int byteswapped)
+{
+#ifdef RPM5
+ return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
+#else
+# if defined(WORDS_BIGENDIAN)
+ if (!byteswapped)
+# else
+ if (byteswapped)
+# endif
+ return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
+ else
+ return db[3] << 24 | db[2] << 16 | db[1] << 8 | db[0];
+#endif
+}
+
+static inline void rpmdbid2db(unsigned char *db, Id id, int byteswapped)
+{
+#ifdef RPM5
+ db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
+#else
+# if defined(WORDS_BIGENDIAN)
+ if (!byteswapped)
+# else
+ if (byteswapped)
+# endif
+ db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
+ else
+ db[3] = id >> 24, db[2] = id >> 16, db[1] = id >> 8, db[0] = id;
+#endif
+}
+
+#ifdef FEDORA
+int
+serialize_dbenv_ops(struct rpmdbstate *state)
+{
+ char lpath[PATH_MAX];
+ mode_t oldmask;
+ int fd;
+ struct flock fl;
+
+ snprintf(lpath, PATH_MAX, "%s/var/lib/rpm/.dbenv.lock", state->rootdir ? state->rootdir : "");
+ oldmask = umask(022);
+ fd = open(lpath, (O_RDWR|O_CREAT), 0644);
+ umask(oldmask);
+ if (fd < 0)
+ return -1;
+ memset(&fl, 0, sizeof(fl));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ for (;;)
+ {
+ if (fcntl(fd, F_SETLKW, &fl) != -1)
+ return fd;
+ if (errno != EINTR)
+ break;
+ }
+ close(fd);
+ return -1;
+}
+#endif
+
+/* should look in /usr/lib/rpm/macros instead, but we want speed... */
+static int
+opendbenv(struct rpmdbstate *state)
+{
+ const char *rootdir = state->rootdir;
+ char dbpath[PATH_MAX];
+ DB_ENV *dbenv = 0;
+ int r;
+
+ if (db_env_create(&dbenv, 0))
+ return pool_error(state->pool, 0, "db_env_create: %s", strerror(errno));
+#if defined(FEDORA) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
+ dbenv->set_thread_count(dbenv, 8);
+#endif
+ snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm", rootdir ? rootdir : "");
+ if (access(dbpath, W_OK) == -1)
+ {
+ snprintf(dbpath, PATH_MAX, "%s/usr/share/rpm/Packages", rootdir ? rootdir : "");
+ if (access(dbpath, R_OK) == 0)
+ state->is_ostree = 1;
+ snprintf(dbpath, PATH_MAX, "%s%s", rootdir ? rootdir : "", state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
+ r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
+ }
+ else
+ {
+#ifdef FEDORA
+ int serialize_fd = serialize_dbenv_ops(state);
+ r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL, 0644);
+ if (serialize_fd >= 0)
+ close(serialize_fd);
+#else
+ r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
+#endif
+ }
+ if (r)
+ {
+ pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
+ dbenv->close(dbenv, 0);
+ return 0;
+ }
+ state->dbenv = dbenv;
+ return 1;
+}
+
+static void
+closedbenv(struct rpmdbstate *state)
+{
+#ifdef FEDORA
+ uint32_t eflags = 0;
+#endif
+
+ if (!state->dbenv)
+ return;
+#ifdef FEDORA
+ (void)state->dbenv->get_open_flags(state->dbenv, &eflags);
+ if (!(eflags & DB_PRIVATE))
+ {
+ int serialize_fd = serialize_dbenv_ops(state);
+ state->dbenv->close(state->dbenv, 0);
+ if (serialize_fd >= 0)
+ close(serialize_fd);
+ }
+ else
+ state->dbenv->close(state->dbenv, 0);
+#else
+ state->dbenv->close(state->dbenv, 0);
+#endif
+ state->dbenv = 0;
+}
+
+static int
+openpkgdb(struct rpmdbstate *state)
+{
+ if (state->dbopened)
+ return state->dbopened > 0 ? 1 : 0;
+ state->dbopened = -1;
+ if (!state->dbenv && !opendbenv(state))
+ return 0;
+ if (db_create(&state->db, state->dbenv, 0))
+ {
+ pool_error(state->pool, 0, "db_create: %s", strerror(errno));
+ state->db = 0;
+ closedbenv(state);
+ return 0;
+ }
+ if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
+ {
+ pool_error(state->pool, 0, "db->open Packages: %s", strerror(errno));
+ state->db->close(state->db, 0);
+ state->db = 0;
+ closedbenv(state);
+ return 0;
+ }
+ if (state->db->get_byteswapped(state->db, &state->byteswapped))
+ {
+ pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
+ state->db->close(state->db, 0);
+ state->db = 0;
+ closedbenv(state);
+ return 0;
+ }
+ state->dbopened = 1;
+ return 1;
+}
+
+/* get the rpmdbids of all installed packages from the Name index database.
+ * This is much faster then querying the big Packages database */
+static struct rpmdbentry *
+getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap)
+{
+ DB_ENV *dbenv = 0;
+ DB *db = 0;
+ DBC *dbc = 0;
+ int byteswapped;
+ DBT dbkey;
+ DBT dbdata;
+ unsigned char *dp;
+ int dl;
+ Id nameoff;
+
+ char *namedata = 0;
+ int namedatal = 0;
+ struct rpmdbentry *entries = 0;
+ int nentries = 0;
+
+ *nentriesp = 0;
+ if (namedatap)
+ *namedatap = 0;
+
+ if (!state->dbenv && !opendbenv(state))
+ return 0;
+ dbenv = state->dbenv;
+ if (db_create(&db, dbenv, 0))
+ {
+ pool_error(state->pool, 0, "db_create: %s", strerror(errno));
+ return 0;
+ }
+ if (db->open(db, 0, index, 0, DB_UNKNOWN, DB_RDONLY, 0664))
+ {
+ pool_error(state->pool, 0, "db->open %s: %s", index, strerror(errno));
+ db->close(db, 0);
+ return 0;
+ }
+ if (db->get_byteswapped(db, &byteswapped))
+ {
+ pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
+ db->close(db, 0);
+ return 0;
+ }
+ if (db->cursor(db, NULL, &dbc, 0))
+ {
+ pool_error(state->pool, 0, "db->cursor: %s", strerror(errno));
+ db->close(db, 0);
+ return 0;
+ }
+ memset(&dbkey, 0, sizeof(dbkey));
+ memset(&dbdata, 0, sizeof(dbdata));
+ if (match)
+ {
+ dbkey.data = (void *)match;
+ dbkey.size = strlen(match);
+ }
+ while (dbc->c_get(dbc, &dbkey, &dbdata, match ? DB_SET : DB_NEXT) == 0)
+ {
+ if (!match && dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
+ continue;
+ dl = dbdata.size;
+ dp = dbdata.data;
+ nameoff = namedatal;
+ if (namedatap)
+ {
+ namedata = solv_extend(namedata, namedatal, dbkey.size + 1, 1, NAMEDATA_BLOCK);
+ memcpy(namedata + namedatal, dbkey.data, dbkey.size);
+ namedata[namedatal + dbkey.size] = 0;
+ namedatal += dbkey.size + 1;
+ }
+ while(dl >= RPM_INDEX_SIZE)
+ {
+ entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
+ entries[nentries].rpmdbid = db2rpmdbid(dp, byteswapped);
+ entries[nentries].nameoff = nameoff;
+ nentries++;
+ dp += RPM_INDEX_SIZE;
+ dl -= RPM_INDEX_SIZE;
+ }
+ if (match)
+ break;
+ }
+ dbc->c_close(dbc);
+ db->close(db, 0);
+ /* make sure that enteries is != 0 if there was no error */
+ if (!entries)
+ entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
+ *nentriesp = nentries;
+ if (namedatap)
+ *namedatap = namedata;
+ return entries;
+}
+
+/* retrive header by rpmdbid */
+static int
+getrpmdbid(struct rpmdbstate *state, Id rpmdbid)
+{
+ unsigned char buf[16];
+ DBT dbkey;
+ DBT dbdata;
+ RpmHead *rpmhead;
+
+ if (!rpmdbid)
+ {
+ pool_error(state->pool, 0, "illegal rpmdbid");
+ return -1;
+ }
+ if (state->dbopened != 1 && !openpkgdb(state))
+ return -1;
+ rpmdbid2db(buf, rpmdbid, state->byteswapped);
+ memset(&dbkey, 0, sizeof(dbkey));
+ memset(&dbdata, 0, sizeof(dbdata));
+ dbkey.data = buf;
+ dbkey.size = 4;
+ dbdata.data = 0;
+ dbdata.size = 0;
+ if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
+ return 0;
+ if (dbdata.size < 8)
+ {
+ pool_error(state->pool, 0, "corrupt rpm database (size)");
+ return -1;
+ }
+ if (dbdata.size > state->rpmheadsize)
+ {
+ state->rpmheadsize = dbdata.size + 128;
+ state->rpmhead = solv_realloc(state->rpmhead, sizeof(*rpmhead) + state->rpmheadsize);
+ }
+ rpmhead = state->rpmhead;
+ memcpy(buf, dbdata.data, 8);
+ rpmhead->forcebinary = 1;
+ 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 > dbdata.size)
+ {
+ pool_error(state->pool, 0, "corrupt rpm database (data size)");
+ return -1;
+ }
+ memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
+ rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+ return 1;
+}
+
+/* retrive header by berkeleydb cursor */
+static Id
+getrpmcursor(struct rpmdbstate *state, DBC *dbc)
+{
+ unsigned char buf[16];
+ DBT dbkey;
+ DBT dbdata;
+ RpmHead *rpmhead;
+ Id dbid;
+
+ memset(&dbkey, 0, sizeof(dbkey));
+ memset(&dbdata, 0, sizeof(dbdata));
+ while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
+ {
+ if (dbkey.size != 4)
+ return pool_error(state->pool, -1, "corrupt Packages database (key size)");
+ dbid = db2rpmdbid(dbkey.data, state->byteswapped);
+ if (dbid == 0) /* the join key */
+ continue;
+ if (dbdata.size < 8)
+ return pool_error(state->pool, -1, "corrupt rpm database (size %u)\n", dbdata.size);
+ if (dbdata.size > state->rpmheadsize)
+ {
+ state->rpmheadsize = dbdata.size + 128;
+ state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
+ }
+ rpmhead = state->rpmhead;
+ memcpy(buf, dbdata.data, 8);
+ rpmhead->forcebinary = 1;
+ 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 > dbdata.size)
+ return pool_error(state->pool, -1, "corrupt rpm database (data size)\n");
+ memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
+ rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+ return dbid;
+ }
+ return 0;
+}
+
+static void
+freestate(struct rpmdbstate *state)
+{
+ /* close down */
+ if (!state)
+ return;
+ if (state->db)
+ state->db->close(state->db, 0);
+ if (state->dbenv)
+ closedbenv(state);
+ if (state->rootdir)
+ solv_free(state->rootdir);
+ solv_free(state->rpmhead);
+}
+
+void *
+rpm_state_create(Pool *pool, const char *rootdir)
+{
+ struct rpmdbstate *state;
+ state = solv_calloc(1, sizeof(*state));
+ state->pool = pool;
+ if (rootdir)
+ state->rootdir = solv_strdup(rootdir);
+ return state;
+}
+
+void *
+rpm_state_free(void *state)
+{
+ freestate(state);
+ return solv_free(state);
+}
+
+static int
+count_headers(struct rpmdbstate *state)
+{
+ Pool *pool = state->pool;
+ char dbpath[PATH_MAX];
+ struct stat statbuf;
+ DB *db = 0;
+ DBC *dbc = 0;
+ int count = 0;
+ DBT dbkey;
+ DBT dbdata;
+
+ snprintf(dbpath, PATH_MAX, "%s%s/Name", state->rootdir ? state->rootdir : "", state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
+ if (stat(dbpath, &statbuf))
+ return 0;
+ memset(&dbkey, 0, sizeof(dbkey));
+ memset(&dbdata, 0, sizeof(dbdata));
+ if (db_create(&db, state->dbenv, 0))
+ {
+ pool_error(pool, 0, "db_create: %s", strerror(errno));
+ return 0;
+ }
+ if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
+ {
+ pool_error(pool, 0, "db->open Name: %s", strerror(errno));
+ db->close(db, 0);
+ return 0;
+ }
+ if (db->cursor(db, NULL, &dbc, 0))
+ {
+ db->close(db, 0);
+ pool_error(pool, 0, "db->cursor: %s", strerror(errno));
+ return 0;
+ }
+ while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
+ count += dbdata.size / RPM_INDEX_SIZE;
+ dbc->c_close(dbc);
+ db->close(db, 0);
+ return count;
+}
+
+/******************************************************************/
+
+static Offset
+copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
+{
+ int cc;
+ Id *ida, *from;
+ Offset ido;
+
+ if (!fromoff)
+ return 0;
+ from = fromrepo->idarraydata + fromoff;
+ for (ida = from, cc = 0; *ida; ida++, cc++)
+ ;
+ if (cc == 0)
+ return 0;
+ ido = repo_reserve_ids(repo, 0, cc);
+ ida = repo->idarraydata + ido;
+ memcpy(ida, from, (cc + 1) * sizeof(Id));
+ repo->idarraysize += cc + 1;
+ return ido;
+}
+
+#define COPYDIR_DIRCACHE_SIZE 512
+
+static Id copydir_complex(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache);
+
+static inline Id
+copydir(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache)
+{
+ if (cache && cache[did & 255] == did)
+ return cache[(did & 255) + 256];
+ return copydir_complex(pool, data, fromdata, did, cache);
+}
+
+static Id
+copydir_complex(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache)
+{
+ Id parent = dirpool_parent(&fromdata->dirpool, did);
+ Id compid = dirpool_compid(&fromdata->dirpool, did);
+ if (parent)
+ parent = copydir(pool, data, fromdata, parent, cache);
+ if (data->localpool || fromdata->localpool)
+ compid = repodata_translate_id(data, fromdata, compid, 1);
+ compid = dirpool_add_dir(&data->dirpool, parent, compid, 1);
+ if (cache)
+ {
+ cache[did & 255] = did;
+ cache[(did & 255) + 256] = compid;
+ }
+ return compid;
+}
+
+struct solvable_copy_cbdata {
+ Repodata *data;
+ Id handle;
+ Id subhandle;
+ Id *dircache;
+};
+
+static int
+solvable_copy_cb(void *vcbdata, Solvable *r, Repodata *fromdata, Repokey *key, KeyValue *kv)
+{
+ struct solvable_copy_cbdata *cbdata = vcbdata;
+ Id id, keyname;
+ Repodata *data = cbdata->data;
+ Id handle = cbdata->handle;
+ Pool *pool = data->repo->pool;
+
+ keyname = key->name;
+ switch(key->type)
+ {
+ case REPOKEY_TYPE_ID:
+ case REPOKEY_TYPE_CONSTANTID:
+ case REPOKEY_TYPE_IDARRAY: /* used for triggers */
+ id = kv->id;
+ if (data->localpool || fromdata->localpool)
+ id = repodata_translate_id(data, fromdata, id, 1);
+ if (key->type == REPOKEY_TYPE_ID)
+ repodata_set_id(data, handle, keyname, id);
+ else if (key->type == REPOKEY_TYPE_CONSTANTID)
+ repodata_set_constantid(data, handle, keyname, id);
+ else
+ repodata_add_idarray(data, handle, keyname, id);
+ break;
+ case REPOKEY_TYPE_STR:
+ repodata_set_str(data, handle, keyname, kv->str);
+ break;
+ case REPOKEY_TYPE_VOID:
+ repodata_set_void(data, handle, keyname);
+ break;
+ case REPOKEY_TYPE_NUM:
+ repodata_set_num(data, handle, keyname, SOLV_KV_NUM64(kv));
+ break;
+ case REPOKEY_TYPE_CONSTANT:
+ repodata_set_constant(data, handle, keyname, kv->num);
+ break;
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ id = kv->id;
+ id = copydir(pool, data, fromdata, id, cbdata->dircache);
+ repodata_add_dirnumnum(data, handle, keyname, id, kv->num, kv->num2);
+ break;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ id = kv->id;
+ id = copydir(pool, data, fromdata, id, cbdata->dircache);
+ repodata_add_dirstr(data, handle, keyname, id, kv->str);
+ break;
+ case REPOKEY_TYPE_FLEXARRAY:
+ if (kv->eof == 2)
+ {
+ assert(cbdata->subhandle);
+ cbdata->handle = cbdata->subhandle;
+ cbdata->subhandle = 0;
+ break;
+ }
+ if (!kv->entry)
+ {
+ assert(!cbdata->subhandle);
+ cbdata->subhandle = cbdata->handle;
+ }
+ cbdata->handle = repodata_new_handle(data);
+ repodata_add_flexarray(data, cbdata->subhandle, keyname, cbdata->handle);
+ break;
+ default:
+ if (solv_chksum_len(key->type))
+ {
+ repodata_set_bin_checksum(data, handle, keyname, key->type, (const unsigned char *)kv->str);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void
+solvable_copy(Solvable *s, Solvable *r, Repodata *data, Id *dircache)
+{
+ int p, i;
+ Repo *repo = s->repo;
+ Pool *pool = repo->pool;
+ Repo *fromrepo = r->repo;
+ struct solvable_copy_cbdata cbdata;
+
+ /* copy solvable data */
+ s->name = r->name;
+ s->evr = r->evr;
+ s->arch = r->arch;
+ s->vendor = r->vendor;
+ s->provides = copydeps(pool, repo, r->provides, fromrepo);
+ s->requires = copydeps(pool, repo, r->requires, fromrepo);
+ s->conflicts = copydeps(pool, repo, r->conflicts, fromrepo);
+ s->obsoletes = copydeps(pool, repo, r->obsoletes, fromrepo);
+ s->recommends = copydeps(pool, repo, r->recommends, fromrepo);
+ s->suggests = copydeps(pool, repo, r->suggests, fromrepo);
+ s->supplements = copydeps(pool, repo, r->supplements, fromrepo);
+ s->enhances = copydeps(pool, repo, r->enhances, fromrepo);
+
+ /* copy all attributes */
+ if (!data)
+ return;
+ cbdata.data = data;
+ cbdata.handle = s - pool->solvables;
+ cbdata.subhandle = 0;
+ cbdata.dircache = dircache;
+ p = r - fromrepo->pool->solvables;
+#if 0
+ repo_search(fromrepo, p, 0, 0, SEARCH_NO_STORAGE_SOLVABLE | SEARCH_SUB | SEARCH_ARRAYSENTINEL, solvable_copy_cb, &cbdata);
+#else
+ FOR_REPODATAS(fromrepo, i, data)
+ {
+ if (p >= data->start && p < data->end)
+ repodata_search(data, p, 0, SEARCH_SUB | SEARCH_ARRAYSENTINEL, solvable_copy_cb, &cbdata);
+ cbdata.dircache = 0; /* only for first repodata */
+ }
+#endif
+}
+
+/* used to sort entries by package name that got returned in some database order */
+static int
+rpmids_sort_cmp(const void *va, const void *vb, void *dp)
+{
+ struct rpmdbentry const *a = va, *b = vb;
+ char *namedata = dp;
+ int r;
+ r = strcmp(namedata + a->nameoff, namedata + b->nameoff);
+ if (r)
+ return r;
+ return a->rpmdbid - b->rpmdbid;
+}
+
+static int
+pkgids_sort_cmp(const void *va, const void *vb, void *dp)
+{
+ Repo *repo = dp;
+ Pool *pool = repo->pool;
+ Solvable *a = pool->solvables + *(Id *)va;
+ Solvable *b = pool->solvables + *(Id *)vb;
+ Id *rpmdbid;
+
+ if (a->name != b->name)
+ return strcmp(pool_id2str(pool, a->name), pool_id2str(pool, b->name));
+ rpmdbid = repo->rpmdbid;
+ return rpmdbid[(a - pool->solvables) - repo->start] - rpmdbid[(b - pool->solvables) - repo->start];
+}
+
+static void
+swap_solvables(Repo *repo, Repodata *data, Id pa, Id pb)
+{
+ Pool *pool = repo->pool;
+ Solvable tmp;
+
+ tmp = pool->solvables[pa];
+ pool->solvables[pa] = pool->solvables[pb];
+ pool->solvables[pb] = tmp;
+ if (repo->rpmdbid)
+ {
+ Id tmpid = repo->rpmdbid[pa - repo->start];
+ repo->rpmdbid[pa - repo->start] = repo->rpmdbid[pb - repo->start];
+ repo->rpmdbid[pb - repo->start] = tmpid;
+ }
+ /* only works if nothing is already internalized! */
+ if (data)
+ repodata_swap_attrs(data, pa, pb);
+}
+
+static void
+mkrpmdbcookie(struct stat *st, unsigned char *cookie, int flags)
+{
+ int f = 0;
+ memset(cookie, 0, 32);
+ cookie[3] = RPMDB_COOKIE_VERSION;
+ memcpy(cookie + 16, &st->st_ino, sizeof(st->st_ino));
+ memcpy(cookie + 24, &st->st_dev, sizeof(st->st_dev));
+ if ((flags & RPM_ADD_WITH_PKGID) != 0)
+ f |= 1;
+ if ((flags & RPM_ADD_WITH_HDRID) != 0)
+ f |= 2;
+ if ((flags & RPM_ADD_WITH_CHANGELOG) != 0)
+ f |= 4;
+ if ((flags & RPM_ADD_NO_FILELIST) == 0)
+ f |= 8;
+ if ((flags & RPM_ADD_NO_RPMLIBREQS) != 0)
+ cookie[1] = 1;
+ cookie[0] = f;
+}
+
+/*
+ * read rpm db as repo
+ *
+ */
+
+int
+repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
+{
+ Pool *pool = repo->pool;
+ char dbpath[PATH_MAX];
+ struct stat packagesstat;
+ unsigned char newcookie[32];
+ const unsigned char *oldcookie = 0;
+ Id oldcookietype = 0;
+ Repodata *data;
+ int count = 0, done = 0;
+ struct rpmdbstate state;
+ int i;
+ Solvable *s;
+ unsigned int now;
+
+ now = solv_timems(0);
+ memset(&state, 0, sizeof(state));
+ state.pool = pool;
+ if (flags & REPO_USE_ROOTDIR)
+ state.rootdir = solv_strdup(pool_get_rootdir(pool));
+
+ data = repo_add_repodata(repo, flags);
+
+ if (ref && !(ref->nsolvables && ref->rpmdbid && ref->pool == repo->pool))
+ {
+ if ((flags & RPMDB_EMPTY_REFREPO) != 0)
+ repo_empty(ref, 1);
+ ref = 0;
+ }
+
+ if (!opendbenv(&state))
+ {
+ solv_free(state.rootdir);
+ return -1;
+ }
+
+ /* XXX: should get ro lock of Packages database! */
+ snprintf(dbpath, PATH_MAX, "%s%s/Packages", state.rootdir ? state.rootdir : "", state.is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
+ if (stat(dbpath, &packagesstat))
+ {
+ pool_error(pool, -1, "%s: %s", dbpath, strerror(errno));
+ freestate(&state);
+ return -1;
+ }
+ mkrpmdbcookie(&packagesstat, newcookie, flags);
+ repodata_set_bin_checksum(data, SOLVID_META, REPOSITORY_RPMDBCOOKIE, REPOKEY_TYPE_SHA256, newcookie);
+
+ if (ref)
+ oldcookie = repo_lookup_bin_checksum(ref, SOLVID_META, REPOSITORY_RPMDBCOOKIE, &oldcookietype);
+ if (!ref || !oldcookie || oldcookietype != REPOKEY_TYPE_SHA256 || memcmp(oldcookie, newcookie, 32) != 0)
+ {
+ int solvstart = 0, solvend = 0;
+ Id dbid;
+ DBC *dbc = 0;
+
+ if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
+ repo_empty(ref, 1); /* get it out of the way */
+ if ((flags & RPMDB_REPORT_PROGRESS) != 0)
+ count = count_headers(&state);
+ if (!openpkgdb(&state))
+ {
+ freestate(&state);
+ return -1;
+ }
+ if (state.db->cursor(state.db, NULL, &dbc, 0))
+ {
+ freestate(&state);
+ return pool_error(pool, -1, "db->cursor failed");
+ }
+ i = 0;
+ s = 0;
+ while ((dbid = getrpmcursor(&state, dbc)) != 0)
+ {
+ if (dbid == -1)
+ {
+ dbc->c_close(dbc);
+ freestate(&state);
+ return -1;
+ }
+ if (!s)
+ {
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ if (!solvstart)
+ solvstart = s - pool->solvables;
+ solvend = s - pool->solvables + 1;
+ }
+ if (!repo->rpmdbid)
+ repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
+ repo->rpmdbid[(s - pool->solvables) - repo->start] = dbid;
+ if (rpm2solv(pool, repo, data, s, state.rpmhead, flags | RPM_ADD_TRIGGERS))
+ {
+ i++;
+ s = 0;
+ }
+ else
+ {
+ /* We can reuse this solvable, but make sure it's still
+ associated with this repo. */
+ memset(s, 0, sizeof(*s));
+ s->repo = repo;
+ }
+ if ((flags & RPMDB_REPORT_PROGRESS) != 0)
+ {
+ if (done < count)
+ done++;
+ if (done < count && (done - 1) * 100 / count != done * 100 / count)
+ pool_debug(pool, SOLV_ERROR, "%%%% %d\n", done * 100 / count);
+ }
+ }
+ dbc->c_close(dbc);
+ if (s)
+ {
+ /* oops, could not reuse. free it instead */
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ solvend--;
+ s = 0;
+ }
+ /* now sort all solvables in the new solvstart..solvend block */
+ if (solvend - solvstart > 1)
+ {
+ Id *pkgids = solv_malloc2(solvend - solvstart, sizeof(Id));
+ for (i = solvstart; i < solvend; i++)
+ pkgids[i - solvstart] = i;
+ solv_sort(pkgids, solvend - solvstart, sizeof(Id), pkgids_sort_cmp, repo);
+ /* adapt order */
+ for (i = solvstart; i < solvend; i++)
+ {
+ int j = pkgids[i - solvstart];
+ while (j < i)
+ j = pkgids[i - solvstart] = pkgids[j - solvstart];
+ if (j != i)
+ swap_solvables(repo, data, i, j);
+ }
+ solv_free(pkgids);
+ }
+ }
+ else
+ {
+ Id dircache[COPYDIR_DIRCACHE_SIZE]; /* see copydir */
+ struct rpmdbentry *entries = 0, *rp;
+ int nentries = 0;
+ char *namedata = 0;
+ unsigned int refmask, h;
+ Id id, *refhash;
+ int res;
+
+ memset(dircache, 0, sizeof(dircache));
+
+ /* get ids of installed rpms */
+ entries = getinstalledrpmdbids(&state, "Name", 0, &nentries, &namedata);
+ if (!entries)
+ {
+ freestate(&state);
+ return -1;
+ }
+
+ /* sort by name */
+ if (nentries > 1)
+ solv_sort(entries, nentries, sizeof(*entries), rpmids_sort_cmp, namedata);
+
+ /* create hash from dbid to ref */
+ refmask = mkmask(ref->nsolvables);
+ refhash = solv_calloc(refmask + 1, sizeof(Id));
+ for (i = 0; i < ref->end - ref->start; i++)
+ {
+ if (!ref->rpmdbid[i])
+ continue;
+ h = ref->rpmdbid[i] & refmask;
+ while (refhash[h])
+ h = (h + 317) & refmask;
+ refhash[h] = i + 1; /* make it non-zero */
+ }
+
+ /* count the misses, they will cost us time */
+ if ((flags & RPMDB_REPORT_PROGRESS) != 0)
+ {
+ for (i = 0, rp = entries; i < nentries; i++, rp++)
+ {
+ if (refhash)
+ {
+ Id dbid = rp->rpmdbid;
+ h = dbid & refmask;
+ while ((id = refhash[h]))
+ {
+ if (ref->rpmdbid[id - 1] == dbid)
+ break;
+ h = (h + 317) & refmask;
+ }
+ if (id)
+ continue;
+ }
+ count++;
+ }
+ }
+
+ if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
+ s = pool_id2solvable(pool, repo_add_solvable_block_before(repo, nentries, ref));
+ else
+ s = pool_id2solvable(pool, repo_add_solvable_block(repo, nentries));
+ if (!repo->rpmdbid)
+ repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
+
+ for (i = 0, rp = entries; i < nentries; i++, rp++, s++)
+ {
+ Id dbid = rp->rpmdbid;
+ repo->rpmdbid[(s - pool->solvables) - repo->start] = rp->rpmdbid;
+ 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 (r->repo == ref)
+ {
+ solvable_copy(s, r, data, dircache);
+ continue;
+ }
+ }
+ }
+ res = getrpmdbid(&state, dbid);
+ if (res <= 0)
+ {
+ if (!res)
+ pool_error(pool, -1, "inconsistent rpm database, key %d not found. run 'rpm --rebuilddb' to fix.", dbid);
+ freestate(&state);
+ solv_free(entries);
+ solv_free(namedata);
+ solv_free(refhash);
+ return -1;
+ }
+ rpm2solv(pool, repo, data, s, state.rpmhead, flags | RPM_ADD_TRIGGERS);
+ if ((flags & RPMDB_REPORT_PROGRESS) != 0)
+ {
+ if (done < count)
+ done++;
+ if (done < count && (done - 1) * 100 / count != done * 100 / count)
+ pool_debug(pool, SOLV_ERROR, "%%%% %d\n", done * 100 / count);
+ }
+ }
+
+ solv_free(entries);
+ solv_free(namedata);
+ solv_free(refhash);
+ if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
+ repo_empty(ref, 1);
+ }
+
+ freestate(&state);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ if ((flags & RPMDB_REPORT_PROGRESS) != 0)
+ pool_debug(pool, SOLV_ERROR, "%%%% 100\n");
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_rpmdb took %d ms\n", solv_timems(now));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
+ return 0;
+}
+
+int
+repo_add_rpmdb_reffp(Repo *repo, FILE *fp, int flags)
+{
+ int res;
+ Repo *ref = 0;
+
+ if (!fp)
+ return repo_add_rpmdb(repo, 0, flags);
+ ref = repo_create(repo->pool, "add_rpmdb_reffp");
+ if (repo_add_solv(ref, fp, 0) != 0)
+ {
+ repo_free(ref, 1);
+ ref = 0;
+ }
+ if (ref && ref->start == ref->end)
+ {
+ repo_free(ref, 1);
+ ref = 0;
+ }
+ if (ref)
+ repo_disable_paging(ref);
+ res = repo_add_rpmdb(repo, ref, flags | RPMDB_EMPTY_REFREPO);
+ if (ref)
+ repo_free(ref, 1);
+ return res;
+}
+
+static inline unsigned int
+getu32(const unsigned char *dp)
+{
+ return dp[0] << 24 | dp[1] << 16 | dp[2] << 8 | dp[3];
+}
+
+
+Id
+repo_add_rpm(Repo *repo, const char *rpm, int flags)
+{
+ unsigned int sigdsize, sigcnt, l;
+ Pool *pool = repo->pool;
+ Solvable *s;
+ RpmHead *rpmhead = 0;
+ int rpmheadsize = 0;
+ char *payloadformat;
+ FILE *fp;
+ unsigned char lead[4096];
+ int headerstart, headerend;
+ struct stat stb;
+ Repodata *data;
+ unsigned char pkgid[16];
+ unsigned char leadsigid[16];
+ unsigned char hdrid[32];
+ int pkgidtype, leadsigidtype, hdridtype;
+ Id chksumtype = 0;
+ Chksum *chksumh = 0;
+ Chksum *leadsigchksumh = 0;
+ int forcebinary = 0;
+
+ data = repo_add_repodata(repo, flags);
+
+ if ((flags & RPM_ADD_WITH_SHA256SUM) != 0)
+ chksumtype = REPOKEY_TYPE_SHA256;
+ else if ((flags & RPM_ADD_WITH_SHA1SUM) != 0)
+ chksumtype = REPOKEY_TYPE_SHA1;
+
+ if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, rpm) : rpm, "r")) == 0)
+ {
+ pool_error(pool, -1, "%s: %s", rpm, strerror(errno));
+ return 0;
+ }
+ if (fstat(fileno(fp), &stb))
+ {
+ pool_error(pool, -1, "fstat: %s", strerror(errno));
+ fclose(fp);
+ return 0;
+ }
+ if (chksumtype)
+ chksumh = solv_chksum_create(chksumtype);
+ if ((flags & RPM_ADD_WITH_LEADSIGID) != 0)
+ leadsigchksumh = solv_chksum_create(REPOKEY_TYPE_MD5);
+ if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
+ {
+ pool_error(pool, -1, "%s: not a rpm", rpm);
+ fclose(fp);
+ return 0;
+ }
+ forcebinary = lead[6] != 0 || lead[7] != 1;
+ if (chksumh)
+ solv_chksum_add(chksumh, lead, 96 + 16);
+ if (leadsigchksumh)
+ solv_chksum_add(leadsigchksumh, lead, 96 + 16);
+ if (lead[78] != 0 || lead[79] != 5)
+ {
+ pool_error(pool, -1, "%s: not a rpm v5 header", rpm);
+ fclose(fp);
+ return 0;
+ }
+ if (getu32(lead + 96) != 0x8eade801)
+ {
+ pool_error(pool, -1, "%s: bad signature header", rpm);
+ fclose(fp);
+ return 0;
+ }
+ sigcnt = getu32(lead + 96 + 8);
+ sigdsize = getu32(lead + 96 + 12);
+ if (sigcnt >= 0x100000 || sigdsize >= 0x100000)
+ {
+ pool_error(pool, -1, "%s: bad signature header", rpm);
+ fclose(fp);
+ return 0;
+ }
+ sigdsize += sigcnt * 16;
+ sigdsize = (sigdsize + 7) & ~7;
+ headerstart = 96 + 16 + sigdsize;
+ pkgidtype = leadsigidtype = hdridtype = 0;
+ if ((flags & (RPM_ADD_WITH_PKGID | RPM_ADD_WITH_HDRID)) != 0)
+ {
+ /* extract pkgid or hdrid from the signature header */
+ if (sigdsize > rpmheadsize)
+ {
+ rpmheadsize = sigdsize + 128;
+ rpmhead = solv_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
+ }
+ if (fread(rpmhead->data, sigdsize, 1, fp) != 1)
+ {
+ pool_error(pool, -1, "%s: unexpected EOF", rpm);
+ fclose(fp);
+ return 0;
+ }
+ if (chksumh)
+ solv_chksum_add(chksumh, rpmhead->data, sigdsize);
+ if (leadsigchksumh)
+ solv_chksum_add(leadsigchksumh, rpmhead->data, sigdsize);
+ rpmhead->forcebinary = 0;
+ rpmhead->cnt = sigcnt;
+ rpmhead->dcnt = sigdsize - sigcnt * 16;
+ rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+ if ((flags & RPM_ADD_WITH_PKGID) != 0)
+ {
+ unsigned char *chksum;
+ unsigned int chksumsize;
+ chksum = headbinary(rpmhead, SIGTAG_MD5, &chksumsize);
+ if (chksum && chksumsize == 16)
+ {
+ pkgidtype = REPOKEY_TYPE_MD5;
+ memcpy(pkgid, chksum, 16);
+ }
+ }
+ if ((flags & RPM_ADD_WITH_HDRID) != 0)
+ {
+ const char *str = headstring(rpmhead, TAG_SHA1HEADER);
+ if (str && strlen(str) == 40)
+ {
+ if (solv_hex2bin(&str, hdrid, 20) == 20)
+ hdridtype = REPOKEY_TYPE_SHA1;
+ }
+ else if (str && strlen(str) == 64)
+ {
+ if (solv_hex2bin(&str, hdrid, 32) == 32)
+ hdridtype = REPOKEY_TYPE_SHA256;
+ }
+ }
+ }
+ else
+ {
+ /* just skip the signature header */
+ while (sigdsize)
+ {
+ l = sigdsize > 4096 ? 4096 : sigdsize;
+ if (fread(lead, l, 1, fp) != 1)
+ {
+ pool_error(pool, -1, "%s: unexpected EOF", rpm);
+ fclose(fp);
+ return 0;
+ }
+ if (chksumh)
+ solv_chksum_add(chksumh, lead, l);
+ if (leadsigchksumh)
+ solv_chksum_add(leadsigchksumh, lead, l);
+ sigdsize -= l;
+ }
+ }
+ if (leadsigchksumh)
+ {
+ leadsigchksumh = solv_chksum_free(leadsigchksumh, leadsigid);
+ leadsigidtype = REPOKEY_TYPE_MD5;
+ }
+ if (fread(lead, 16, 1, fp) != 1)
+ {
+ pool_error(pool, -1, "%s: unexpected EOF", rpm);
+ fclose(fp);
+ return 0;
+ }
+ if (chksumh)
+ solv_chksum_add(chksumh, lead, 16);
+ if (getu32(lead) != 0x8eade801)
+ {
+ pool_error(pool, -1, "%s: bad header", rpm);
+ fclose(fp);
+ return 0;
+ }
+ sigcnt = getu32(lead + 8);
+ sigdsize = getu32(lead + 12);
+ if (sigcnt >= 0x100000 || sigdsize >= 0x2000000)
+ {
+ pool_error(pool, -1, "%s: bad header", rpm);
+ fclose(fp);
+ return 0;
+ }
+ l = sigdsize + sigcnt * 16;
+ headerend = headerstart + 16 + l;
+ if (l > rpmheadsize)
+ {
+ rpmheadsize = l + 128;
+ rpmhead = solv_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
+ }
+ if (fread(rpmhead->data, l, 1, fp) != 1)
+ {
+ pool_error(pool, -1, "%s: unexpected EOF", rpm);
+ fclose(fp);
+ return 0;
+ }
+ if (chksumh)
+ solv_chksum_add(chksumh, rpmhead->data, l);
+ rpmhead->forcebinary = forcebinary;
+ rpmhead->cnt = sigcnt;
+ rpmhead->dcnt = sigdsize;
+ rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+ if (headexists(rpmhead, TAG_PATCHESNAME))
+ {
+ /* this is a patch rpm, ignore */
+ pool_error(pool, -1, "%s: is patch rpm", rpm);
+ fclose(fp);
+ solv_chksum_free(chksumh, 0);
+ solv_free(rpmhead);
+ return 0;
+ }
+ payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
+ if (payloadformat && !strcmp(payloadformat, "drpm"))
+ {
+ /* this is a delta rpm */
+ pool_error(pool, -1, "%s: is delta rpm", rpm);
+ fclose(fp);
+ solv_chksum_free(chksumh, 0);
+ solv_free(rpmhead);
+ return 0;
+ }
+ if (chksumh)
+ while ((l = fread(lead, 1, sizeof(lead), fp)) > 0)
+ solv_chksum_add(chksumh, lead, l);
+ fclose(fp);
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ if (!rpm2solv(pool, repo, data, s, rpmhead, flags & ~(RPM_ADD_WITH_HDRID | RPM_ADD_WITH_PKGID)))
+ {
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ solv_chksum_free(chksumh, 0);
+ solv_free(rpmhead);
+ return 0;
+ }
+ if (!(flags & REPO_NO_LOCATION))
+ repodata_set_location(data, s - pool->solvables, 0, 0, rpm);
+ if (S_ISREG(stb.st_mode))
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_HEADEREND, headerend);
+ if (pkgidtype)
+ repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, pkgidtype, pkgid);
+ if (hdridtype)
+ repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_HDRID, hdridtype, hdrid);
+ if (leadsigidtype)
+ repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_LEADSIGID, leadsigidtype, leadsigid);
+ if (chksumh)
+ {
+ repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, chksumtype, solv_chksum_get(chksumh, 0));
+ chksumh = solv_chksum_free(chksumh, 0);
+ }
+ solv_free(rpmhead);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return s - pool->solvables;
+}
+
+Id
+repo_add_rpm_handle(Repo *repo, void *rpmhandle, int flags)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ RpmHead *rpmhead = rpmhandle;
+ Solvable *s;
+ char *payloadformat;
+
+ data = repo_add_repodata(repo, flags);
+ if (headexists(rpmhead, TAG_PATCHESNAME))
+ {
+ pool_error(pool, -1, "is a patch rpm");
+ return 0;
+ }
+ payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
+ if (payloadformat && !strcmp(payloadformat, "drpm"))
+ {
+ /* this is a delta rpm */
+ pool_error(pool, -1, "is a delta rpm");
+ return 0;
+ }
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ if (!rpm2solv(pool, repo, data, s, rpmhead, flags))
+ {
+ repo_free_solvable(repo, s - pool->solvables, 1);
+ return 0;
+ }
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return s - pool->solvables;
+}
+
+static inline void
+linkhash(const char *lt, char *hash)
+{
+ unsigned int r = 0;
+ const unsigned char *str = (const unsigned char *)lt;
+ int l, c;
+
+ l = strlen(lt);
+ while ((c = *str++) != 0)
+ r += (r << 3) + c;
+ sprintf(hash, "%08x%08x%08x%08x", r, l, 0, 0);
+}
+
+void
+rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, const char *, struct filelistinfo *), void *cbdata)
+{
+ RpmHead *rpmhead = rpmhandle;
+ char **bn;
+ char **dn;
+ char **md = 0;
+ char **lt = 0;
+ unsigned int *di, diidx;
+ unsigned int *co = 0;
+ unsigned int *ff = 0;
+ unsigned int lastdir;
+ int lastdirl;
+ unsigned int *fm;
+ int cnt, dcnt, cnt2;
+ int i, l1, l;
+ char *space = 0;
+ int spacen = 0;
+ char md5[33];
+ struct filelistinfo info;
+
+ dn = headstringarray(rpmhead, TAG_DIRNAMES, &dcnt);
+ if (!dn)
+ return;
+ if ((flags & RPM_ITERATE_FILELIST_ONLYDIRS) != 0)
+ {
+ for (i = 0; i < dcnt; i++)
+ (*cb)(cbdata, dn[i], 0);
+ solv_free(dn);
+ return;
+ }
+ bn = headstringarray(rpmhead, TAG_BASENAMES, &cnt);
+ if (!bn)
+ {
+ solv_free(dn);
+ return;
+ }
+ di = headint32array(rpmhead, TAG_DIRINDEXES, &cnt2);
+ if (!di || cnt != cnt2)
+ {
+ solv_free(di);
+ solv_free(bn);
+ solv_free(dn);
+ return;
+ }
+ fm = headint16array(rpmhead, TAG_FILEMODES, &cnt2);
+ if (!fm || cnt != cnt2)
+ {
+ solv_free(fm);
+ solv_free(di);
+ solv_free(bn);
+ solv_free(dn);
+ return;
+ }
+ if ((flags & RPM_ITERATE_FILELIST_WITHMD5) != 0)
+ {
+ md = headstringarray(rpmhead, TAG_FILEMD5S, &cnt2);
+ if (!md || cnt != cnt2)
+ {
+ solv_free(md);
+ solv_free(fm);
+ solv_free(di);
+ solv_free(bn);
+ solv_free(dn);
+ return;
+ }
+ }
+ if ((flags & RPM_ITERATE_FILELIST_WITHCOL) != 0)
+ {
+ co = headint32array(rpmhead, TAG_FILECOLORS, &cnt2);
+ if (!co || cnt != cnt2)
+ {
+ solv_free(co);
+ solv_free(md);
+ solv_free(fm);
+ solv_free(di);
+ solv_free(bn);
+ solv_free(dn);
+ return;
+ }
+ }
+ if ((flags & RPM_ITERATE_FILELIST_NOGHOSTS) != 0)
+ {
+ ff = headint32array(rpmhead, TAG_FILEFLAGS, &cnt2);
+ if (!ff || cnt != cnt2)
+ {
+ solv_free(ff);
+ solv_free(co);
+ solv_free(md);
+ solv_free(fm);
+ solv_free(di);
+ solv_free(bn);
+ solv_free(dn);
+ return;
+ }
+ }
+ lastdir = dcnt;
+ lastdirl = 0;
+ memset(&info, 0, sizeof(info));
+ for (i = 0; i < cnt; i++)
+ {
+ if (ff && (ff[i] & FILEFLAG_GHOST) != 0)
+ continue;
+ diidx = di[i];
+ if (diidx >= dcnt)
+ continue;
+ l1 = lastdir == diidx ? lastdirl : strlen(dn[diidx]);
+ l = l1 + strlen(bn[i]) + 1;
+ if (l > spacen)
+ {
+ spacen = l + 16;
+ space = solv_realloc(space, spacen);
+ }
+ if (lastdir != diidx)
+ {
+ strcpy(space, dn[diidx]);
+ lastdir = diidx;
+ lastdirl = l1;
+ }
+ strcpy(space + l1, bn[i]);
+ info.diridx = diidx;
+ info.dirlen = l1;
+ if (fm)
+ info.mode = fm[i];
+ if (md)
+ {
+ info.digest = md[i];
+ if (fm && S_ISLNK(fm[i]))
+ {
+ info.digest = 0;
+ if (!lt)
+ {
+ lt = headstringarray(rpmhead, TAG_FILELINKTOS, &cnt2);
+ if (cnt != cnt2)
+ lt = solv_free(lt);
+ }
+ if (lt)
+ {
+ linkhash(lt[i], md5);
+ info.digest = md5;
+ }
+ }
+ if (!info.digest)
+ {
+ sprintf(md5, "%08x%08x%08x%08x", (fm[i] >> 12) & 65535, 0, 0, 0);
+ info.digest = md5;
+ }
+ }
+ if (co)
+ info.color = co[i];
+ (*cb)(cbdata, space, &info);
+ }
+ solv_free(space);
+ solv_free(lt);
+ solv_free(md);
+ solv_free(fm);
+ solv_free(di);
+ solv_free(bn);
+ solv_free(dn);
+ solv_free(co);
+ solv_free(ff);
+}
+
+char *
+rpm_query(void *rpmhandle, Id what)
+{
+ const char *name, *arch, *sourcerpm;
+ char *evr, *r;
+ int l;
+
+ RpmHead *rpmhead = rpmhandle;
+ r = 0;
+ switch (what)
+ {
+ case 0:
+ name = headstring(rpmhead, TAG_NAME);
+ if (!name)
+ name = "";
+ sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
+ if (sourcerpm || (rpmhead->forcebinary && !headexists(rpmhead, TAG_SOURCEPACKAGE)))
+ arch = headstring(rpmhead, TAG_ARCH);
+ else
+ {
+ if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
+ arch = "nosrc";
+ else
+ arch = "src";
+ }
+ if (!arch)
+ arch = "noarch";
+ evr = headtoevr(rpmhead);
+ l = strlen(name) + 1 + strlen(evr ? evr : "") + 1 + strlen(arch) + 1;
+ r = solv_malloc(l);
+ sprintf(r, "%s-%s.%s", name, evr ? evr : "", arch);
+ solv_free(evr);
+ break;
+ case SOLVABLE_NAME:
+ name = headstring(rpmhead, TAG_NAME);
+ r = solv_strdup(name);
+ break;
+ case SOLVABLE_SUMMARY:
+ name = headstring(rpmhead, TAG_SUMMARY);
+ r = solv_strdup(name);
+ break;
+ case SOLVABLE_DESCRIPTION:
+ name = headstring(rpmhead, TAG_DESCRIPTION);
+ r = solv_strdup(name);
+ break;
+ case SOLVABLE_EVR:
+ r = headtoevr(rpmhead);
+ break;
+ }
+ return r;
+}
+
+unsigned long long
+rpm_query_num(void *rpmhandle, Id what, unsigned long long notfound)
+{
+ RpmHead *rpmhead = rpmhandle;
+ unsigned int u32;
+
+ switch (what)
+ {
+ case SOLVABLE_INSTALLTIME:
+ u32 = headint32(rpmhead, TAG_INSTALLTIME);
+ return u32 ? u32 : notfound;
+ }
+ return notfound;
+}
+
+int
+rpm_installedrpmdbids(void *rpmstate, const char *index, const char *match, Queue *rpmdbidq)
+{
+ struct rpmdbentry *entries;
+ int nentries, i;
+
+ entries = getinstalledrpmdbids(rpmstate, index ? index : "Name", match, &nentries, 0);
+ if (rpmdbidq)
+ {
+ queue_empty(rpmdbidq);
+ for (i = 0; i < nentries; i++)
+ queue_push(rpmdbidq, entries[i].rpmdbid);
+ }
+ solv_free(entries);
+ return nentries;
+}
+
+void *
+rpm_byrpmdbid(void *rpmstate, Id rpmdbid)
+{
+ struct rpmdbstate *state = rpmstate;
+ int r;
+
+ r = getrpmdbid(state, rpmdbid);
+ if (!r)
+ pool_error(state->pool, 0, "header #%d not in database", rpmdbid);
+ return r <= 0 ? 0 : state->rpmhead;
+}
+
+void *
+rpm_byfp(void *rpmstate, FILE *fp, const char *name)
+{
+ struct rpmdbstate *state = rpmstate;
+ /* int headerstart, headerend; */
+ RpmHead *rpmhead;
+ unsigned int sigdsize, sigcnt, l;
+ unsigned char lead[4096];
+ int forcebinary = 0;
+
+ if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
+ {
+ pool_error(state->pool, 0, "%s: not a rpm", name);
+ return 0;
+ }
+ forcebinary = lead[6] != 0 || lead[7] != 1;
+ if (lead[78] != 0 || lead[79] != 5)
+ {
+ pool_error(state->pool, 0, "%s: not a V5 header", name);
+ return 0;
+ }
+ if (getu32(lead + 96) != 0x8eade801)
+ {
+ pool_error(state->pool, 0, "%s: bad signature header", name);
+ return 0;
+ }
+ sigcnt = getu32(lead + 96 + 8);
+ sigdsize = getu32(lead + 96 + 12);
+ if (sigcnt >= 0x100000 || sigdsize >= 0x100000)
+ {
+ pool_error(state->pool, 0, "%s: bad signature header", name);
+ return 0;
+ }
+ sigdsize += sigcnt * 16;
+ sigdsize = (sigdsize + 7) & ~7;
+ /* headerstart = 96 + 16 + sigdsize; */
+ while (sigdsize)
+ {
+ l = sigdsize > 4096 ? 4096 : sigdsize;
+ if (fread(lead, l, 1, fp) != 1)
+ {
+ pool_error(state->pool, 0, "%s: unexpected EOF", name);
+ return 0;
+ }
+ sigdsize -= l;
+ }
+ if (fread(lead, 16, 1, fp) != 1)
+ {
+ pool_error(state->pool, 0, "%s: unexpected EOF", name);
+ return 0;
+ }
+ if (getu32(lead) != 0x8eade801)
+ {
+ pool_error(state->pool, 0, "%s: bad header", name);
+ return 0;
+ }
+ sigcnt = getu32(lead + 8);
+ sigdsize = getu32(lead + 12);
+ if (sigcnt >= 0x100000 || sigdsize >= 0x2000000)
+ {
+ pool_error(state->pool, 0, "%s: bad header", name);
+ return 0;
+ }
+ l = sigdsize + sigcnt * 16;
+ /* headerend = headerstart + 16 + l; */
+ if (l > state->rpmheadsize)
+ {
+ state->rpmheadsize = l + 128;
+ state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
+ }
+ rpmhead = state->rpmhead;
+ if (fread(rpmhead->data, l, 1, fp) != 1)
+ {
+ pool_error(state->pool, 0, "%s: unexpected EOF", name);
+ return 0;
+ }
+ rpmhead->forcebinary = forcebinary;
+ rpmhead->cnt = sigcnt;
+ rpmhead->dcnt = sigdsize;
+ rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+ return rpmhead;
+}
+
+#ifdef ENABLE_RPMDB_BYRPMHEADER
+
+void *
+rpm_byrpmh(void *rpmstate, Header h)
+{
+ struct rpmdbstate *state = rpmstate;
+ const unsigned char *uh;
+ unsigned int sigdsize, sigcnt, l;
+ RpmHead *rpmhead;
+
+#ifndef RPM5
+ uh = headerUnload(h);
+#else
+ uh = headerUnload(h, NULL);
+#endif
+ if (!uh)
+ return 0;
+ sigcnt = getu32(uh);
+ sigdsize = getu32(uh + 4);
+ l = sigdsize + sigcnt * 16;
+ if (l > state->rpmheadsize)
+ {
+ state->rpmheadsize = l + 128;
+ state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
+ }
+ rpmhead = state->rpmhead;
+ memcpy(rpmhead->data, uh + 8, l - 8);
+ free((void *)uh);
+ rpmhead->forcebinary = 0;
+ rpmhead->cnt = sigcnt;
+ rpmhead->dcnt = sigdsize;
+ rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+ return rpmhead;
+}
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include "queue.h"
+#include "repo.h"
+
+struct headerToken_s;
+
+extern int repo_add_rpmdb(Repo *repo, Repo *ref, int flags);
+extern int repo_add_rpmdb_reffp(Repo *repo, FILE *reffp, int flags);
+extern Id repo_add_rpm(Repo *repo, const char *rpm, int flags);
+
+#define RPMDB_REPORT_PROGRESS (1 << 8)
+#define RPM_ADD_WITH_PKGID (1 << 9)
+#define RPM_ADD_NO_FILELIST (1 << 10)
+#define RPM_ADD_NO_RPMLIBREQS (1 << 11)
+#define RPM_ADD_WITH_SHA1SUM (1 << 12)
+#define RPM_ADD_WITH_SHA256SUM (1 << 13)
+#define RPM_ADD_TRIGGERS (1 << 14)
+#define RPM_ADD_WITH_HDRID (1 << 15)
+#define RPM_ADD_WITH_LEADSIGID (1 << 16)
+#define RPM_ADD_WITH_CHANGELOG (1 << 17)
+#define RPM_ADD_FILTERED_FILELIST (1 << 18)
+
+#define RPMDB_EMPTY_REFREPO (1 << 30) /* internal */
+
+#define RPM_ITERATE_FILELIST_ONLYDIRS (1 << 0)
+#define RPM_ITERATE_FILELIST_WITHMD5 (1 << 1)
+#define RPM_ITERATE_FILELIST_WITHCOL (1 << 2)
+#define RPM_ITERATE_FILELIST_NOGHOSTS (1 << 3)
+
+/* create and free internal state, rootdir is the rootdir of the rpm database */
+extern void *rpm_state_create(Pool *pool, const char *rootdir);
+extern void *rpm_state_free(void *rpmstate);
+
+/* return all matching rpmdbids */
+extern int rpm_installedrpmdbids(void *rpmstate, const char *index, const char *match, Queue *rpmdbidq);
+
+/* return handles to a rpm header */
+extern void *rpm_byrpmdbid(void *rpmstate, Id rpmdbid);
+extern void *rpm_byfp(void *rpmstate, FILE *fp, const char *name);
+extern void *rpm_byrpmh(void *rpmstate, struct headerToken_s *h);
+
+/* operations on a rpm header handle */
+
+struct filelistinfo {
+ unsigned int dirlen;
+ unsigned int diridx;
+ const char *digest;
+ unsigned int mode;
+ unsigned int color;
+};
+
+extern char *rpm_query(void *rpmhandle, Id what);
+extern unsigned long long rpm_query_num(void *rpmhandle, Id what, unsigned long long notfound);
+extern void rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, const char *, struct filelistinfo *), void *cbdata);
+extern Id repo_add_rpm_handle(Repo *repo, void *rpmhandle, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "repo.h"
+#define DISABLE_SPLIT
+#include "tools_util.h"
+#include "repo_rpmmd.h"
+#include "chksum.h"
+#ifdef ENABLE_COMPLEX_DEPS
+#include "pool_parserpmrichdep.h"
+#endif
+
+enum state {
+ STATE_START,
+
+ STATE_SOLVABLE,
+
+ STATE_NAME,
+ STATE_ARCH,
+ STATE_VERSION,
+
+ /* package rpm-md */
+ STATE_LOCATION,
+ STATE_CHECKSUM,
+ STATE_RPM_GROUP,
+ STATE_RPM_LICENSE,
+
+ /* resobject attributes */
+ STATE_SUMMARY,
+ STATE_DESCRIPTION,
+ STATE_DISTRIBUTION,
+ STATE_PACKAGER,
+ STATE_URL,
+ STATE_INSNOTIFY,
+ STATE_DELNOTIFY,
+ STATE_VENDOR,
+ STATE_SIZE,
+ STATE_TIME,
+ STATE_DOWNLOADSIZE,
+ STATE_INSTALLTIME,
+ STATE_INSTALLONLY,
+
+ /* Novell/SUSE extended attributes */
+ STATE_EULA,
+ STATE_KEYWORD,
+ STATE_DISKUSAGE,
+ STATE_DIRS,
+ STATE_DIR,
+
+ /* patch */
+ STATE_ID,
+ STATE_TIMESTAMP,
+ STATE_AFFECTSPKG,
+ STATE_REBOOTNEEDED,
+
+ /* pattern attributes */
+ STATE_CATEGORY, /* pattern and patches */
+ STATE_ORDER,
+ STATE_INCLUDES,
+ STATE_INCLUDESENTRY,
+ STATE_EXTENDS,
+ STATE_EXTENDSENTRY,
+ STATE_SCRIPT,
+ STATE_ICON,
+ STATE_USERVISIBLE,
+ STATE_DEFAULT,
+ STATE_INSTALL_TIME,
+
+ /* product */
+ STATE_RELNOTESURL,
+ STATE_UPDATEURL,
+ STATE_OPTIONALURL,
+ STATE_FLAG,
+
+ /* rpm-md dependencies inside the
+ format tag */
+ STATE_PROVIDES,
+ STATE_REQUIRES,
+ STATE_OBSOLETES,
+ STATE_CONFLICTS,
+ STATE_RECOMMENDS,
+ STATE_SUPPLEMENTS,
+ STATE_SUGGESTS,
+ STATE_ENHANCES,
+ STATE_FRESHENS,
+ STATE_SOURCERPM,
+ STATE_HEADERRANGE,
+
+ STATE_PROVIDESENTRY,
+ STATE_REQUIRESENTRY,
+ STATE_OBSOLETESENTRY,
+ STATE_CONFLICTSENTRY,
+ STATE_RECOMMENDSENTRY,
+ STATE_SUPPLEMENTSENTRY,
+ STATE_SUGGESTSENTRY,
+ STATE_ENHANCESENTRY,
+ STATE_FRESHENSENTRY,
+
+ STATE_FILE,
+
+ STATE_CHANGELOG,
+
+ /* general */
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+static struct stateswitch stateswitches[] = {
+ /** fake tag used to enclose 2 different xml files in one **/
+ { STATE_START, "rpmmd", STATE_START, 0 },
+
+ /** tags for different package data, we just ignore the tag **/
+ { STATE_START, "metadata", STATE_START, 0 },
+ { STATE_START, "otherdata", STATE_START, 0 },
+ { STATE_START, "filelists", STATE_START, 0 },
+ { STATE_START, "diskusagedata", STATE_START, 0 },
+ { STATE_START, "susedata", STATE_START, 0 },
+
+ { STATE_START, "product", STATE_SOLVABLE, 0 },
+ { STATE_START, "pattern", STATE_SOLVABLE, 0 },
+ { STATE_START, "patch", STATE_SOLVABLE, 0 },
+ { STATE_START, "package", STATE_SOLVABLE, 0 },
+
+ { STATE_SOLVABLE, "name", STATE_NAME, 1 },
+ { STATE_SOLVABLE, "arch", STATE_ARCH, 1 },
+ { STATE_SOLVABLE, "version", STATE_VERSION, 0 },
+
+ /* package attributes rpm-md */
+ { STATE_SOLVABLE, "location", STATE_LOCATION, 0 },
+ { STATE_SOLVABLE, "checksum", STATE_CHECKSUM, 1 },
+
+ /* resobject attributes */
+
+ { STATE_SOLVABLE, "summary", STATE_SUMMARY, 1 },
+ { STATE_SOLVABLE, "description", STATE_DESCRIPTION, 1 },
+ { STATE_SOLVABLE, "distribution", STATE_DISTRIBUTION, 1 },
+ { STATE_SOLVABLE, "url", STATE_URL, 1 },
+ { STATE_SOLVABLE, "packager", STATE_PACKAGER, 1 },
+ { STATE_SOLVABLE, "vendor", STATE_VENDOR, 1 },
+ { STATE_SOLVABLE, "size", STATE_SIZE, 0 },
+ { STATE_SOLVABLE, "archive-size", STATE_DOWNLOADSIZE, 1 },
+ { STATE_SOLVABLE, "install-time", STATE_INSTALLTIME, 1 },
+ { STATE_SOLVABLE, "install-only", STATE_INSTALLONLY, 1 },
+ { STATE_SOLVABLE, "time", STATE_TIME, 0 },
+
+ /* extended Novell/SUSE attributes (susedata.xml) */
+ { STATE_SOLVABLE, "eula", STATE_EULA, 1 },
+ { STATE_SOLVABLE, "keyword", STATE_KEYWORD, 1 },
+ { STATE_SOLVABLE, "diskusage", STATE_DISKUSAGE, 0 },
+
+ /* pattern attribute */
+ { STATE_SOLVABLE, "script", STATE_SCRIPT, 1 },
+ { STATE_SOLVABLE, "icon", STATE_ICON, 1 },
+ { STATE_SOLVABLE, "uservisible", STATE_USERVISIBLE, 1 },
+ { STATE_SOLVABLE, "category", STATE_CATEGORY, 1 },
+ { STATE_SOLVABLE, "order", STATE_ORDER, 1 },
+ { STATE_SOLVABLE, "includes", STATE_INCLUDES, 0 },
+ { STATE_SOLVABLE, "extends", STATE_EXTENDS, 0 },
+ { STATE_SOLVABLE, "default", STATE_DEFAULT, 1 },
+ { STATE_SOLVABLE, "install-time", STATE_INSTALL_TIME, 1 },
+
+ /* product attributes */
+ /* note the product type is an attribute */
+ { STATE_SOLVABLE, "release-notes-url", STATE_RELNOTESURL, 1 },
+ { STATE_SOLVABLE, "update-url", STATE_UPDATEURL, 1 },
+ { STATE_SOLVABLE, "optional-url", STATE_OPTIONALURL, 1 },
+ { STATE_SOLVABLE, "flag", STATE_FLAG, 1 },
+
+ { STATE_SOLVABLE, "rpm:vendor", STATE_VENDOR, 1 },
+ { STATE_SOLVABLE, "rpm:group", STATE_RPM_GROUP, 1 },
+ { STATE_SOLVABLE, "rpm:license", STATE_RPM_LICENSE, 1 },
+
+ /* rpm-md dependencies */
+ { STATE_SOLVABLE, "rpm:provides", STATE_PROVIDES, 0 },
+ { STATE_SOLVABLE, "rpm:requires", STATE_REQUIRES, 0 },
+ { STATE_SOLVABLE, "rpm:obsoletes", STATE_OBSOLETES, 0 },
+ { STATE_SOLVABLE, "rpm:conflicts", STATE_CONFLICTS, 0 },
+ { STATE_SOLVABLE, "rpm:recommends", STATE_RECOMMENDS , 0 },
+ { STATE_SOLVABLE, "rpm:supplements", STATE_SUPPLEMENTS, 0 },
+ { STATE_SOLVABLE, "rpm:suggests", STATE_SUGGESTS, 0 },
+ { STATE_SOLVABLE, "rpm:enhances", STATE_ENHANCES, 0 },
+ { STATE_SOLVABLE, "rpm:freshens", STATE_FRESHENS, 0 },
+ { STATE_SOLVABLE, "rpm:sourcerpm", STATE_SOURCERPM, 1 },
+ { STATE_SOLVABLE, "rpm:header-range", STATE_HEADERRANGE, 0 },
+ { STATE_SOLVABLE, "file", STATE_FILE, 1 },
+ { STATE_SOLVABLE, "changelog", STATE_CHANGELOG, 1 },
+
+ /* extended Novell/SUSE diskusage attributes (susedata.xml) */
+ { STATE_DISKUSAGE, "dirs", STATE_DIRS, 0 },
+ { STATE_DIRS, "dir", STATE_DIR, 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_INCLUDES, "item", STATE_INCLUDESENTRY, 0 },
+ { STATE_EXTENDS, "item", STATE_EXTENDSENTRY, 0 },
+
+ { NUMSTATES}
+};
+
+struct parsedata {
+ int ret;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+ char *kind;
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ Solvable *solvable;
+ Offset freshens;
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+ struct joindata jd;
+ /* temporal to store attribute tag language */
+ const char *tmplang;
+ Id chksumtype;
+ Id handle;
+ XML_Parser *parser;
+ Id (*dirs)[3]; /* dirid, size, nfiles */
+ int ndirs;
+ const char *language; /* default language */
+ Id langcache[ID_NUM_INTERNAL]; /* cache for the default language */
+
+ Id lastdir;
+ char *lastdirstr;
+ int lastdirstrl;
+
+ Id changelog_handle;
+
+ /** Hash to maps checksums to solv */
+ Stringpool cspool;
+ /** Cache of known checksums to solvable id */
+ Id *cscache;
+ /* the current longest index in the table */
+ int ncscache;
+};
+
+static Id
+langtag(struct parsedata *pd, Id tag, const char *language)
+{
+ if (language)
+ {
+ if (!language[0] || !strcmp(language, "en"))
+ return tag;
+ return pool_id2langid(pd->pool, tag, language, 1);
+ }
+ if (!pd->language)
+ return tag;
+ if (tag >= ID_NUM_INTERNAL)
+ return pool_id2langid(pd->pool, tag, pd->language, 1);
+ if (!pd->langcache[tag])
+ pd->langcache[tag] = pool_id2langid(pd->pool, tag, pd->language, 1);
+ return pd->langcache[tag];
+}
+
+static int
+id3_cmp (const void *v1, const void *v2, void *dp)
+{
+ Id *i1 = (Id*)v1;
+ Id *i2 = (Id*)v2;
+ return i1[0] - i2[0];
+}
+
+static void
+commit_diskusage (struct parsedata *pd, Id handle)
+{
+ int i;
+ Dirpool *dp = &pd->data->dirpool;
+ /* Now sort in dirid order. This ensures that parents come before
+ their children. */
+ if (pd->ndirs > 1)
+ solv_sort(pd->dirs, pd->ndirs, sizeof (pd->dirs[0]), id3_cmp, 0);
+ /* Substract leaf numbers from all parents to make the numbers
+ non-cumulative. This must be done post-order (i.e. all leafs
+ adjusted before parents). We ensure this by starting at the end of
+ the array moving to the start, hence seeing leafs before parents. */
+ for (i = pd->ndirs; i--;)
+ {
+ Id p = dirpool_parent(dp, pd->dirs[i][0]);
+ int j = i;
+ for (; p; p = dirpool_parent(dp, p))
+ {
+ for (; j--;)
+ if (pd->dirs[j][0] == p)
+ break;
+ if (j >= 0)
+ {
+ if (pd->dirs[j][1] < pd->dirs[i][1])
+ pd->dirs[j][1] = 0;
+ else
+ pd->dirs[j][1] -= pd->dirs[i][1];
+ if (pd->dirs[j][2] < pd->dirs[i][2])
+ pd->dirs[j][2] = 0;
+ else
+ pd->dirs[j][2] -= pd->dirs[i][2];
+ }
+ else
+ /* Haven't found this parent in the list, look further if
+ we maybe find the parents parent. */
+ j = i;
+ }
+ }
+#if 0
+ char sbuf[1024];
+ char *buf = sbuf;
+ unsigned slen = sizeof (sbuf);
+ for (i = 0; i < pd->ndirs; i++)
+ {
+ dir2str (attr, pd->dirs[i][0], &buf, &slen);
+ fprintf (stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
+ }
+ if (buf != sbuf)
+ free (buf);
+#endif
+ for (i = 0; i < pd->ndirs; i++)
+ if (pd->dirs[i][1] || pd->dirs[i][2])
+ {
+ repodata_add_dirnumnum(pd->data, handle, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
+ }
+ pd->ndirs = 0;
+}
+
+
+/*
+ * makeevr_atts
+ * parse 'epoch', 'ver' and 'rel', return evr Id
+ *
+ */
+
+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 && (!*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 = solv_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 pool_str2id(pool, pd->content, 1);
+}
+
+
+/*
+ * find_attr
+ * find value for xml attribute
+ * I: txt, name of attribute
+ * I: atts, list of key/value attributes
+ * O: pointer to value of matching key, or NULL
+ *
+ */
+
+static inline const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ }
+ return 0;
+}
+
+
+/*
+ * dependency relations
+ */
+
+static char *flagtab[] = {
+ "GT",
+ "EQ",
+ "GE",
+ "LT",
+ "NE",
+ "LE"
+};
+
+
+/*
+ * adddep
+ * parse attributes to reldep Id
+ *
+ */
+
+static unsigned int
+adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
+{
+ Id id, marker;
+ const char *n, *f, *k;
+ const char **a;
+
+ n = f = k = 0;
+ marker = isreq ? -SOLVABLE_PREREQMARKER : 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')
+ marker = SOLVABLE_PREREQMARKER;
+ }
+ 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 = solv_realloc(pd->content, l + 256);
+ pd->acontent = l + 256;
+ }
+ sprintf(pd->content, "%s:%s", k, n);
+ id = pool_str2id(pool, pd->content, 1);
+ }
+#ifdef ENABLE_COMPLEX_DEPS
+ else if (!f && n[0] == '(')
+ {
+ id = pool_parserpmrichdep(pool, n);
+ if (!id)
+ return olddeps;
+ }
+#endif
+ else
+ id = pool_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 = pool_rel2id(pool, id, evr, flags, 1);
+ }
+#if 0
+ fprintf(stderr, "new dep %s\n", pool_dep2str(pool, id));
+#endif
+ return repo_addid_dep(pd->repo, olddeps, id, marker);
+}
+
+
+/*
+ * set_description_author
+ *
+ */
+static void
+set_description_author(Repodata *data, Id handle, char *str, struct parsedata *pd)
+{
+ char *aut, *p;
+
+ if (!str || !*str)
+ return;
+ for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
+ if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
+ break;
+ if (aut)
+ {
+ /* oh my, found SUSE special author section */
+ int l = aut - str;
+ str[l] = 0;
+ while (l > 0 && str[l - 1] == '\n')
+ str[--l] = 0;
+ if (l)
+ repodata_set_str(data, handle, langtag(pd, SOLVABLE_DESCRIPTION, pd->tmplang), str);
+ p = aut + 19;
+ aut = str; /* copy over */
+ while (*p == ' ' || *p == '\n')
+ p++;
+ while (*p)
+ {
+ if (*p == '\n')
+ {
+ *aut++ = *p++;
+ while (*p == ' ')
+ p++;
+ continue;
+ }
+ *aut++ = *p++;
+ }
+ while (aut != str && aut[-1] == '\n')
+ aut--;
+ *aut = 0;
+ if (*str)
+ repodata_set_str(data, handle, SOLVABLE_AUTHORS, str);
+ }
+ else if (*str)
+ repodata_set_str(data, handle, langtag(pd, SOLVABLE_DESCRIPTION, pd->tmplang), str);
+}
+
+
+/*-----------------------------------------------*/
+/* XML callbacks */
+
+/*
+ * startElement
+ * XML callback
+ *
+ */
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ struct stateswitch *sw;
+ const char *str;
+ Id handle = pd->handle;
+ const char *pkgid;
+
+ /* fprintf(stderr, "into %s, from %d, depth %d, statedepth %d\n", name, pd->state, pd->depth, pd->statedepth); */
+
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ if (pd->state == STATE_START && !strcmp(name, "patterns"))
+ return;
+ if (pd->state == STATE_START && !strcmp(name, "products"))
+ return;
+#if 0
+ if (pd->state == STATE_START && !strcmp(name, "metadata"))
+ return;
+#endif
+ if (pd->state == STATE_SOLVABLE && !strcmp(name, "format"))
+ return;
+
+ pd->depth++;
+ if (!pd->swtab[pd->state])
+ return;
+ 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;
+
+ if (!s && pd->state != STATE_SOLVABLE)
+ return;
+
+ switch(pd->state)
+ {
+ case STATE_SOLVABLE:
+ pd->kind = 0;
+ if (name[2] == 't' && name[3] == 't')
+ pd->kind = "pattern";
+ else if (name[1] == 'r')
+ pd->kind = "product";
+ else if (name[2] == 't' && name[3] == 'c')
+ pd->kind = "patch";
+
+ /* to support extension metadata files like others.xml which
+ have the following structure:
+
+ <otherdata xmlns="http://linux.duke.edu/metadata/other"
+ packages="101">
+ <package pkgid="b78f8664cd90efe42e09a345e272997ef1b53c18"
+ name="zaptel-kmp-default"
+ arch="i586"><version epoch="0"
+ ver="1.2.10_2.6.22_rc4_git6_2" rel="70"/>
+ ...
+
+ we need to check if the pkgid is there and if it matches
+ an already seen package, that means we don't need to create
+ a new solvable but just append the attributes to the existing
+ one.
+ */
+ if ((pkgid = find_attr("pkgid", atts)) != NULL)
+ {
+ /* look at the checksum cache */
+ Id index = stringpool_str2id(&pd->cspool, pkgid, 0);
+ if (!index || index >= pd->ncscache || !pd->cscache[index])
+ {
+ pool_debug(pool, SOLV_WARN, "the repository specifies extra information about package with checksum '%s', which does not exist in the repository.\n", pkgid);
+ pd->solvable = 0;
+ pd->handle = 0;
+ break;
+ }
+ pd->solvable = pool_id2solvable(pool, pd->cscache[index]);
+ }
+ else
+ {
+ /* this is a new package */
+ pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+ pd->freshens = 0;
+ }
+ pd->handle = handle = pd->solvable - pool->solvables;
+ if (pd->kind && pd->kind[1] == 'r')
+ {
+ /* products can have a type */
+ const char *type = find_attr("type", atts);
+ if (type && *type)
+ repodata_set_str(pd->data, handle, PRODUCT_TYPE, type);
+ }
+#if 0
+ fprintf(stderr, "package #%d\n", pd->solvable - pool->solvables);
+#endif
+
+ break;
+ case STATE_VERSION:
+ s->evr = makeevr_atts(pool, pd, atts);
+ break;
+ case STATE_PROVIDES:
+ s->provides = 0;
+ break;
+ case STATE_PROVIDESENTRY:
+ s->provides = adddep(pool, pd, s->provides, atts, 0);
+ break;
+ case STATE_REQUIRES:
+ s->requires = 0;
+ break;
+ case STATE_REQUIRESENTRY:
+ s->requires = adddep(pool, pd, s->requires, atts, 1);
+ break;
+ case STATE_OBSOLETES:
+ s->obsoletes = 0;
+ break;
+ case STATE_OBSOLETESENTRY:
+ s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
+ break;
+ case STATE_CONFLICTS:
+ s->conflicts = 0;
+ break;
+ case STATE_CONFLICTSENTRY:
+ s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
+ break;
+ case STATE_RECOMMENDS:
+ s->recommends = 0;
+ break;
+ case STATE_RECOMMENDSENTRY:
+ s->recommends = adddep(pool, pd, s->recommends, atts, 0);
+ break;
+ case STATE_SUPPLEMENTS:
+ s->supplements= 0;
+ break;
+ case STATE_SUPPLEMENTSENTRY:
+ s->supplements = adddep(pool, pd, s->supplements, atts, 0);
+ break;
+ case STATE_SUGGESTS:
+ s->suggests = 0;
+ break;
+ case STATE_SUGGESTSENTRY:
+ s->suggests = adddep(pool, pd, s->suggests, atts, 0);
+ break;
+ case STATE_ENHANCES:
+ s->enhances = 0;
+ break;
+ case STATE_ENHANCESENTRY:
+ s->enhances = adddep(pool, pd, s->enhances, atts, 0);
+ break;
+ case STATE_FRESHENS:
+ pd->freshens = 0;
+ break;
+ case STATE_FRESHENSENTRY:
+ pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
+ break;
+ case STATE_EULA:
+ case STATE_SUMMARY:
+ case STATE_CATEGORY:
+ case STATE_DESCRIPTION:
+ pd->tmplang = join_dup(&pd->jd, find_attr("lang", atts));
+ break;
+ case STATE_USERVISIBLE:
+ repodata_set_void(pd->data, handle, SOLVABLE_ISVISIBLE);
+ break;
+ case STATE_INCLUDESENTRY:
+ str = find_attr("pattern", atts);
+ if (str)
+ repodata_add_poolstr_array(pd->data, handle, SOLVABLE_INCLUDES, join2(&pd->jd, "pattern", ":", str));
+ break;
+ case STATE_EXTENDSENTRY:
+ str = find_attr("pattern", atts);
+ if (str)
+ repodata_add_poolstr_array(pd->data, handle, SOLVABLE_EXTENDS, join2(&pd->jd, "pattern", ":", str));
+ break;
+ case STATE_LOCATION:
+ str = find_attr("href", atts);
+ if (str)
+ {
+ repodata_set_location(pd->data, handle, 0, 0, str);
+ str = find_attr("xml:base", atts);
+ if (str)
+ repodata_set_poolstr(pd->data, handle, SOLVABLE_MEDIABASE, str);
+ }
+ break;
+ case STATE_CHECKSUM:
+ str = find_attr("type", atts);
+ pd->chksumtype = str && *str ? solv_chksum_str2type(str) : 0;
+ if (!pd->chksumtype)
+ pd->ret = pool_error(pool, -1, "line %d: unknown checksum type: %s", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), str ? str : "NULL");
+ break;
+ case STATE_TIME:
+ {
+ unsigned int t;
+ str = find_attr("build", atts);
+ if (str && (t = atoi(str)) != 0)
+ repodata_set_num(pd->data, handle, SOLVABLE_BUILDTIME, t);
+ break;
+ }
+ case STATE_SIZE:
+ if ((str = find_attr("installed", atts)) != 0)
+ repodata_set_num(pd->data, handle, SOLVABLE_INSTALLSIZE, strtoull(str, 0, 10));
+ if ((str = find_attr("package", atts)) != 0)
+ repodata_set_num(pd->data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(str, 0, 10));
+ break;
+ case STATE_HEADERRANGE:
+ {
+ unsigned int end;
+ str = find_attr("end", atts);
+ if (str && (end = atoi(str)) != 0)
+ repodata_set_num(pd->data, handle, SOLVABLE_HEADEREND, end);
+ break;
+ }
+ /*
+ <diskusage>
+ <dirs>
+ <dir name="/" size="56" count="11"/>
+ <dir name="usr/" size="56" count="11"/>
+ <dir name="usr/bin/" size="38" count="10"/>
+ <dir name="usr/share/" size="18" count="1"/>
+ <dir name="usr/share/doc/" size="18" count="1"/>
+ </dirs>
+ </diskusage>
+ */
+ case STATE_DISKUSAGE:
+ {
+ /* Really, do nothing, wait for <dir> tag */
+ break;
+ }
+ case STATE_DIR:
+ {
+ long filesz = 0, filenum = 0;
+ Id dirid;
+ if ((str = find_attr("name", atts)) != 0)
+ dirid = repodata_str2dir(pd->data, str, 1);
+ else
+ {
+ pd->ret = pool_error(pool, -1, "<dir .../> tag without 'name' attribute");
+ break;
+ }
+ if (!dirid)
+ dirid = repodata_str2dir(pd->data, "/", 1);
+ if ((str = find_attr("size", atts)) != 0)
+ filesz = strtol(str, 0, 0);
+ if ((str = find_attr("count", atts)) != 0)
+ filenum = strtol(str, 0, 0);
+ pd->dirs = solv_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
+ pd->dirs[pd->ndirs][0] = dirid;
+ pd->dirs[pd->ndirs][1] = filesz;
+ pd->dirs[pd->ndirs][2] = filenum;
+ pd->ndirs++;
+ break;
+ }
+ case STATE_CHANGELOG:
+ pd->changelog_handle = repodata_new_handle(pd->data);
+ if ((str = find_attr("date", atts)) != 0)
+ repodata_set_num(pd->data, pd->changelog_handle, SOLVABLE_CHANGELOG_TIME, strtoull(str, 0, 10));
+ if ((str = find_attr("author", atts)) != 0)
+ repodata_set_str(pd->data, pd->changelog_handle, SOLVABLE_CHANGELOG_AUTHOR, str);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+ * endElement
+ * XML callback
+ *
+ */
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ Repo *repo = pd->repo;
+ Id handle = pd->handle;
+ Id id;
+ char *p;
+
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+ /* printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth); */
+ return;
+ }
+
+ /* ignore patterns & metadata */
+ if (pd->state == STATE_START && !strcmp(name, "patterns"))
+ return;
+ if (pd->state == STATE_START && !strcmp(name, "products"))
+ return;
+#if 0
+ if (pd->state == STATE_START && !strcmp(name, "metadata"))
+ return;
+#endif
+ if (pd->state == STATE_SOLVABLE && !strcmp(name, "format"))
+ return;
+
+ pd->depth--;
+ pd->statedepth--;
+
+
+ if (!s)
+ {
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+ return;
+ }
+
+ switch (pd->state)
+ {
+ case STATE_SOLVABLE:
+ if (pd->kind && !s->name) /* add namespace in case of NULL name */
+ s->name = pool_str2id(pool, join2(&pd->jd, pd->kind, ":", 0), 1);
+ if (!s->arch)
+ s->arch = ARCH_NOARCH;
+ if (!s->evr)
+ s->evr = ID_EMPTY; /* some patterns have this */
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, pd->freshens);
+ s->conflicts = repo_fix_conflicts(repo, s->conflicts);
+ pd->freshens = 0;
+ pd->kind = 0;
+ pd->solvable = s = 0;
+ break;
+ case STATE_NAME:
+ if (pd->kind)
+ s->name = pool_str2id(pool, join2(&pd->jd, pd->kind, ":", pd->content), 1);
+ else
+ s->name = pool_str2id(pool, pd->content, 1);
+ break;
+ case STATE_ARCH:
+ s->arch = pool_str2id(pool, pd->content, 1);
+ break;
+ case STATE_VENDOR:
+ s->vendor = pool_str2id(pool, pd->content, 1);
+ break;
+ case STATE_RPM_GROUP:
+ repodata_set_poolstr(pd->data, handle, SOLVABLE_GROUP, pd->content);
+ break;
+ case STATE_RPM_LICENSE:
+ repodata_set_poolstr(pd->data, handle, SOLVABLE_LICENSE, pd->content);
+ break;
+ case STATE_CHECKSUM:
+ {
+ Id index;
+
+ if (!pd->chksumtype)
+ break;
+ if (strlen(pd->content) != 2 * solv_chksum_len(pd->chksumtype))
+ {
+ pd->ret = pool_error(pool, -1, "line %d: invalid checksum length for %s", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), solv_chksum_type2str(pd->chksumtype));
+ break;
+ }
+ repodata_set_checksum(pd->data, handle, SOLVABLE_CHECKSUM, pd->chksumtype, pd->content);
+ /* we save the checksum to solvable id relationship for extended
+ metadata */
+ index = stringpool_str2id(&pd->cspool, pd->content, 1 /* create it */);
+ if (index >= pd->ncscache)
+ {
+ pd->cscache = solv_zextend(pd->cscache, pd->ncscache, index + 1 - pd->ncscache, sizeof(Id), 255);
+ pd->ncscache = index + 1;
+ }
+ /* add the checksum to the cache */
+ pd->cscache[index] = s - pool->solvables;
+ break;
+ }
+ case STATE_FILE:
+#if 0
+ id = pool_str2id(pool, pd->content, 1);
+ s->provides = repo_addid_dep(repo, s->provides, id, SOLVABLE_FILEMARKER);
+#endif
+ if ((p = strrchr(pd->content, '/')) != 0)
+ {
+ *p++ = 0;
+ if (pd->lastdir && !strcmp(pd->lastdirstr, pd->content))
+ {
+ id = pd->lastdir;
+ }
+ else
+ {
+ int l;
+ id = repodata_str2dir(pd->data, pd->content, 1);
+ l = strlen(pd->content) + 1;
+ if (l > pd->lastdirstrl)
+ {
+ pd->lastdirstrl = l + 128;
+ pd->lastdirstr = solv_realloc(pd->lastdirstr, pd->lastdirstrl);
+ }
+ strcpy(pd->lastdirstr, pd->content);
+ pd->lastdir = id;
+ }
+ }
+ else
+ {
+ p = pd->content;
+ id = 0;
+ }
+ if (!id)
+ id = repodata_str2dir(pd->data, "/", 1);
+ repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, id, p);
+ break;
+ case STATE_SUMMARY:
+ repodata_set_str(pd->data, handle, langtag(pd, SOLVABLE_SUMMARY, pd->tmplang), pd->content);
+ break;
+ case STATE_DESCRIPTION:
+ set_description_author(pd->data, handle, pd->content, pd);
+ break;
+ case STATE_CATEGORY:
+ repodata_set_str(pd->data, handle, langtag(pd, SOLVABLE_CATEGORY, pd->tmplang), pd->content);
+ break;
+ case STATE_DISTRIBUTION:
+ repodata_set_poolstr(pd->data, handle, SOLVABLE_DISTRIBUTION, pd->content);
+ break;
+ case STATE_URL:
+ if (pd->content[0])
+ repodata_set_str(pd->data, handle, SOLVABLE_URL, pd->content);
+ break;
+ case STATE_PACKAGER:
+ if (pd->content[0])
+ repodata_set_poolstr(pd->data, handle, SOLVABLE_PACKAGER, pd->content);
+ break;
+ case STATE_SOURCERPM:
+ if (pd->content[0])
+ repodata_set_sourcepkg(pd->data, handle, pd->content);
+ break;
+ case STATE_RELNOTESURL:
+ if (pd->content[0])
+ {
+ repodata_add_poolstr_array(pd->data, handle, PRODUCT_URL, pd->content);
+ repodata_add_idarray(pd->data, handle, PRODUCT_URL_TYPE, pool_str2id(pool, "releasenotes", 1));
+ }
+ break;
+ case STATE_UPDATEURL:
+ if (pd->content[0])
+ {
+ repodata_add_poolstr_array(pd->data, handle, PRODUCT_URL, pd->content);
+ repodata_add_idarray(pd->data, handle, PRODUCT_URL_TYPE, pool_str2id(pool, "update", 1));
+ }
+ break;
+ case STATE_OPTIONALURL:
+ if (pd->content[0])
+ {
+ repodata_add_poolstr_array(pd->data, handle, PRODUCT_URL, pd->content);
+ repodata_add_idarray(pd->data, handle, PRODUCT_URL_TYPE, pool_str2id(pool, "optional", 1));
+ }
+ break;
+ case STATE_FLAG:
+ if (pd->content[0])
+ repodata_add_poolstr_array(pd->data, handle, PRODUCT_FLAGS, pd->content);
+ break;
+ case STATE_EULA:
+ if (pd->content[0])
+ repodata_set_str(pd->data, handle, langtag(pd, SOLVABLE_EULA, pd->tmplang), pd->content);
+ break;
+ case STATE_KEYWORD:
+ if (pd->content[0])
+ repodata_add_poolstr_array(pd->data, handle, SOLVABLE_KEYWORDS, pd->content);
+ break;
+ case STATE_DISKUSAGE:
+ if (pd->ndirs)
+ commit_diskusage(pd, handle);
+ break;
+ case STATE_ORDER:
+ if (pd->content[0])
+ repodata_set_str(pd->data, handle, SOLVABLE_ORDER, pd->content);
+ break;
+ case STATE_CHANGELOG:
+ repodata_set_str(pd->data, pd->changelog_handle, SOLVABLE_CHANGELOG_TEXT, pd->content);
+ repodata_add_flexarray(pd->data, handle, SOLVABLE_CHANGELOG, pd->changelog_handle);
+ pd->changelog_handle = 0;
+ break;
+ default:
+ break;
+ }
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+ /* fprintf(stderr, "back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth); */
+}
+
+
+/*
+ * characterData
+ * XML callback
+ *
+ */
+
+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 = solv_realloc(pd->content, l + 256);
+ pd->acontent = l + 256;
+ }
+ c = pd->content + pd->lcontent;
+ pd->lcontent += len;
+ while (len-- > 0)
+ *c++ = *s++;
+ *c = 0;
+}
+
+
+/*-----------------------------------------------*/
+/* 'main' */
+
+#define BUFF_SIZE 8192
+
+/*
+ * repo_add_rpmmd
+ * parse rpm-md metadata (primary, others)
+ *
+ */
+
+int
+repo_add_rpmmd(Repo *repo, FILE *fp, const char *language, int flags)
+{
+ Pool *pool = repo->pool;
+ struct parsedata pd;
+ char buf[BUFF_SIZE];
+ int i, l;
+ struct stateswitch *sw;
+ Repodata *data;
+ unsigned int now;
+ XML_Parser parser;
+
+ now = solv_timems(0);
+ data = repo_add_repodata(repo, flags);
+
+ 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.repo = repo;
+ pd.data = data;
+
+ pd.content = solv_malloc(256);
+ pd.acontent = 256;
+ pd.lcontent = 0;
+ pd.kind = 0;
+ pd.language = language && *language && strcmp(language, "en") != 0 ? language : 0;
+
+ /* initialize the string pool where we will store
+ the package checksums we know about, to get an Id
+ we can use in a cache */
+ stringpool_init_empty(&pd.cspool);
+ if ((flags & REPO_EXTEND_SOLVABLES) != 0)
+ {
+ /* setup join data */
+ Dataiterator di;
+ dataiterator_init(&di, pool, repo, 0, SOLVABLE_CHECKSUM, 0, 0);
+ while (dataiterator_step(&di))
+ {
+ const char *str;
+ int index;
+
+ if (!solv_chksum_len(di.key->type))
+ continue;
+ str = repodata_chk2str(di.data, di.key->type, (const unsigned char *)di.kv.str);
+ index = stringpool_str2id(&pd.cspool, str, 1);
+ if (index >= pd.ncscache)
+ {
+ pd.cscache = solv_zextend(pd.cscache, pd.ncscache, index + 1 - pd.ncscache, sizeof(Id), 255);
+ pd.ncscache = index + 1;
+ }
+ pd.cscache[index] = di.solvid;
+ }
+ dataiterator_free(&di);
+ }
+
+ parser = XML_ParserCreate(NULL);
+ XML_SetUserData(parser, &pd);
+ pd.parser = &parser;
+ 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)
+ {
+ pd.ret = pool_error(pool, -1, "repo_rpmmd: %s at line %u:%u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ break;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+ solv_free(pd.content);
+ solv_free(pd.lastdirstr);
+ join_freemem(&pd.jd);
+ stringpool_free(&pd.cspool);
+ solv_free(pd.cscache);
+ repodata_free_dircache(data);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_rpmmd took %d ms\n", solv_timems(now));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
+ return pd.ret;
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_rpmmd(Repo *repo, FILE *fp, const char *language, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "hash.h"
+#include "chksum.h"
+#include "tools_util.h"
+#include "repo_susetags.h"
+#ifdef ENABLE_COMPLEX_DEPS
+#include "pool_parserpmrichdep.h"
+#endif
+
+struct datashare {
+ Id name;
+ Id evr;
+ Id arch;
+};
+
+struct parsedata {
+ int ret;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+ char *kind;
+ int flags;
+ int last_found_source;
+ struct datashare *share_with;
+ int nshare;
+ Id (*dirs)[3]; /* dirid, size, nfiles */
+ int ndirs;
+ struct joindata jd;
+ char *language; /* the default language */
+ Id langcache[ID_NUM_INTERNAL]; /* cache for the default language */
+ int lineno;
+ char *filelist;
+ int afilelist; /* allocated */
+ int nfilelist; /* used */
+};
+
+static char *flagtab[] = {
+ ">",
+ "=",
+ ">=",
+ "<",
+ "!=",
+ "<="
+};
+
+
+static Id
+langtag(struct parsedata *pd, Id tag, const char *language)
+{
+ if (language && *language)
+ return pool_id2langid(pd->repo->pool, tag, language, 1);
+ if (!pd->language)
+ return tag;
+ if (tag >= ID_NUM_INTERNAL)
+ return pool_id2langid(pd->repo->pool, tag, pd->language, 1);
+ if (!pd->langcache[tag])
+ pd->langcache[tag] = pool_id2langid(pd->repo->pool, tag, pd->language, 1);
+ return pd->langcache[tag];
+}
+
+/*
+ * adddep
+ * create and add dependency
+ */
+
+static unsigned int
+adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker, char *kind)
+{
+ int i, flags;
+ Id id, evrid;
+ char *sp[4];
+
+ if (line[6] == '/')
+ {
+ /* A file dependency. Do not try to parse it */
+ id = pool_str2id(pool, line + 6, 1);
+ }
+#ifdef ENABLE_COMPLEX_DEPS
+ else if (line[6] == '(')
+ {
+ id = pool_parserpmrichdep(pool, line + 6);
+ if (!id)
+ {
+ pd->ret = pool_error(pool, -1, "susetags: line %d: bad dependency: '%s'\n", pd->lineno, line);
+ return olddeps;
+ }
+ }
+#endif
+ else
+ {
+ i = split(line + 6, sp, 4); /* name, <op>, evr, ? */
+ if (i != 1 && i != 3) /* expect either 'name' or 'name' <op> 'evr' */
+ {
+ pd->ret = pool_error(pool, -1, "susetags: line %d: bad dependency: '%s'\n", pd->lineno, line);
+ return olddeps;
+ }
+ if (kind)
+ id = pool_str2id(pool, join2(&pd->jd, kind, ":", sp[0]), 1);
+ else
+ id = pool_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)
+ {
+ if (!strcmp(sp[1], "<>"))
+ flags = 4;
+ else
+ {
+ pd->ret = pool_error(pool, -1, "susetags: line %d: unknown relation: '%s'\n", pd->lineno, sp[1]);
+ return olddeps;
+ }
+ }
+ id = pool_rel2id(pool, id, evrid, flags + 1, 1);
+ }
+ }
+ return repo_addid_dep(pd->repo, olddeps, id, marker);
+}
+
+
+/*
+ * add_source
+ *
+ */
+
+static void
+add_source(struct parsedata *pd, char *line, Solvable *s, Id handle)
+{
+ Pool *pool = s->repo->pool;
+ char *sp[5];
+ Id name;
+ Id arch;
+ const char *evr, *sevr;
+
+ if (split(line, sp, 5) != 4)
+ {
+ pd->ret = pool_error(pool, -1, "susetags: line %d: bad source line '%s'\n", pd->lineno, line);
+ return;
+ }
+
+ name = pool_str2id(pool, sp[0], 1);
+ arch = pool_str2id(pool, sp[3], 1); /* do this before id2str */
+ evr = join2(&pd->jd, sp[1], "-", sp[2]);
+ sevr = pool_id2str(pool, s->evr);
+ if (sevr)
+ {
+ /* strip epoch */
+ const char *p;
+ for (p = sevr; *p >= '0' && *p <= '9'; p++)
+ ;
+ if (p != sevr && *p == ':' && p[1])
+ sevr = p;
+ }
+ if (name == s->name)
+ repodata_set_void(pd->data, handle, SOLVABLE_SOURCENAME);
+ else
+ repodata_set_id(pd->data, handle, SOLVABLE_SOURCENAME, name);
+ if (sevr && !strcmp(sevr, evr))
+ repodata_set_void(pd->data, handle, SOLVABLE_SOURCEEVR);
+ else
+ repodata_set_id(pd->data, handle, SOLVABLE_SOURCEEVR, pool_str2id(pool, evr, 1));
+ repodata_set_constantid(pd->data, handle, SOLVABLE_SOURCEARCH, arch);
+}
+
+/*
+ * add_dirline
+ * add a line with directory information
+ *
+ */
+
+static void
+add_dirline(struct parsedata *pd, char *line)
+{
+ char *sp[6];
+ long filesz;
+ long filenum;
+ Id dirid;
+ if (split(line, sp, 6) != 5)
+ return;
+ pd->dirs = solv_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
+ filesz = strtol(sp[1], 0, 0);
+ filesz += strtol(sp[2], 0, 0);
+ filenum = strtol(sp[3], 0, 0);
+ filenum += strtol(sp[4], 0, 0);
+ /* hack: we know that there's room for a / */
+ if (*sp[0] != '/')
+ *--sp[0] = '/';
+ dirid = repodata_str2dir(pd->data, sp[0], 1);
+#if 0
+fprintf(stderr, "%s -> %d\n", sp[0], dirid);
+#endif
+ pd->dirs[pd->ndirs][0] = dirid;
+ pd->dirs[pd->ndirs][1] = filesz;
+ pd->dirs[pd->ndirs][2] = filenum;
+ pd->ndirs++;
+}
+
+static void
+set_checksum(struct parsedata *pd, Repodata *data, Id handle, Id keyname, char *line)
+{
+ char *sp[3];
+ Id type;
+ if (split(line, sp, 3) != 2)
+ {
+ pd->ret = pool_error(pd->pool, -1, "susetags: line %d: bad checksum line '%s'\n", pd->lineno, line);
+ return;
+ }
+ type = solv_chksum_str2type(sp[0]);
+ if (!type)
+ {
+ pd->ret = pool_error(pd->pool, -1, "susetags: line %d: unknown checksum type: '%s'\n", pd->lineno, sp[0]);
+ return;
+ }
+ if (strlen(sp[1]) != 2 * solv_chksum_len(type))
+ {
+ pd->ret = pool_error(pd->pool, -1, "susetags: line %d: bad checksum length for type %s: '%s'\n", pd->lineno, sp[0], sp[1]);
+ return;
+ }
+ repodata_set_checksum(data, handle, keyname, type, sp[1]);
+}
+
+
+/*
+ * id3_cmp
+ * compare
+ *
+ */
+
+static int
+id3_cmp(const void *v1, const void *v2, void *dp)
+{
+ Id *i1 = (Id*)v1;
+ Id *i2 = (Id*)v2;
+ return i1[0] - i2[0];
+}
+
+
+/*
+ * commit_diskusage
+ *
+ */
+
+static void
+commit_diskusage(struct parsedata *pd, Id handle)
+{
+ int i;
+ Dirpool *dp = &pd->data->dirpool;
+ /* Now sort in dirid order. This ensures that parents come before
+ their children. */
+ if (pd->ndirs > 1)
+ solv_sort(pd->dirs, pd->ndirs, sizeof(pd->dirs[0]), id3_cmp, 0);
+ /* Substract leaf numbers from all parents to make the numbers
+ non-cumulative. This must be done post-order (i.e. all leafs
+ adjusted before parents). We ensure this by starting at the end of
+ the array moving to the start, hence seeing leafs before parents. */
+ for (i = pd->ndirs; i--;)
+ {
+ Id p = dirpool_parent(dp, pd->dirs[i][0]);
+ int j = i;
+ for (; p; p = dirpool_parent(dp, p))
+ {
+ for (; j--;)
+ if (pd->dirs[j][0] == p)
+ break;
+ if (j >= 0)
+ {
+ if (pd->dirs[j][1] < pd->dirs[i][1])
+ pd->dirs[j][1] = 0;
+ else
+ pd->dirs[j][1] -= pd->dirs[i][1];
+ if (pd->dirs[j][2] < pd->dirs[i][2])
+ pd->dirs[j][2] = 0;
+ else
+ pd->dirs[j][2] -= pd->dirs[i][2];
+ }
+ else
+ /* Haven't found this parent in the list, look further if
+ we maybe find the parents parent. */
+ j = i;
+ }
+ }
+#if 0
+ char sbuf[1024];
+ char *buf = sbuf;
+ unsigned slen = sizeof(sbuf);
+ for (i = 0; i < pd->ndirs; i++)
+ {
+ dir2str(attr, pd->dirs[i][0], &buf, &slen);
+ fprintf(stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
+ }
+ if (buf != sbuf)
+ free (buf);
+#endif
+ for (i = 0; i < pd->ndirs; i++)
+ if (pd->dirs[i][1] || pd->dirs[i][2])
+ {
+ repodata_add_dirnumnum(pd->data, handle, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
+ }
+ pd->ndirs = 0;
+}
+
+
+/* Unfortunately "a"[0] is no constant expression in the C languages,
+ so we need to pass the four characters individually :-/ */
+#define CTAG(a,b,c,d) ((unsigned)(((unsigned char)a) << 24) \
+ | ((unsigned char)b << 16) \
+ | ((unsigned char)c << 8) \
+ | ((unsigned char)d))
+
+/*
+ * tag_from_string
+ *
+ */
+
+static inline unsigned
+tag_from_string(char *cs)
+{
+ unsigned char *s = (unsigned char *)cs;
+ return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]);
+}
+
+
+/*
+ * repo_add_susetags
+ * Parse susetags file passed in fp, fill solvables into repo
+ *
+ * susetags is key,value based
+ * for short values
+ * =key: value
+ * is used
+ * for long (multi-line) values,
+ * +key:
+ * value
+ * value
+ * -key:
+ * is used
+ *
+ * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata
+ * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/packages
+ * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/pattern
+ *
+ * Assumptions:
+ * All keys have 3 characters and end in ':'
+ */
+
+static void
+finish_solvable(struct parsedata *pd, Solvable *s, Offset freshens)
+{
+ Pool *pool = pd->repo->pool;
+ Id handle = s - pool->solvables;
+
+ if (pd->nfilelist)
+ {
+ int l;
+ Id did;
+ for (l = 0; l < pd->nfilelist; l += strlen(pd->filelist + l) + 1)
+ {
+ char *p = strrchr(pd->filelist + l, '/');
+ if (!p)
+ continue;
+ *p++ = 0;
+ did = repodata_str2dir(pd->data, pd->filelist + l, 1);
+ p[-1] = '/';
+ if (!did)
+ did = repodata_str2dir(pd->data, "/", 1);
+ repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, did, p);
+ }
+ pd->nfilelist = 0;
+ }
+ /* A self provide, except for source packages. This is harmless
+ to do twice (in case we see the same package twice). */
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(pd->repo, s->provides,
+ pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ /* XXX This uses repo_addid_dep internally, so should also be
+ harmless to do twice. */
+ s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, freshens);
+ s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
+ if (pd->ndirs)
+ commit_diskusage(pd, handle);
+}
+
+static Hashtable
+joinhash_init(Repo *repo, Hashval *hmp)
+{
+ Hashval hm = mkmask(repo->nsolvables);
+ Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
+ Hashval h, hh;
+ Solvable *s;
+ int i;
+
+ FOR_REPO_SOLVABLES(repo, i, s)
+ {
+ hh = HASHCHAIN_START;
+ h = s->name & hm;
+ while (ht[h])
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ ht[h] = i;
+ }
+ *hmp = hm;
+ return ht;
+}
+
+static Solvable *
+joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, Id name, Id evr, Id arch, Id start)
+{
+ Hashval h, hh;
+
+ if (!name || !arch || !evr)
+ return 0;
+ hh = HASHCHAIN_START;
+ h = name & hm;
+ while (ht[h])
+ {
+ Solvable *s = repo->pool->solvables + ht[h];
+ if (ht[h] >= start && s->name == name && s->evr == evr && s->arch == arch)
+ return s;
+ h = HASHCHAIN_NEXT(h, hh, hm);
+ }
+ return 0;
+}
+
+static Id
+lookup_shared_id(Repodata *data, Id p, Id keyname, Id voidid, int uninternalized)
+{
+ Id r;
+ r = repodata_lookup_type(data, p, keyname);
+ if (r)
+ {
+ if (r == REPOKEY_TYPE_VOID)
+ return voidid;
+ r = repodata_lookup_id(data, p, keyname);
+ if (r)
+ return r;
+ }
+ if (uninternalized)
+ return repodata_lookup_id_uninternalized(data, p, keyname, voidid);
+ return 0;
+}
+
+static inline Id
+toevr(Pool *pool, struct parsedata *pd, const char *version, const char *release)
+{
+ return makeevr(pool, !release || (release[0] == '-' && !release[1]) ?
+ version : join2(&pd->jd, version, "-", release));
+}
+
+
+/*
+ * parse susetags
+ *
+ * fp: file to read from
+ * defvendor: default vendor (0 if none)
+ * language: current language (0 if none)
+ * flags: flags
+ */
+
+int
+repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int flags)
+{
+ Pool *pool = repo->pool;
+ char *line, *linep;
+ int aline;
+ Solvable *s;
+ Offset freshens;
+ int intag = 0;
+ int cummulate = 0;
+ int indesc = 0;
+ int indelta = 0;
+ int last_found_pack = 0;
+ Id first_new_pkg = 0;
+ char *sp[5];
+ struct parsedata pd;
+ Repodata *data = 0;
+ Id handle = 0;
+ Hashtable joinhash = 0;
+ Hashval joinhashm = 0;
+ int createdpkgs = 0;
+
+ if ((flags & (SUSETAGS_EXTEND|REPO_EXTEND_SOLVABLES)) != 0 && repo->nrepodata)
+ {
+ joinhash = joinhash_init(repo, &joinhashm);
+ indesc = 1;
+ }
+
+ data = repo_add_repodata(repo, flags);
+
+ memset(&pd, 0, sizeof(pd));
+ line = solv_malloc(1024);
+ aline = 1024;
+
+ pd.pool = pool;
+ pd.repo = repo;
+ pd.data = data;
+ pd.flags = flags;
+ pd.language = language && *language ? solv_strdup(language) : 0;
+
+ linep = line;
+ s = 0;
+ freshens = 0;
+
+ /* if this is a join setup the recorded share data */
+ if (joinhash)
+ {
+ Repodata *sdata;
+ int i;
+
+ FOR_REPODATAS(repo, i, sdata)
+ {
+ int p;
+ if (!repodata_has_keyname(sdata, SUSETAGS_SHARE_NAME))
+ continue;
+ for (p = sdata->start; p < sdata->end; p++)
+ {
+ Id name, evr, arch;
+ name = lookup_shared_id(sdata, p, SUSETAGS_SHARE_NAME, pool->solvables[p].name, sdata == data);
+ if (!name)
+ continue;
+ evr = lookup_shared_id(sdata, p, SUSETAGS_SHARE_EVR, pool->solvables[p].evr, sdata == data);
+ if (!evr)
+ continue;
+ arch = lookup_shared_id(sdata, p, SUSETAGS_SHARE_ARCH, pool->solvables[p].arch, sdata == data);
+ if (!arch)
+ continue;
+ if (p - repo->start >= pd.nshare)
+ {
+ pd.share_with = solv_realloc2(pd.share_with, p - repo->start + 256, sizeof(*pd.share_with));
+ memset(pd.share_with + pd.nshare, 0, (p - repo->start + 256 - pd.nshare) * sizeof(*pd.share_with));
+ pd.nshare = p - repo->start + 256;
+ }
+ pd.share_with[p - repo->start].name = name;
+ pd.share_with[p - repo->start].evr = evr;
+ pd.share_with[p - repo->start].arch = arch;
+ }
+ }
+ }
+
+ /*
+ * read complete file
+ *
+ * collect values in 'struct parsedata pd'
+ * then build .solv (and .attr) file
+ */
+
+ for (;;)
+ {
+ unsigned tag;
+ char *olinep; /* old line pointer */
+ char line_lang[6];
+ int keylen = 3;
+
+ if (pd.ret)
+ break;
+ if (linep - line + 16 > aline) /* (re-)alloc buffer */
+ {
+ aline = linep - line;
+ line = solv_realloc(line, aline + 512);
+ linep = line + aline;
+ aline += 512;
+ }
+ if (!fgets(linep, aline - (linep - line), fp)) /* read line */
+ break;
+ olinep = linep;
+ linep += strlen(linep);
+ if (linep == line || linep[-1] != '\n')
+ continue;
+ pd.lineno++;
+ *--linep = 0;
+ if (linep == olinep)
+ continue;
+
+ if (intag)
+ {
+ /* check for multi-line value tags (+Key:/-Key:) */
+
+ int is_end = (linep[-intag - keylen + 1] == '-')
+ && (linep[-1] == ':')
+ && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - keylen] == '\n');
+ if (is_end
+ && strncmp(linep - 1 - intag, line + 1, intag))
+ {
+ pool_debug(pool, SOLV_ERROR, "susetags: Nonmatching multi-line tags: %d: '%s' '%s' %d\n", pd.lineno, linep - 1 - intag, line + 1, intag);
+ }
+ if (cummulate && !is_end)
+ {
+ *linep++ = '\n';
+ continue;
+ }
+ if (cummulate && is_end)
+ {
+ linep[-intag - keylen + 1] = 0;
+ if (linep[-intag - keylen] == '\n')
+ linep[-intag - keylen] = 0;
+ linep = line;
+ intag = 0;
+ }
+ if (!cummulate && is_end)
+ {
+ intag = 0;
+ linep = line;
+ continue;
+ }
+ if (!cummulate && !is_end)
+ linep = line + intag + keylen;
+ }
+ else
+ linep = line;
+
+ if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
+ {
+ char *tagend = strchr(line, ':');
+ if (!tagend)
+ {
+ pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
+ break;
+ }
+ intag = tagend - (line + 1);
+ cummulate = 0;
+ switch (tag_from_string(line)) /* check if accumulation is needed */
+ {
+ case CTAG('+', 'D', 'e', 's'):
+ case CTAG('+', 'E', 'u', 'l'):
+ case CTAG('+', 'I', 'n', 's'):
+ case CTAG('+', 'D', 'e', 'l'):
+ case CTAG('+', 'A', 'u', 't'):
+ if (line[4] == ':' || line[4] == '.')
+ cummulate = 1;
+ break;
+ default:
+ break;
+ }
+ line[0] = '='; /* handle lines between +Key:/-Key: as =Key: */
+ line[intag + keylen - 1] = ' ';
+ linep = line + intag + keylen;
+ continue;
+ }
+ if (*line == '#' || !*line)
+ continue;
+ if (! (line[0] && line[1] && line[2] && line[3] && (line[4] == ':' || line[4] == '.')))
+ continue;
+ line_lang[0] = 0;
+ if (line[4] == '.')
+ {
+ char *endlang;
+ endlang = strchr(line + 5, ':');
+ if (endlang)
+ {
+ int langsize = endlang - (line + 5);
+ keylen = endlang - line - 1;
+ if (langsize > 5)
+ langsize = 5;
+ strncpy(line_lang, line + 5, langsize);
+ line_lang[langsize] = 0;
+ }
+ }
+ tag = tag_from_string(line);
+
+ if (indelta)
+ {
+ /* Example:
+ =Dlt: subversion 1.6.16 1.3.1 i586
+ =Dsq: subversion 1.6.15 4.2 i586 d57b3fc86e7a2f73796e8e35b96fa86212c910
+ =Cks: SHA1 14a8410cf741856a5d70d89dab62984dba6a1ca7
+ =Loc: 1 subversion-1.6.15_1.6.16-4.2_1.3.1.i586.delta.rpm
+ =Siz: 81558
+ */
+ switch (tag)
+ {
+ case CTAG('=', 'D', 's', 'q'):
+ {
+ Id evr;
+ if (split(line + 5, sp, 5) != 5)
+ continue;
+ repodata_set_id(data, handle, DELTA_SEQ_NAME, pool_str2id(pool, sp[0], 1));
+ evr = toevr(pool, &pd, sp[1], sp[2]);
+ repodata_set_id(data, handle, DELTA_SEQ_EVR, evr);
+ /* repodata_set_id(data, handle, DELTA_SEQ_ARCH, pool_str2id(pool, sp[3], 1)); */
+ repodata_set_str(data, handle, DELTA_SEQ_NUM, sp[4]);
+ repodata_set_id(data, handle, DELTA_BASE_EVR, evr);
+ continue;
+ }
+ case CTAG('=', 'C', 'k', 's'):
+ set_checksum(&pd, data, handle, DELTA_CHECKSUM, line + 6);
+ continue;
+ case CTAG('=', 'L', 'o', 'c'):
+ {
+ int i = split(line + 6, sp, 3);
+ if (i != 2 && i != 3)
+ {
+ pd.ret = pool_error(pool, -1, "susetags: line %d: bad location line '%s'\n", pd.lineno, line);
+ continue;
+ }
+ repodata_set_deltalocation(data, handle, atoi(sp[0]), i == 3 ? sp[2] : 0, sp[1]);
+ continue;
+ }
+ case CTAG('=', 'S', 'i', 'z'):
+ if (split(line + 6, sp, 3) == 2)
+ repodata_set_num(data, handle, DELTA_DOWNLOADSIZE, strtoull(sp[0], 0, 10));
+ continue;
+ case CTAG('=', 'P', 'k', 'g'):
+ case CTAG('=', 'P', 'a', 't'):
+ case CTAG('=', 'D', 'l', 't'):
+ handle = 0;
+ indelta = 0;
+ break;
+ default:
+ pool_debug(pool, SOLV_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
+ continue;
+ }
+ }
+
+ /*
+ * start of (next) package or pattern or delta
+ *
+ * =Pkg: <name> <version> <release> <architecture>
+ * (=Pat: ...)
+ */
+ if (tag == CTAG('=', 'D', 'l', 't'))
+ {
+ if (s)
+ finish_solvable(&pd, s, freshens);
+ s = 0;
+ pd.kind = 0;
+ if (split(line + 5, sp, 5) != 4)
+ {
+ pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
+ break;
+ }
+ handle = repodata_new_handle(data);
+ repodata_set_id(data, handle, DELTA_PACKAGE_NAME, pool_str2id(pool, sp[0], 1));
+ repodata_set_id(data, handle, DELTA_PACKAGE_EVR, toevr(pool, &pd, sp[1], sp[2]));
+ repodata_set_id(data, handle, DELTA_PACKAGE_ARCH, pool_str2id(pool, sp[3], 1));
+ repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DELTAINFO, handle);
+ indelta = 1;
+ continue;
+ }
+ if (tag == CTAG('=', 'P', 'k', 'g')
+ || tag == CTAG('=', 'P', 'a', 't'))
+ {
+ /* If we have an old solvable, complete it by filling in some
+ default stuff. */
+ if (s)
+ finish_solvable(&pd, s, freshens);
+
+ /*
+ * define kind
+ */
+
+ pd.kind = 0;
+ if (line[3] == 't')
+ pd.kind = "pattern";
+
+ /*
+ * parse nevra
+ */
+
+ if (split(line + 5, sp, 5) != 4)
+ {
+ pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
+ break;
+ }
+ s = 0;
+ freshens = 0;
+
+ if (joinhash)
+ {
+ /* data join operation. find solvable matching name/arch/evr and
+ * add data to it */
+ Id name, evr, arch;
+ /* we don't use the create flag here as a simple pre-check for existance */
+ if (pd.kind)
+ name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 0);
+ else
+ name = pool_str2id(pool, sp[0], 0);
+ evr = toevr(pool, &pd, sp[1], sp[2]);
+ arch = pool_str2id(pool, sp[3], 0);
+ if (name && arch)
+ {
+ Id start = (flags & REPO_EXTEND_SOLVABLES) ? 0 : first_new_pkg;
+ if (repo->start + last_found_pack + 1 >= start && repo->start + last_found_pack + 1 < repo->end)
+ {
+ s = pool->solvables + repo->start + last_found_pack + 1;
+ if (s->repo != repo || s->name != name || s->evr != evr || s->arch != arch)
+ s = 0;
+ }
+ if (!s)
+ s = joinhash_lookup(repo, joinhash, joinhashm, name, evr, arch, start);
+ }
+ /* do not create new packages in EXTEND_SOLVABLES mode */
+ if (!s && (flags & REPO_EXTEND_SOLVABLES) != 0)
+ continue;
+ /* fallthrough to package creation */
+ }
+ if (!s)
+ {
+ /* normal operation. create a new solvable. */
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ if (pd.kind)
+ s->name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 1);
+ else
+ s->name = pool_str2id(pool, sp[0], 1);
+ s->evr = toevr(pool, &pd, sp[1], sp[2]);
+ s->arch = pool_str2id(pool, sp[3], 1);
+ s->vendor = defvendor;
+ if (!first_new_pkg)
+ first_new_pkg = s - pool->solvables;
+ createdpkgs = 1;
+ }
+ last_found_pack = (s - pool->solvables) - repo->start;
+ if (data)
+ handle = s - pool->solvables;
+ }
+
+ /* If we have no current solvable to add to, ignore all further lines
+ for it. Probably invalid input data in the second set of
+ solvables. */
+ if (indesc >= 2 && !s)
+ {
+#if 0
+ pool_debug(pool, SOLV_ERROR, "susetags: huh %d: %s?\n", pd.lineno, line);
+#endif
+ continue;
+ }
+ switch (tag)
+ {
+ case CTAG('=', 'P', 'r', 'v'): /* provides */
+ if (line[6] == '/')
+ {
+ /* probably a filelist entry. stash it away for now */
+ int l = strlen(line + 6) + 1;
+ if (pd.nfilelist + l > pd.afilelist)
+ {
+ pd.afilelist = pd.nfilelist + l + 512;
+ pd.filelist = solv_realloc(pd.filelist, pd.afilelist);
+ }
+ memcpy(pd.filelist + pd.nfilelist, line + 6, l);
+ pd.nfilelist += l;
+ break;
+ }
+ if (pd.nfilelist)
+ {
+ int l;
+ for (l = 0; l < pd.nfilelist; l += strlen(pd.filelist + l) + 1)
+ s->provides = repo_addid_dep(pd.repo, s->provides, pool_str2id(pool, pd.filelist + l, 1), 0);
+ pd.nfilelist = 0;
+ }
+ s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
+ continue;
+ case CTAG('=', 'R', 'e', 'q'): /* requires */
+ s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
+ continue;
+ case CTAG('=', 'P', 'r', 'q'): /* pre-requires / packages required */
+ if (pd.kind)
+ {
+ s->requires = adddep(pool, &pd, s->requires, line, 0, 0); /* patterns: a required package */
+ }
+ else
+ s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
+ continue;
+ case CTAG('=', 'O', 'b', 's'): /* obsoletes */
+ s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
+ continue;
+ case CTAG('=', 'C', 'o', 'n'): /* conflicts */
+ s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
+ continue;
+ case CTAG('=', 'R', 'e', 'c'): /* recommends */
+ s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
+ continue;
+ case CTAG('=', 'S', 'u', 'p'): /* supplements */
+ s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
+ continue;
+ case CTAG('=', 'E', 'n', 'h'): /* enhances */
+ s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
+ continue;
+ case CTAG('=', 'S', 'u', 'g'): /* suggests */
+ s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
+ continue;
+ case CTAG('=', 'F', 'r', 'e'): /* freshens */
+ freshens = adddep(pool, &pd, freshens, line, 0, pd.kind);
+ continue;
+ case CTAG('=', 'P', 'r', 'c'): /* packages recommended */
+ s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
+ continue;
+ case CTAG('=', 'P', 's', 'g'): /* packages suggested */
+ s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
+ continue;
+ case CTAG('=', 'P', 'c', 'n'): /* pattern: package conflicts */
+ s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
+ continue;
+ case CTAG('=', 'P', 'o', 'b'): /* pattern: package obsoletes */
+ s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
+ continue;
+ case CTAG('=', 'P', 'f', 'r'): /* pattern: package freshens */
+ freshens = adddep(pool, &pd, freshens, line, 0, 0);
+ continue;
+ case CTAG('=', 'P', 's', 'p'): /* pattern: package supplements */
+ s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
+ continue;
+ case CTAG('=', 'P', 'e', 'n'): /* pattern: package enhances */
+ s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
+ continue;
+ case CTAG('=', 'V', 'e', 'r'): /* - version - */
+ last_found_pack = 0;
+ handle = 0;
+ indesc++;
+ if (createdpkgs)
+ {
+ solv_free(joinhash);
+ joinhash = joinhash_init(repo, &joinhashm);
+ createdpkgs = 0;
+ }
+ continue;
+ case CTAG('=', 'V', 'n', 'd'): /* vendor */
+ s->vendor = pool_str2id(pool, line + 6, 1);
+ continue;
+
+ /* From here it's the attribute tags. */
+ case CTAG('=', 'G', 'r', 'p'):
+ repodata_set_poolstr(data, handle, SOLVABLE_GROUP, line + 6);
+ continue;
+ case CTAG('=', 'L', 'i', 'c'):
+ repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, line + 6);
+ continue;
+ case CTAG('=', 'L', 'o', 'c'):
+ {
+ int i = split(line + 6, sp, 3);
+ if (i != 2 && i != 3)
+ {
+ pd.ret = pool_error(pool, -1, "susetags: line %d: bad location line '%s'\n", pd.lineno, line);
+ continue;
+ }
+ repodata_set_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : pool_id2str(pool, s->arch), sp[1]);
+ }
+ continue;
+ case CTAG('=', 'S', 'r', 'c'):
+ add_source(&pd, line + 6, s, handle);
+ continue;
+ case CTAG('=', 'S', 'i', 'z'):
+ if (split(line + 6, sp, 3) == 2)
+ {
+ repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(sp[0], 0, 10));
+ repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, strtoull(sp[1], 0, 10));
+ }
+ continue;
+ case CTAG('=', 'T', 'i', 'm'):
+ {
+ unsigned int t = atoi(line + 6);
+ if (t)
+ repodata_set_num(data, handle, SOLVABLE_BUILDTIME, t);
+ }
+ continue;
+ case CTAG('=', 'K', 'w', 'd'):
+ repodata_add_poolstr_array(data, handle, SOLVABLE_KEYWORDS, line + 6);
+ continue;
+ case CTAG('=', 'A', 'u', 't'):
+ repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
+ continue;
+ case CTAG('=', 'S', 'u', 'm'):
+ repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, line_lang), line + 3 + keylen);
+ continue;
+ case CTAG('=', 'D', 'e', 's'):
+ repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, line_lang), line + 3 + keylen);
+ continue;
+ case CTAG('=', 'E', 'u', 'l'):
+ repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, line_lang), line + 6);
+ continue;
+ case CTAG('=', 'I', 'n', 's'):
+ repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, line_lang), line + 6);
+ continue;
+ case CTAG('=', 'D', 'e', 'l'):
+ repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, line_lang), line + 6);
+ continue;
+ case CTAG('=', 'V', 'i', 's'):
+ {
+ /* Accept numbers and textual bools. */
+ int k;
+ k = atoi(line + 6);
+ if (k || !strcasecmp(line + 6, "true"))
+ repodata_set_void(data, handle, SOLVABLE_ISVISIBLE);
+ }
+ continue;
+ case CTAG('=', 'S', 'h', 'r'):
+ {
+ Id name, evr, arch;
+ if (split(line + 6, sp, 5) != 4)
+ {
+ pd.ret = pool_error(pool, -1, "susetags: line %d: bad =Shr line '%s'\n", pd.lineno, line);
+ continue;
+ }
+ name = pool_str2id(pool, sp[0], 1);
+ evr = toevr(pool, &pd, sp[1], sp[2]);
+ arch = pool_str2id(pool, sp[3], 1);
+ if (last_found_pack >= pd.nshare)
+ {
+ pd.share_with = solv_realloc2(pd.share_with, last_found_pack + 256, sizeof(*pd.share_with));
+ memset(pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof(*pd.share_with));
+ pd.nshare = last_found_pack + 256;
+ }
+ pd.share_with[last_found_pack].name = name;
+ pd.share_with[last_found_pack].evr = evr;
+ pd.share_with[last_found_pack].arch = arch;
+ if ((flags & SUSETAGS_RECORD_SHARES) != 0)
+ {
+ if (s->name == name)
+ repodata_set_void(data, handle, SUSETAGS_SHARE_NAME);
+ else
+ repodata_set_id(data, handle, SUSETAGS_SHARE_NAME, name);
+ if (s->evr == evr)
+ repodata_set_void(data, handle, SUSETAGS_SHARE_EVR);
+ else
+ repodata_set_id(data, handle, SUSETAGS_SHARE_EVR, evr);
+ if (s->arch == arch)
+ repodata_set_void(data, handle, SUSETAGS_SHARE_ARCH);
+ else
+ repodata_set_id(data, handle, SUSETAGS_SHARE_ARCH, arch);
+ }
+ continue;
+ }
+ case CTAG('=', 'D', 'i', 'r'):
+ add_dirline(&pd, line + 6);
+ continue;
+ case CTAG('=', 'C', 'a', 't'):
+ repodata_set_poolstr(data, handle, langtag(&pd, SOLVABLE_CATEGORY, line_lang), line + 3 + keylen);
+ break;
+ case CTAG('=', 'O', 'r', 'd'):
+ /* Order is a string not a number, so we can retroactively insert
+ new patterns in the middle, i.e. 1 < 15 < 2. */
+ repodata_set_str(data, handle, SOLVABLE_ORDER, line + 6);
+ break;
+ case CTAG('=', 'I', 'c', 'o'):
+ repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
+ break;
+ case CTAG('=', 'E', 'x', 't'):
+ repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2(&pd.jd, "pattern", ":", line + 6));
+ break;
+ case CTAG('=', 'I', 'n', 'c'):
+ repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2(&pd.jd, "pattern", ":", line + 6));
+ break;
+ case CTAG('=', 'C', 'k', 's'):
+ set_checksum(&pd, data, handle, SOLVABLE_CHECKSUM, line + 6);
+ break;
+ case CTAG('=', 'L', 'a', 'n'):
+ pd.language = solv_free(pd.language);
+ memset(pd.langcache, 0, sizeof(pd.langcache));
+ if (line[6])
+ pd.language = solv_strdup(line + 6);
+ break;
+
+ case CTAG('=', 'F', 'l', 's'):
+ {
+ char *p = strrchr(line + 6, '/');
+ Id did;
+ /* strip trailing slash */
+ if (p && p != line + 6 && !p[1])
+ {
+ *p = 0;
+ p = strrchr(line + 6, '/');
+ }
+ if (p)
+ {
+ *p++ = 0;
+ did = repodata_str2dir(data, line + 6, 1);
+ }
+ else
+ {
+ p = line + 6;
+ did = 0;
+ }
+ if (!did)
+ did = repodata_str2dir(data, "/", 1);
+ repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, p);
+ break;
+ }
+ case CTAG('=', 'H', 'd', 'r'):
+ /* rpm header range */
+ if (split(line + 6, sp, 3) == 2)
+ {
+ /* we ignore the start value */
+ unsigned int end = (unsigned int)atoi(sp[1]);
+ if (end)
+ repodata_set_num(data, handle, SOLVABLE_HEADEREND, end);
+ }
+ break;
+
+ case CTAG('=', 'P', 'a', 't'):
+ case CTAG('=', 'P', 'k', 'g'):
+ break;
+
+ default:
+#if 0
+ pool_debug(pool, SOLV_WARN, "susetags: unknown line: %d: %s\n", pd.lineno, line);
+#endif
+ break;
+ }
+
+ }
+
+ if (s)
+ finish_solvable(&pd, s, freshens);
+ solv_free(pd.filelist);
+
+ /* Shared attributes
+ * (e.g. multiple binaries built from same source)
+ */
+ if (pd.nshare)
+ {
+ int i, last_found;
+ Map keyidmap;
+
+ map_init(&keyidmap, data->nkeys);
+ for (i = 1; i < data->nkeys; i++)
+ {
+ Id keyname = data->keys[i].name;
+ if (keyname == SOLVABLE_INSTALLSIZE || keyname == SOLVABLE_DISKUSAGE || keyname == SOLVABLE_FILELIST)
+ continue;
+ if (keyname == SOLVABLE_MEDIADIR || keyname == SOLVABLE_MEDIAFILE || keyname == SOLVABLE_MEDIANR)
+ continue;
+ if (keyname == SOLVABLE_DOWNLOADSIZE || keyname == SOLVABLE_CHECKSUM)
+ continue;
+ if (keyname == SOLVABLE_SOURCENAME || keyname == SOLVABLE_SOURCEARCH || keyname == SOLVABLE_SOURCEEVR)
+ continue;
+ if (keyname == SOLVABLE_PKGID || keyname == SOLVABLE_HDRID || keyname == SOLVABLE_LEADSIGID)
+ continue;
+ if (keyname == SUSETAGS_SHARE_NAME || keyname == SUSETAGS_SHARE_EVR || keyname == SUSETAGS_SHARE_ARCH)
+ continue;
+ MAPSET(&keyidmap, i);
+ }
+ last_found = 0;
+ for (i = 0; i < pd.nshare; i++)
+ {
+ unsigned int n, nn;
+ Solvable *found = 0;
+ if (!pd.share_with[i].name)
+ continue;
+ for (n = repo->start, nn = repo->start + last_found; n < repo->end; n++, nn++)
+ {
+ if (nn >= repo->end)
+ nn = repo->start;
+ found = pool->solvables + nn;
+ if (found->repo == repo
+ && found->name == pd.share_with[i].name
+ && found->evr == pd.share_with[i].evr
+ && found->arch == pd.share_with[i].arch)
+ {
+ last_found = nn - repo->start;
+ break;
+ }
+ }
+ if (n != repo->end)
+ repodata_merge_some_attrs(data, repo->start + i, repo->start + last_found, &keyidmap, 0);
+ }
+ free(pd.share_with);
+ map_free(&keyidmap);
+ }
+
+ solv_free(joinhash);
+ repodata_free_dircache(data);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+
+ solv_free(pd.language);
+ solv_free(line);
+ join_freemem(&pd.jd);
+ return pd.ret;
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* read susetags file <fp> into <repo>
+ * if <attrname> given, write attributes as '<attrname>.attr'
+ */
+
+#define SUSETAGS_EXTEND (1 << 9)
+#define SUSETAGS_RECORD_SHARES (1 << 10)
+
+extern int repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE /* glibc2 needs this */
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+#include <time.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_updateinfoxml.h"
+#define DISABLE_SPLIT
+#include "tools_util.h"
+
+/*
+ * <updates>
+ * <update from="rel-eng@fedoraproject.org" status="stable" type="security" version="1.4">
+ * <id>FEDORA-2007-4594</id>
+ * <title>imlib-1.9.15-6.fc8</title>
+ * <severity>Important</severity>
+ * <release>Fedora 8</release>
+ * <rights>Copyright 2007 Company Inc</rights>
+ * <issued date="2007-12-28 16:42:30"/>
+ * <updated date="2008-03-14 12:00:00"/>
+ * <references>
+ * <reference href="https://bugzilla.redhat.com/show_bug.cgi?id=426091" id="426091" title="CVE-2007-3568 imlib: infinite loop DoS using crafted BMP image" type="bugzilla"/>
+ * </references>
+ * <description>This update includes a fix for a denial-of-service issue (CVE-2007-3568) whereby an attacker who could get an imlib-using user to view a specially-crafted BMP image could cause the user's CPU to go into an infinite loop.</description>
+ * <pkglist>
+ * <collection short="F8">
+ * <name>Fedora 8</name>
+ * <package arch="ppc64" name="imlib-debuginfo" release="6.fc8" src="http://download.fedoraproject.org/pub/fedora/linux/updates/8/ppc64/imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm" version="1.9.15">
+ * <filename>imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm</filename>
+ * <reboot_suggested>True</reboot_suggested>
+ * </package>
+ * </collection>
+ * </pkglist>
+ * </update>
+ * </updates>
+*/
+
+enum state {
+ STATE_START,
+ STATE_UPDATES,
+ STATE_UPDATE,
+ STATE_ID,
+ STATE_TITLE,
+ STATE_RELEASE,
+ STATE_ISSUED,
+ STATE_UPDATED,
+ STATE_MESSAGE,
+ STATE_REFERENCES,
+ STATE_REFERENCE,
+ STATE_DESCRIPTION,
+ STATE_PKGLIST,
+ STATE_COLLECTION,
+ STATE_NAME,
+ STATE_PACKAGE,
+ STATE_FILENAME,
+ STATE_REBOOT,
+ STATE_RESTART,
+ STATE_RELOGIN,
+ STATE_RIGHTS,
+ STATE_SEVERITY,
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+
+/* !! must be sorted by first column !! */
+static struct stateswitch stateswitches[] = {
+ { STATE_START, "updates", STATE_UPDATES, 0 },
+ { STATE_START, "update", STATE_UPDATE, 0 },
+ { STATE_UPDATES, "update", STATE_UPDATE, 0 },
+ { STATE_UPDATE, "id", STATE_ID, 1 },
+ { STATE_UPDATE, "title", STATE_TITLE, 1 },
+ { STATE_UPDATE, "severity", STATE_SEVERITY, 1 },
+ { STATE_UPDATE, "rights", STATE_RIGHTS, 1 },
+ { STATE_UPDATE, "release", STATE_RELEASE, 1 },
+ { STATE_UPDATE, "issued", STATE_ISSUED, 0 },
+ { STATE_UPDATE, "updated", STATE_UPDATED, 0 },
+ { STATE_UPDATE, "description", STATE_DESCRIPTION, 1 },
+ { STATE_UPDATE, "message", STATE_MESSAGE , 1 },
+ { STATE_UPDATE, "references", STATE_REFERENCES, 0 },
+ { STATE_UPDATE, "pkglist", STATE_PKGLIST, 0 },
+ { STATE_REFERENCES, "reference", STATE_REFERENCE, 0 },
+ { STATE_PKGLIST, "collection", STATE_COLLECTION, 0 },
+ { STATE_COLLECTION, "name", STATE_NAME, 1 },
+ { STATE_COLLECTION, "package", STATE_PACKAGE, 0 },
+ { STATE_PACKAGE, "filename", STATE_FILENAME, 1 },
+ { STATE_PACKAGE, "reboot_suggested",STATE_REBOOT, 1 },
+ { STATE_PACKAGE, "restart_suggested",STATE_RESTART, 1 },
+ { STATE_PACKAGE, "relogin_suggested",STATE_RELOGIN, 1 },
+ { NUMSTATES }
+};
+
+struct parsedata {
+ int ret;
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+ Id handle;
+ Solvable *solvable;
+ time_t buildtime;
+ Id collhandle;
+ struct joindata jd;
+
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+};
+
+/*
+ * Convert date strings ("1287746075" or "2010-10-22 13:14:35")
+ * to timestamp.
+ */
+static time_t
+datestr2timestamp(const char *date)
+{
+ const char *p;
+ struct tm tm;
+
+ if (!date || !*date)
+ return 0;
+ for (p = date; *p >= '0' && *p <= '9'; p++)
+ ;
+ if (!*p)
+ return atoi(date);
+ memset(&tm, 0, sizeof(tm));
+ if (!strptime(date, "%F%T", &tm))
+ return 0;
+ return timegm(&tm);
+}
+
+/*
+ * create evr (as Id) from 'epoch', 'version' and 'release' attributes
+ */
+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, "version"))
+ v = atts[1];
+ else if (!strcmp(*atts, "release"))
+ r = atts[1];
+ }
+ if (e && (!*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 pool_str2id(pool, pd->content, 1);
+}
+
+
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *solvable = pd->solvable;
+ struct stateswitch *sw;
+ /*const char *str; */
+
+#if 0
+ fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ pd->depth++;
+ if (!pd->swtab[pd->state])
+ return;
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
+ if (!strcmp(sw->ename, name))
+ break;
+
+ if (sw->from != pd->state)
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#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_START:
+ break;
+ case STATE_UPDATES:
+ break;
+ /*
+ * <update from="rel-eng@fedoraproject.org"
+ * status="stable"
+ * type="bugfix" (enhancement, security)
+ * version="1.4">
+ */
+ case STATE_UPDATE:
+ {
+ const char *from = 0, *type = 0, *version = 0;
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, "from"))
+ from = atts[1];
+ else if (!strcmp(*atts, "type"))
+ type = atts[1];
+ else if (!strcmp(*atts, "version"))
+ version = atts[1];
+ }
+ solvable = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+ pd->handle = pd->solvable - pool->solvables;
+
+ solvable->vendor = pool_str2id(pool, from, 1);
+ solvable->evr = pool_str2id(pool, version, 1);
+ solvable->arch = ARCH_NOARCH;
+ if (type)
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_PATCHCATEGORY, type);
+ pd->buildtime = (time_t)0;
+ }
+ break;
+ /* <id>FEDORA-2007-4594</id> */
+ case STATE_ID:
+ break;
+ /* <title>imlib-1.9.15-6.fc8</title> */
+ case STATE_TITLE:
+ break;
+ /* <release>Fedora 8</release> */
+ case STATE_RELEASE:
+ break;
+ /* <issued date="2008-03-21 21:36:55"/>
+ */
+ case STATE_ISSUED:
+ case STATE_UPDATED:
+ {
+ const char *date = 0;
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, "date"))
+ date = atts[1];
+ }
+ if (date)
+ {
+ time_t t = datestr2timestamp(date);
+ if (t && t > pd->buildtime)
+ pd->buildtime = t;
+ }
+ }
+ break;
+ case STATE_REFERENCES:
+ break;
+ /* <reference href="https://bugzilla.redhat.com/show_bug.cgi?id=330471"
+ * id="330471"
+ * title="LDAP schema file missing for dhcpd"
+ * type="bugzilla"/>
+ */
+ case STATE_REFERENCE:
+ {
+ const char *href = 0, *id = 0, *title = 0, *type = 0;
+ Id refhandle;
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, "href"))
+ href = atts[1];
+ else if (!strcmp(*atts, "id"))
+ id = atts[1];
+ else if (!strcmp(*atts, "title"))
+ title = atts[1];
+ else if (!strcmp(*atts, "type"))
+ type = atts[1];
+ }
+ refhandle = repodata_new_handle(pd->data);
+ if (href)
+ repodata_set_str(pd->data, refhandle, UPDATE_REFERENCE_HREF, href);
+ if (id)
+ repodata_set_str(pd->data, refhandle, UPDATE_REFERENCE_ID, id);
+ if (title)
+ repodata_set_str(pd->data, refhandle, UPDATE_REFERENCE_TITLE, title);
+ if (type)
+ repodata_set_poolstr(pd->data, refhandle, UPDATE_REFERENCE_TYPE, type);
+ repodata_add_flexarray(pd->data, pd->handle, UPDATE_REFERENCE, refhandle);
+ }
+ break;
+ /* <description>This update ...</description> */
+ case STATE_DESCRIPTION:
+ break;
+ /* <message type="confirm">This update ...</message> */
+ case STATE_MESSAGE:
+ break;
+ case STATE_PKGLIST:
+ break;
+ /* <collection short="F8" */
+ case STATE_COLLECTION:
+ break;
+ /* <name>Fedora 8</name> */
+ case STATE_NAME:
+ break;
+ /* <package arch="ppc64" name="imlib-debuginfo" release="6.fc8"
+ * src="http://download.fedoraproject.org/pub/fedora/linux/updates/8/ppc64/imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm"
+ * version="1.9.15">
+ *
+ *
+ * -> patch.conflicts: {name} < {version}.{release}
+ */
+ case STATE_PACKAGE:
+ {
+ const char *arch = 0, *name = 0;
+ Id evr = makeevr_atts(pool, pd, atts); /* parse "epoch", "version", "release" */
+ Id n, a = 0;
+ Id rel_id;
+
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, "arch"))
+ arch = atts[1];
+ else if (!strcmp(*atts, "name"))
+ name = atts[1];
+ }
+ /* generated Id for name */
+ n = pool_str2id(pool, name, 1);
+ rel_id = n;
+ if (arch)
+ {
+ /* generate Id for arch and combine with name */
+ a = pool_str2id(pool, arch, 1);
+ rel_id = pool_rel2id(pool, n, a, REL_ARCH, 1);
+ }
+ rel_id = pool_rel2id(pool, rel_id, evr, REL_LT, 1);
+ solvable->conflicts = repo_addid_dep(pd->repo, solvable->conflicts, rel_id, 0);
+
+ /* who needs the collection anyway? */
+ pd->collhandle = repodata_new_handle(pd->data);
+ repodata_set_id(pd->data, pd->collhandle, UPDATE_COLLECTION_NAME, n);
+ repodata_set_id(pd->data, pd->collhandle, UPDATE_COLLECTION_EVR, evr);
+ if (a)
+ repodata_set_id(pd->data, pd->collhandle, UPDATE_COLLECTION_ARCH, a);
+ break;
+ }
+ /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */
+ /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */
+ case STATE_FILENAME:
+ break;
+ /* <reboot_suggested>True</reboot_suggested> */
+ case STATE_REBOOT:
+ break;
+ /* <restart_suggested>True</restart_suggested> */
+ case STATE_RESTART:
+ break;
+ /* <relogin_suggested>True</relogin_suggested> */
+ case STATE_RELOGIN:
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ Repo *repo = pd->repo;
+
+#if 0
+ fprintf(stderr, "end: %s\n", name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+#if 0
+ fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+#endif
+ return;
+ }
+
+ pd->depth--;
+ pd->statedepth--;
+ switch (pd->state)
+ {
+ case STATE_START:
+ break;
+ case STATE_UPDATES:
+ break;
+ case STATE_UPDATE:
+ s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ if (pd->buildtime)
+ {
+ repodata_set_num(pd->data, pd->handle, SOLVABLE_BUILDTIME, pd->buildtime);
+ pd->buildtime = (time_t)0;
+ }
+ break;
+ case STATE_ID:
+ s->name = pool_str2id(pool, join2(&pd->jd, "patch", ":", pd->content), 1);
+ break;
+ /* <title>imlib-1.9.15-6.fc8</title> */
+ case STATE_TITLE:
+ while (pd->lcontent > 0 && pd->content[pd->lcontent - 1] == '\n')
+ pd->content[--pd->lcontent] = 0;
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_SUMMARY, pd->content);
+ break;
+ case STATE_SEVERITY:
+ repodata_set_poolstr(pd->data, pd->handle, UPDATE_SEVERITY, pd->content);
+ break;
+ case STATE_RIGHTS:
+ repodata_set_poolstr(pd->data, pd->handle, UPDATE_RIGHTS, pd->content);
+ break;
+ /*
+ * <release>Fedora 8</release>
+ */
+ case STATE_RELEASE:
+ break;
+ case STATE_ISSUED:
+ break;
+ case STATE_REFERENCES:
+ break;
+ case STATE_REFERENCE:
+ break;
+ /*
+ * <description>This update ...</description>
+ */
+ case STATE_DESCRIPTION:
+ repodata_set_str(pd->data, pd->handle, SOLVABLE_DESCRIPTION, pd->content);
+ break;
+ /*
+ * <message>Warning! ...</message>
+ */
+ case STATE_MESSAGE:
+ repodata_set_str(pd->data, pd->handle, UPDATE_MESSAGE, pd->content);
+ break;
+ case STATE_PKGLIST:
+ break;
+ case STATE_COLLECTION:
+ break;
+ case STATE_NAME:
+ break;
+ case STATE_PACKAGE:
+ repodata_add_flexarray(pd->data, pd->handle, UPDATE_COLLECTION, pd->collhandle);
+ pd->collhandle = 0;
+ break;
+ /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */
+ /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */
+ case STATE_FILENAME:
+ repodata_set_str(pd->data, pd->collhandle, UPDATE_COLLECTION_FILENAME, pd->content);
+ break;
+ /* <reboot_suggested>True</reboot_suggested> */
+ case STATE_REBOOT:
+ if (pd->content[0] == 'T' || pd->content[0] == 't'|| pd->content[0] == '1')
+ {
+ /* FIXME: this is per-package, the global flag should be computed at runtime */
+ repodata_set_void(pd->data, pd->handle, UPDATE_REBOOT);
+ repodata_set_void(pd->data, pd->collhandle, UPDATE_REBOOT);
+ }
+ break;
+ /* <restart_suggested>True</restart_suggested> */
+ case STATE_RESTART:
+ if (pd->content[0] == 'T' || pd->content[0] == 't'|| pd->content[0] == '1')
+ {
+ /* FIXME: this is per-package, the global flag should be computed at runtime */
+ repodata_set_void(pd->data, pd->handle, UPDATE_RESTART);
+ repodata_set_void(pd->data, pd->collhandle, UPDATE_RESTART);
+ }
+ break;
+ /* <relogin_suggested>True</relogin_suggested> */
+ case STATE_RELOGIN:
+ if (pd->content[0] == 'T' || pd->content[0] == 't'|| pd->content[0] == '1')
+ {
+ /* FIXME: this is per-package, the global flag should be computed at runtime */
+ repodata_set_void(pd->data, pd->handle, UPDATE_RELOGIN);
+ repodata_set_void(pd->data, pd->collhandle, UPDATE_RELOGIN);
+ }
+ break;
+ default:
+ break;
+ }
+
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+}
+
+
+static void XMLCALL
+characterData(void *userData, const XML_Char *s, int len)
+{
+ struct parsedata *pd = userData;
+ int l;
+ char *c;
+
+ if (!pd->docontent)
+ {
+#if 0
+ fprintf(stderr, "Content: [%d]'%.*s'\n", pd->state, len, s);
+#endif
+ 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
+
+int
+repo_add_updateinfoxml(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ struct parsedata pd;
+ char buf[BUFF_SIZE];
+ int i, l;
+ struct stateswitch *sw;
+ Repodata *data;
+ XML_Parser parser;
+
+ data = repo_add_repodata(repo, flags);
+
+ 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.repo = repo;
+ pd.data = data;
+
+ pd.content = malloc(256);
+ pd.acontent = 256;
+ pd.lcontent = 0;
+ 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)
+ {
+ pd.ret = pool_error(pool, -1, "repo_updateinfoxml: %s at line %u:%u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ break;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+ free(pd.content);
+ join_freemem(&pd.jd);
+
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return pd.ret;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_updateinfoxml(Repo *repo, FILE *fp, int flags);
--- /dev/null
+/*
+ * repo_zyppdb.c
+ *
+ * Parses legacy /var/lib/zypp/db/products/... files.
+ * They are old (pre Code11) product descriptions. See bnc#429177
+ *
+ *
+ * Copyright (c) 2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <dirent.h>
+#include <expat.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#define DISABLE_SPLIT
+#include "tools_util.h"
+#include "repo_zyppdb.h"
+
+
+enum state {
+ STATE_START,
+ STATE_PRODUCT,
+ STATE_NAME,
+ STATE_VERSION,
+ STATE_ARCH,
+ STATE_SUMMARY,
+ STATE_VENDOR,
+ STATE_INSTALLTIME,
+ NUMSTATES
+};
+
+struct stateswitch {
+ enum state from;
+ char *ename;
+ enum state to;
+ int docontent;
+};
+
+/* !! must be sorted by first column !! */
+static struct stateswitch stateswitches[] = {
+ { STATE_START, "product", STATE_PRODUCT, 0 },
+ { STATE_PRODUCT, "name", STATE_NAME, 1 },
+ { STATE_PRODUCT, "version", STATE_VERSION, 0 },
+ { STATE_PRODUCT, "arch", STATE_ARCH, 1 },
+ { STATE_PRODUCT, "summary", STATE_SUMMARY, 1 },
+ { STATE_PRODUCT, "install-time", STATE_INSTALLTIME, 1 },
+ { STATE_PRODUCT, "vendor", STATE_VENDOR, 1 },
+ { NUMSTATES }
+};
+
+struct parsedata {
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+ struct joindata jd;
+
+ const char *tmplang;
+
+ Solvable *solvable;
+ Id handle;
+};
+
+
+/*
+ * find_attr
+ * find value for xml attribute
+ * I: txt, name of attribute
+ * I: atts, list of key/value attributes
+ * O: pointer to value of matching key, or NULL
+ *
+ */
+
+static inline const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ }
+ return 0;
+}
+
+
+/*
+ * XML callback: startElement
+ */
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ Solvable *s = pd->solvable;
+ struct stateswitch *sw;
+
+#if 0
+ fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ pd->depth++;
+ if (!pd->swtab[pd->state]) /* no statetable -> no substates */
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#endif
+ return;
+ }
+ for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
+ if (!strcmp(sw->ename, name))
+ break;
+
+ if (sw->from != pd->state)
+ {
+#if 0
+ fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#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_PRODUCT:
+ {
+ /* parse 'type' */
+ const char *type = find_attr("type", atts);
+ s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+ pd->handle = s - pool->solvables;
+ if (type)
+ repodata_set_str(pd->data, pd->handle, PRODUCT_TYPE, type);
+ }
+ break;
+ case STATE_VERSION:
+ {
+ const char *ver = find_attr("ver", atts);
+ const char *rel = find_attr("rel", atts);
+ /* const char *epoch = find_attr("epoch", atts); ignored */
+ s->evr = makeevr(pd->pool, join2(&pd->jd, ver, "-", rel));
+ }
+ break;
+ /* <summary lang="xy">... */
+ case STATE_SUMMARY:
+ pd->tmplang = join_dup(&pd->jd, find_attr("lang", atts));
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+ Solvable *s = pd->solvable;
+
+#if 0
+ fprintf(stderr, "end: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+#if 0
+ fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+#endif
+ return;
+ }
+
+ pd->depth--;
+ pd->statedepth--;
+
+ switch (pd->state)
+ {
+ case STATE_PRODUCT:
+ if (!s->arch)
+ s->arch = ARCH_NOARCH;
+ if (!s->evr)
+ s->evr = ID_EMPTY;
+ if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
+ pd->solvable = 0;
+ break;
+ case STATE_NAME:
+ s->name = pool_str2id(pd->pool, join2(&pd->jd, "product", ":", pd->content), 1);
+ break;
+ case STATE_ARCH:
+ s->arch = pool_str2id(pd->pool, pd->content, 1);
+ break;
+ case STATE_SUMMARY:
+ repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_SUMMARY, pd->tmplang, 1), pd->content);
+ break;
+ case STATE_VENDOR:
+ s->vendor = pool_str2id(pd->pool, pd->content, 1);
+ break;
+ case STATE_INSTALLTIME:
+ repodata_set_num(pd->data, pd->handle, SOLVABLE_INSTALLTIME, atol(pd->content));
+ default:
+ break;
+ }
+
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+
+#if 0
+ fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
+#endif
+}
+
+
+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
+
+
+/*
+ * add single product to repo
+ *
+ */
+
+static void
+add_zyppdb_product(struct parsedata *pd, FILE *fp)
+{
+ char buf[BUFF_SIZE];
+ int l;
+
+ 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)
+ {
+ pool_debug(pd->pool, SOLV_ERROR, "repo_zyppdb: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ if (pd->solvable)
+ {
+ repo_free_solvable(pd->repo, pd->solvable - pd->pool->solvables, 1);
+ pd->solvable = 0;
+ }
+ return;
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+}
+
+
+/*
+ * read all installed products
+ *
+ * parse each one as a product
+ */
+
+int
+repo_add_zyppdb_products(Repo *repo, const char *dirpath, int flags)
+{
+ int i;
+ struct parsedata pd;
+ struct stateswitch *sw;
+ struct dirent *entry;
+ char *fullpath;
+ DIR *dir;
+ FILE *fp;
+ Repodata *data;
+
+ data = repo_add_repodata(repo, flags);
+ memset(&pd, 0, sizeof(pd));
+ pd.repo = repo;
+ pd.pool = repo->pool;
+ pd.data = data;
+
+ pd.content = malloc(256);
+ pd.acontent = 256;
+
+ 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;
+ }
+
+ if (flags & REPO_USE_ROOTDIR)
+ dirpath = pool_prepend_rootdir(repo->pool, dirpath);
+ dir = opendir(dirpath);
+ if (dir)
+ {
+ while ((entry = readdir(dir)))
+ {
+ if (strlen(entry->d_name) < 3)
+ continue; /* skip '.' and '..' */
+ fullpath = join2(&pd.jd, dirpath, "/", entry->d_name);
+ if ((fp = fopen(fullpath, "r")) == 0)
+ {
+ pool_error(repo->pool, 0, "%s: %s", fullpath, strerror(errno));
+ continue;
+ }
+ add_zyppdb_product(&pd, fp);
+ fclose(fp);
+ }
+ }
+ closedir(dir);
+
+ free(pd.content);
+ join_freemem(&pd.jd);
+ if (flags & REPO_USE_ROOTDIR)
+ solv_free((char *)dirpath);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_zyppdb_products(Repo *repo, const char *dirpath, int flags);
--- /dev/null
+/*
+ * Copyright (c) 2013, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* simple and slow rsa/dsa verification code. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+#include "solv_pgpvrfy.h"
+
+typedef unsigned int mp_t;
+typedef unsigned long long mp2_t;
+#define MP_T_BYTES 4
+
+#define MP_T_BITS (MP_T_BYTES * 8)
+
+static inline void
+mpzero(int len, mp_t *target)
+{
+ memset(target, 0, MP_T_BYTES * len);
+}
+
+static inline mp_t *
+mpnew(int len)
+{
+ return solv_calloc(len, MP_T_BYTES);
+}
+
+static inline void
+mpcpy(int len, mp_t *target, mp_t *source)
+{
+ memcpy(target, source, len * MP_T_BYTES);
+}
+
+#if 0
+static void mpdump(int l, mp_t *a, char *s)
+{
+ int i;
+ if (s)
+ fprintf(stderr, "%s", s);
+ for (i = l - 1; i >= 0; i--)
+ fprintf(stderr, "%0*x", MP_T_BYTES * 2, a[i]);
+ fprintf(stderr, "\n");
+}
+#endif
+
+/* target[len] = x, target = target % mod
+ * assumes that target < (mod << MP_T_BITS)! */
+static void
+mpdomod(int len, mp_t *target, mp2_t x, mp_t *mod)
+{
+ int i, j;
+ for (i = len - 1; i >= 0; i--)
+ {
+ x = (x << MP_T_BITS) | target[i];
+ target[i] = 0;
+ if (mod[i])
+ break;
+ }
+ if (i < 0)
+ return;
+ while (x >= 2 * (mp2_t)mod[i])
+ {
+ /* reduce */
+ mp2_t z = x / ((mp2_t)mod[i] + 1);
+ mp2_t n = 0;
+ if ((z >> MP_T_BITS) != 0)
+ z = (mp2_t)1 << MP_T_BITS; /* just in case... */
+ for (j = 0; j < i; j++)
+ {
+ mp_t n2;
+ n += mod[j] * z;
+ n2 = (mp_t)n;
+ n >>= MP_T_BITS;
+ if (n2 > target[j])
+ n++;
+ target[j] -= n2;
+ }
+ n += mod[j] * z;
+ x -= n;
+ }
+ target[i] = x;
+ if (x >= mod[i])
+ {
+ mp_t n;
+ if (x == mod[i])
+ {
+ for (j = i - 1; j >= 0; j--)
+ if (target[j] < mod[j])
+ return;
+ else if (target[j] > mod[j])
+ break;
+ }
+ /* target >= mod, subtract mod */
+ n = 0;
+ for (j = 0; j <= i; j++)
+ {
+ mp2_t n2 = mod[j] + n;
+ n = n2 > target[j] ? 1 : 0;
+ target[j] -= (mp_t)n2;
+ }
+ }
+}
+
+/* target += src * m */
+static void
+mpmult_add_int(int len, mp_t *target, mp_t *src, mp2_t m, mp_t *mod)
+{
+ int i;
+ mp2_t x = 0;
+ for (i = 0; i < len; i++)
+ {
+ x += src[i] * m + target[i];
+ target[i] = x;
+ x >>= MP_T_BITS;
+ }
+ mpdomod(len, target, x, mod);
+}
+
+/* target = target << MP_T_BITS */
+static void
+mpshift(int len, mp_t *target, mp_t *mod)
+{
+ mp_t x;
+ if (len <= 0)
+ return;
+ x = target[len - 1];
+ if (len > 1)
+ memmove(target + 1, target, (len - 1) * MP_T_BYTES);
+ target[0] = 0;
+ mpdomod(len, target, x, mod);
+}
+
+/* target += m1 * m2 */
+static void
+mpmult_add(int len, mp_t *target, mp_t *m1, int m2len, mp_t *m2, mp_t *tmp, mp_t *mod)
+{
+ int i, j;
+ for (j = m2len - 1; j >= 0; j--)
+ if (m2[j])
+ break;
+ if (j < 0)
+ return;
+ mpcpy(len, tmp, m1);
+ for (i = 0; i < j; i++)
+ {
+ if (m2[i])
+ mpmult_add_int(len, target, tmp, m2[i], mod);
+ mpshift(len, tmp, mod);
+ }
+ if (m2[i])
+ mpmult_add_int(len, target, tmp, m2[i], mod);
+}
+
+/* target = target * m */
+static void
+mpmult_inplace(int len, mp_t *target, mp_t *m, mp_t *tmp1, mp_t *tmp2, mp_t *mod)
+{
+ mpzero(len, tmp1);
+ mpmult_add(len, tmp1, target, len, m, tmp2, mod);
+ mpcpy(len, target, tmp1);
+}
+
+/* target = target ^ 16 * b ^ e */
+static void
+mppow_int(int len, mp_t *target, mp_t *t, mp_t *mod, int e)
+{
+ mp_t *t2 = t + len * 16;
+ mpmult_inplace(len, target, target, t, t2, mod);
+ mpmult_inplace(len, target, target, t, t2, mod);
+ mpmult_inplace(len, target, target, t, t2, mod);
+ mpmult_inplace(len, target, target, t, t2, mod);
+ if (e)
+ mpmult_inplace(len, target, t + len * e, t, t2, mod);
+}
+
+/* target = b ^ e (b has to be < mod) */
+static void
+mppow(int len, mp_t *target, mp_t *b, int elen, mp_t *e, mp_t *mod)
+{
+ int i, j;
+ mp_t *t;
+ mpzero(len, target);
+ target[0] = 1;
+ for (i = elen - 1; i >= 0; i--)
+ if (e[i])
+ break;
+ if (i < 0)
+ return;
+ t = mpnew(len * 17);
+ mpcpy(len, t + len, b);
+ for (j = 2; j < 16; j++)
+ mpmult_add(len, t + len * j, b, len, t + len * j - len, t + len * 16, mod);
+ for (; i >= 0; i--)
+ {
+#if MP_T_BYTES == 4
+ mppow_int(len, target, t, mod, (e[i] >> 28) & 0x0f);
+ mppow_int(len, target, t, mod, (e[i] >> 24) & 0x0f);
+ mppow_int(len, target, t, mod, (e[i] >> 20) & 0x0f);
+ mppow_int(len, target, t, mod, (e[i] >> 16) & 0x0f);
+ mppow_int(len, target, t, mod, (e[i] >> 12) & 0x0f);
+ mppow_int(len, target, t, mod, (e[i] >> 8) & 0x0f);
+ mppow_int(len, target, t, mod, (e[i] >> 4) & 0x0f);
+ mppow_int(len, target, t, mod, e[i] & 0x0f);
+#elif MP_T_BYTES == 1
+ mppow_int(len, target, t, mod, (e[i] >> 4) & 0x0f);
+ mppow_int(len, target, t, mod, e[i] & 0x0f);
+#endif
+ }
+ free(t);
+}
+
+/* target = m1 * m2 (m1 has to be < mod) */
+static void
+mpmult(int len, mp_t *target, mp_t *m1, int m2len, mp_t *m2, mp_t *mod)
+{
+ mp_t *tmp = mpnew(len);
+ mpzero(len, target);
+ mpmult_add(len, target, m1, m2len, m2, tmp, mod);
+ free(tmp);
+}
+
+static int
+mpisless(int len, mp_t *a, mp_t *b)
+{
+ int i;
+ for (i = len - 1; i >= 0; i--)
+ if (a[i] < b[i])
+ return 1;
+ else if (a[i] > b[i])
+ return 0;
+ return 0;
+}
+
+static int
+mpiszero(int len, mp_t *a)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ if (a[i])
+ return 0;
+ return 1;
+}
+
+static void
+mpdec(int len, mp_t *a)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ if (a[i]--)
+ return;
+ else
+ a[i] = -(mp_t)1;
+}
+
+static int
+mpdsa(int pl, mp_t *p, int ql, mp_t *q, mp_t *g, mp_t *y, mp_t *r, mp_t *s, int hl, mp_t *h)
+{
+ mp_t *w;
+ mp_t *tmp;
+ mp_t *u1, *u2;
+ mp_t *gu1, *yu2;
+#if 0
+ mpdump(pl, p, "p = ");
+ mpdump(ql, q, "q = ");
+ mpdump(pl, g, "g = ");
+ mpdump(pl, y, "y = ");
+ mpdump(ql, r, "r = ");
+ mpdump(ql, s, "s = ");
+ mpdump(hl, h, "h = ");
+#endif
+ if (pl < ql || !mpisless(pl, g, p) || !mpisless(pl, y, p))
+ return 0; /* hmm, bad pubkey? */
+ if (!mpisless(ql, r, q) || mpiszero(ql, r))
+ return 0;
+ if (!mpisless(ql, s, q) || mpiszero(ql, s))
+ return 0;
+ tmp = mpnew(pl); /* note pl */
+ mpcpy(ql, tmp, q); /* tmp = q */
+ mpdec(ql, tmp); /* tmp-- */
+ mpdec(ql, tmp); /* tmp-- */
+ w = mpnew(ql);
+ mppow(ql, w, s, ql, tmp, q); /* w = s ^ tmp (s ^ -1) */
+ u1 = mpnew(pl); /* note pl */
+ /* order is important here: h can be >= q */
+ mpmult(ql, u1, w, hl, h, q); /* u1 = w * h */
+ u2 = mpnew(ql); /* u2 = 0 */
+ mpmult(ql, u2, w, ql, r, q); /* u2 = w * r */
+ free(w);
+ gu1 = mpnew(pl);
+ yu2 = mpnew(pl);
+ mppow(pl, gu1, g, ql, u1, p); /* gu1 = g ^ u1 */
+ mppow(pl, yu2, y, ql, u2, p); /* yu2 = y ^ u2 */
+ mpmult(pl, u1, gu1, pl, yu2, p); /* u1 = gu1 * yu2 */
+ free(gu1);
+ free(yu2);
+ mpzero(ql, u2);
+ u2[0] = 1; /* u2 = 1 */
+ mpmult(ql, tmp, u2, pl, u1, q); /* tmp = u2 * u1 */
+ free(u1);
+ free(u2);
+#if 0
+ mpdump(ql, tmp, "res = ");
+#endif
+ if (memcmp(tmp, r, ql * MP_T_BYTES) != 0)
+ {
+ free(tmp);
+ return 0;
+ }
+ free(tmp);
+ return 1;
+}
+
+static int
+mprsa(int nl, mp_t *n, int el, mp_t *e, mp_t *m, mp_t *c)
+{
+ mp_t *tmp;
+#if 0
+ mpdump(nl, n, "n = ");
+ mpdump(el, e, "e = ");
+ mpdump(nl, m, "m = ");
+ mpdump(nl, c, "c = ");
+#endif
+ if (!mpisless(nl, m, n))
+ return 0;
+ if (!mpisless(nl, c, n))
+ return 0;
+ tmp = mpnew(nl);
+ mppow(nl, tmp, m, el, e, n); /* tmp = m ^ e */
+#if 0
+ mpdump(nl, tmp, "res = ");
+#endif
+ if (memcmp(tmp, c, nl * MP_T_BYTES) != 0)
+ {
+ free(tmp);
+ return 0;
+ }
+ free(tmp);
+ return 1;
+}
+
+/* create mp with size tbits from data with size dbits */
+static mp_t *
+mpbuild(const unsigned char *d, int dbits, int tbits, int *mplp)
+{
+ int l = (tbits + MP_T_BITS - 1) / MP_T_BITS;
+ int dl, i;
+
+ mp_t *out = mpnew(l ? l : 1);
+ if (mplp)
+ *mplp = l;
+ dl = (dbits + 7) / 8;
+ d += dl;
+ if (dbits > tbits)
+ dl = (tbits + 7) / 8;
+ for (i = 0; dl > 0; dl--, i++)
+ {
+ int x = *--d;
+ out[i / MP_T_BYTES] |= x << (8 * (i % MP_T_BYTES));
+ }
+ return out;
+}
+
+static const unsigned char *
+findmpi(const unsigned char **mpip, int *mpilp, int maxbits, int *outlen)
+{
+ int mpil = *mpilp;
+ const unsigned char *mpi = *mpip;
+ int bits, l;
+
+ *outlen = 0;
+ if (mpil < 2)
+ return 0;
+ bits = mpi[0] << 8 | mpi[1];
+ l = 2 + (bits + 7) / 8;
+ if (bits > maxbits || mpil < l || (bits && !mpi[2]))
+ {
+ *mpilp = 0;
+ return 0;
+ }
+ *outlen = bits;
+ *mpilp = mpil - l;
+ *mpip = mpi + l;
+ return mpi + 2;
+}
+
+int
+solv_pgpvrfy(const unsigned char *pub, int publ, const unsigned char *sig, int sigl)
+{
+ int hashl;
+ unsigned char *oid = 0;
+ const unsigned char *mpi;
+ int mpil;
+ int res = 0;
+
+ if (!pub || !sig || publ < 1 || sigl < 2)
+ return 0;
+ if (pub[0] != sig[0])
+ return 0; /* key algo mismatch */
+ switch(sig[1])
+ {
+ case 1:
+ hashl = 16; /* MD5 */
+ oid = (unsigned char *)"\022\060\040\060\014\006\010\052\206\110\206\367\015\002\005\005\000\004\020";
+ break;
+ case 2:
+ hashl = 20; /* SHA-1 */
+ oid = (unsigned char *)"\017\060\041\060\011\006\005\053\016\003\002\032\005\000\004\024";
+ break;
+ case 8:
+ hashl = 32; /* SHA-256 */
+ oid = (unsigned char *)"\023\060\061\060\015\006\011\140\206\110\001\145\003\004\002\001\005\000\004\040";
+ break;
+ case 9:
+ hashl = 48; /* SHA-384 */
+ oid = (unsigned char *)"\023\060\101\060\015\006\011\140\206\110\001\145\003\004\002\002\005\000\004\060";
+ break;
+ case 10:
+ hashl = 64; /* SHA-512 */
+ oid = (unsigned char *)"\023\060\121\060\015\006\011\140\206\110\001\145\003\004\002\003\005\000\004\100";
+ break;
+ case 11:
+ hashl = 28; /* SHA-224 */
+ oid = (unsigned char *)"\023\060\061\060\015\006\011\140\206\110\001\145\003\004\002\004\005\000\004\034";
+ break;
+ default:
+ return 0; /* unsupported hash algo */
+ }
+ if (sigl < 2 + hashl)
+ return 0;
+ switch (pub[0])
+ {
+ case 1: /* RSA */
+ {
+ const unsigned char *n, *e, *m;
+ unsigned char *c;
+ int nlen, elen, mlen, clen;
+ mp_t *nx, *ex, *mx, *cx;
+ int nxl, exl;
+
+ mpi = pub + 1;
+ mpil = publ - 1;
+ n = findmpi(&mpi, &mpil, 8192, &nlen);
+ e = findmpi(&mpi, &mpil, 1024, &elen);
+ mpi = sig + 2 + hashl;
+ mpil = sigl - (2 + hashl);
+ m = findmpi(&mpi, &mpil, nlen, &mlen);
+ if (!n || !e || !m || !nlen || !elen)
+ return 0;
+ /* build padding block */
+ clen = (nlen - 1) / 8;
+ if (hashl + *oid + 2 > clen)
+ return 0;
+ c = solv_malloc(clen);
+ memset(c, 0xff, clen);
+ c[0] = 1;
+ memcpy(c + clen - hashl, sig + 2, hashl);
+ memcpy(c + clen - hashl - *oid, oid + 1, *oid);
+ c[clen - hashl - *oid - 1] = 0;
+ clen = clen * 8 - 7; /* always <= nlen */
+ nx = mpbuild(n, nlen, nlen, &nxl);
+ ex = mpbuild(e, elen, elen, &exl);
+ mx = mpbuild(m, mlen, nlen, 0);
+ cx = mpbuild(c, clen, nlen, 0);
+ free(c);
+ res = mprsa(nxl, nx, exl, ex, mx, cx);
+ free(nx);
+ free(ex);
+ free(mx);
+ free(cx);
+ break;
+ }
+ case 17: /* DSA */
+ {
+ const unsigned char *p, *q, *g, *y, *r, *s;
+ int plen, qlen, glen, ylen, rlen, slen, hlen;
+ mp_t *px, *qx, *gx, *yx, *rx, *sx, *hx;
+ int pxl, qxl, hxl;
+
+ mpi = pub + 1;
+ mpil = publ - 1;
+ p = findmpi(&mpi, &mpil, 8192, &plen);
+ q = findmpi(&mpi, &mpil, 1024, &qlen);
+ g = findmpi(&mpi, &mpil, plen, &glen);
+ y = findmpi(&mpi, &mpil, plen, &ylen);
+ mpi = sig + 2 + hashl;
+ mpil = sigl - (2 + hashl);
+ r = findmpi(&mpi, &mpil, qlen, &rlen);
+ s = findmpi(&mpi, &mpil, qlen, &slen);
+ if (!p || !q || !g || !y || !r || !s || !plen || !qlen)
+ return 0;
+ hlen = (qlen + 7) & ~7;
+ if (hlen > hashl * 8)
+ return 0;
+ px = mpbuild(p, plen, plen, &pxl);
+ qx = mpbuild(q, qlen, qlen, &qxl);
+ gx = mpbuild(g, glen, plen, 0);
+ yx = mpbuild(y, ylen, plen, 0);
+ rx = mpbuild(r, rlen, qlen, 0);
+ sx = mpbuild(s, slen, qlen, 0);
+ hx = mpbuild(sig + 2, hlen, hlen, &hxl);
+ res = mpdsa(pxl, px, qxl, qx, gx, yx, rx, sx, hxl, hx);
+ free(px);
+ free(qx);
+ free(gx);
+ free(yx);
+ free(rx);
+ free(sx);
+ free(hx);
+ break;
+ }
+ default:
+ return 0; /* unsupported pubkey algo */
+ }
+ return res;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2013, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int solv_pgpvrfy(const unsigned char *pub, int publ, const unsigned char *sig, int sigl);
+
--- /dev/null
+/*
+ * Copyright (c) 2011, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <fcntl.h>
+
+#include "solv_xfopen.h"
+#include "util.h"
+
+
+/* Evil hack for Haiku: fopencookie() is implemented internally, but not
+ exported by a header. */
+#ifdef __HAIKU__
+
+typedef struct {
+ ssize_t (*read)(void*, char*, size_t);
+ ssize_t (*write)(void*, const char*, size_t);
+ int (*seek)(off_t*, int);
+ int (*close)(void*);
+} cookie_io_functions_t;
+
+
+FILE *fopencookie(void*, const char*, cookie_io_functions_t);
+
+#endif /* __HAIKU__ */
+
+
+static FILE *cookieopen(void *cookie, const char *mode,
+ ssize_t (*cread)(void *, char *, size_t),
+ ssize_t (*cwrite)(void *, const char *, size_t),
+ int (*cclose)(void *))
+{
+#ifdef HAVE_FUNOPEN
+ if (!cookie)
+ return 0;
+ return funopen(cookie,
+ (int (*)(void *, char *, int))(*mode == 'r' ? cread : NULL), /* readfn */
+ (int (*)(void *, const char *, int))(*mode == 'w' ? cwrite : NULL), /* writefn */
+ (fpos_t (*)(void *, fpos_t, int))NULL, /* seekfn */
+ cclose
+ );
+#elif defined(HAVE_FOPENCOOKIE)
+ cookie_io_functions_t cio;
+
+ if (!cookie)
+ return 0;
+ memset(&cio, 0, sizeof(cio));
+ if (*mode == 'r')
+ cio.read = cread;
+ else if (*mode == 'w')
+ cio.write = cwrite;
+ cio.close = cclose;
+ return fopencookie(cookie, *mode == 'w' ? "w" : "r", cio);
+#else
+# error Need to implement custom I/O
+#endif
+}
+
+
+/* gzip compression */
+
+static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
+{
+ return gzread((gzFile)cookie, buf, nbytes);
+}
+
+static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
+{
+ return gzwrite((gzFile)cookie, buf, nbytes);
+}
+
+static int cookie_gzclose(void *cookie)
+{
+ return gzclose((gzFile)cookie);
+}
+
+static inline FILE *mygzfopen(const char *fn, const char *mode)
+{
+ gzFile gzf = gzopen(fn, mode);
+ return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
+}
+
+static inline FILE *mygzfdopen(int fd, const char *mode)
+{
+ gzFile gzf = gzdopen(fd, mode);
+ return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
+}
+
+#ifdef ENABLE_BZIP2_COMPRESSION
+
+#include <bzlib.h>
+
+/* bzip2 compression */
+
+static ssize_t cookie_bzread(void *cookie, char *buf, size_t nbytes)
+{
+ return BZ2_bzread((BZFILE *)cookie, buf, nbytes);
+}
+
+static ssize_t cookie_bzwrite(void *cookie, const char *buf, size_t nbytes)
+{
+ return BZ2_bzwrite((BZFILE *)cookie, (char *)buf, nbytes);
+}
+
+static int cookie_bzclose(void *cookie)
+{
+ BZ2_bzclose((BZFILE *)cookie);
+ return 0;
+}
+
+static inline FILE *mybzfopen(const char *fn, const char *mode)
+{
+ BZFILE *bzf = BZ2_bzopen(fn, mode);
+ return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
+}
+
+static inline FILE *mybzfdopen(int fd, const char *mode)
+{
+ BZFILE *bzf = BZ2_bzdopen(fd, mode);
+ return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
+}
+
+#endif
+
+
+#ifdef ENABLE_LZMA_COMPRESSION
+
+#include <lzma.h>
+
+/* lzma code written by me in 2008 for rpm's rpmio.c */
+
+typedef struct lzfile {
+ unsigned char buf[1 << 15];
+ lzma_stream strm;
+ FILE *file;
+ int encoding;
+ int eof;
+} LZFILE;
+
+static inline lzma_ret setup_alone_encoder(lzma_stream *strm, int level)
+{
+ lzma_options_lzma options;
+ lzma_lzma_preset(&options, level);
+ return lzma_alone_encoder(strm, &options);
+}
+
+static lzma_stream stream_init = LZMA_STREAM_INIT;
+
+static LZFILE *lzopen(const char *path, const char *mode, int fd, int isxz)
+{
+ int level = 7;
+ int encoding = 0;
+ FILE *fp;
+ LZFILE *lzfile;
+ lzma_ret ret;
+
+ if (!path && fd < 0)
+ return 0;
+ for (; *mode; mode++)
+ {
+ if (*mode == 'w')
+ encoding = 1;
+ else if (*mode == 'r')
+ encoding = 0;
+ else if (*mode >= '1' && *mode <= '9')
+ level = *mode - '0';
+ }
+ if (fd != -1)
+ fp = fdopen(fd, encoding ? "w" : "r");
+ else
+ fp = fopen(path, encoding ? "w" : "r");
+ if (!fp)
+ return 0;
+ lzfile = calloc(1, sizeof(*lzfile));
+ if (!lzfile)
+ {
+ fclose(fp);
+ return 0;
+ }
+ lzfile->file = fp;
+ lzfile->encoding = encoding;
+ lzfile->eof = 0;
+ lzfile->strm = stream_init;
+ if (encoding)
+ {
+ if (isxz)
+ ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
+ else
+ ret = setup_alone_encoder(&lzfile->strm, level);
+ }
+ else
+ ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
+ if (ret != LZMA_OK)
+ {
+ fclose(fp);
+ free(lzfile);
+ return 0;
+ }
+ return lzfile;
+}
+
+static int lzclose(void *cookie)
+{
+ LZFILE *lzfile = cookie;
+ lzma_ret ret;
+ size_t n;
+ int rc;
+
+ if (!lzfile)
+ return -1;
+ if (lzfile->encoding)
+ {
+ for (;;)
+ {
+ lzfile->strm.avail_out = sizeof(lzfile->buf);
+ lzfile->strm.next_out = lzfile->buf;
+ ret = lzma_code(&lzfile->strm, LZMA_FINISH);
+ if (ret != LZMA_OK && ret != LZMA_STREAM_END)
+ return -1;
+ n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
+ if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
+ return -1;
+ if (ret == LZMA_STREAM_END)
+ break;
+ }
+ }
+ lzma_end(&lzfile->strm);
+ rc = fclose(lzfile->file);
+ free(lzfile);
+ return rc;
+}
+
+static ssize_t lzread(void *cookie, char *buf, size_t len)
+{
+ LZFILE *lzfile = cookie;
+ lzma_ret ret;
+ int eof = 0;
+
+ if (!lzfile || lzfile->encoding)
+ return -1;
+ if (lzfile->eof)
+ return 0;
+ lzfile->strm.next_out = (unsigned char *)buf;
+ lzfile->strm.avail_out = len;
+ for (;;)
+ {
+ if (!lzfile->strm.avail_in)
+ {
+ lzfile->strm.next_in = lzfile->buf;
+ lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
+ if (!lzfile->strm.avail_in)
+ eof = 1;
+ }
+ ret = lzma_code(&lzfile->strm, LZMA_RUN);
+ if (ret == LZMA_STREAM_END)
+ {
+ lzfile->eof = 1;
+ return len - lzfile->strm.avail_out;
+ }
+ if (ret != LZMA_OK)
+ return -1;
+ if (!lzfile->strm.avail_out)
+ return len;
+ if (eof)
+ return -1;
+ }
+}
+
+static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
+{
+ LZFILE *lzfile = cookie;
+ lzma_ret ret;
+ size_t n;
+ if (!lzfile || !lzfile->encoding)
+ return -1;
+ if (!len)
+ return 0;
+ lzfile->strm.next_in = (unsigned char *)buf;
+ lzfile->strm.avail_in = len;
+ for (;;)
+ {
+ lzfile->strm.next_out = lzfile->buf;
+ lzfile->strm.avail_out = sizeof(lzfile->buf);
+ ret = lzma_code(&lzfile->strm, LZMA_RUN);
+ if (ret != LZMA_OK)
+ return -1;
+ n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
+ if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
+ return -1;
+ if (!lzfile->strm.avail_in)
+ return len;
+ }
+}
+
+static inline FILE *myxzfopen(const char *fn, const char *mode)
+{
+ LZFILE *lzf = lzopen(fn, mode, -1, 1);
+ return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
+}
+
+static inline FILE *myxzfdopen(int fd, const char *mode)
+{
+ LZFILE *lzf = lzopen(0, mode, fd, 1);
+ return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
+}
+
+static inline FILE *mylzfopen(const char *fn, const char *mode)
+{
+ LZFILE *lzf = lzopen(fn, mode, -1, 0);
+ return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
+}
+
+static inline FILE *mylzfdopen(int fd, const char *mode)
+{
+ LZFILE *lzf = lzopen(0, mode, fd, 0);
+ return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
+}
+
+#endif /* ENABLE_LZMA_COMPRESSION */
+
+
+FILE *
+solv_xfopen(const char *fn, const char *mode)
+{
+ char *suf;
+
+ if (!fn)
+ return 0;
+ if (!mode)
+ mode = "r";
+ suf = strrchr(fn, '.');
+ if (suf && !strcmp(suf, ".gz"))
+ return mygzfopen(fn, mode);
+#ifdef ENABLE_LZMA_COMPRESSION
+ if (suf && !strcmp(suf, ".xz"))
+ return myxzfopen(fn, mode);
+ if (suf && !strcmp(suf, ".lzma"))
+ return mylzfopen(fn, mode);
+#else
+ if (suf && !strcmp(suf, ".xz"))
+ return 0;
+ if (suf && !strcmp(suf, ".lzma"))
+ return 0;
+#endif
+#ifdef ENABLE_BZIP2_COMPRESSION
+ if (suf && !strcmp(suf, ".bz2"))
+ return mybzfopen(fn, mode);
+#else
+ if (suf && !strcmp(suf, ".bz2"))
+ return 0;
+#endif
+ return fopen(fn, mode);
+}
+
+FILE *
+solv_xfopen_fd(const char *fn, int fd, const char *mode)
+{
+ const char *simplemode = mode;
+ char *suf;
+
+ suf = fn ? strrchr(fn, '.') : 0;
+ if (!mode)
+ {
+ int fl = fcntl(fd, F_GETFL, 0);
+ if (fl == -1)
+ return 0;
+ fl &= O_RDONLY|O_WRONLY|O_RDWR;
+ if (fl == O_WRONLY)
+ mode = simplemode = "w";
+ else if (fl == O_RDWR)
+ {
+ mode = "r+";
+ simplemode = "r";
+ }
+ else
+ mode = simplemode = "r";
+ }
+ if (suf && !strcmp(suf, ".gz"))
+ return mygzfdopen(fd, simplemode);
+#ifdef ENABLE_LZMA_COMPRESSION
+ if (suf && !strcmp(suf, ".xz"))
+ return myxzfdopen(fd, simplemode);
+ if (suf && !strcmp(suf, ".lzma"))
+ return mylzfdopen(fd, simplemode);
+#else
+ if (suf && !strcmp(suf, ".xz"))
+ return 0;
+ if (suf && !strcmp(suf, ".lzma"))
+ return 0;
+#endif
+#ifdef ENABLE_BZIP2_COMPRESSION
+ if (suf && !strcmp(suf, ".bz2"))
+ return mybzfdopen(fd, simplemode);
+#else
+ if (suf && !strcmp(suf, ".bz2"))
+ return 0;
+#endif
+ return fdopen(fd, mode);
+}
+
+int
+solv_xfopen_iscompressed(const char *fn)
+{
+ const char *suf = fn ? strrchr(fn, '.') : 0;
+ if (!suf)
+ return 0;
+ if (!strcmp(suf, ".gz"))
+ return 1;
+ if (!strcmp(suf, ".xz") || !strcmp(suf, ".lzma"))
+#ifdef ENABLE_LZMA_COMPRESSION
+ return 1;
+#else
+ return -1;
+#endif
+ if (!strcmp(suf, ".bz2"))
+#ifdef ENABLE_BZIP2_COMPRESSION
+ return 1;
+#else
+ return -1;
+#endif
+ return 0;
+}
+
+struct bufcookie {
+ char **bufp;
+ size_t *buflp;
+ char *freemem;
+ size_t bufl_int;
+};
+
+static ssize_t cookie_bufread(void *cookie, char *buf, size_t nbytes)
+{
+ struct bufcookie *bc = cookie;
+ size_t n = *bc->buflp > nbytes ? nbytes : *bc->buflp;
+ if (n)
+ {
+ memcpy(buf, *bc->bufp, n);
+ *bc->bufp += n;
+ *bc->buflp -= n;
+ }
+ return n;
+}
+
+static ssize_t cookie_bufwrite(void *cookie, const char *buf, size_t nbytes)
+{
+ struct bufcookie *bc = cookie;
+ int n = nbytes > 0x40000000 ? 0x40000000 : nbytes;
+ if (n)
+ {
+ *bc->bufp = solv_extend(*bc->bufp, *bc->buflp, n + 1, 1, 4095);
+ memcpy(*bc->bufp, buf, n);
+ (*bc->bufp)[n] = 0; /* zero-terminate */
+ *bc->buflp += n;
+ }
+ return n;
+}
+
+static int cookie_bufclose(void *cookie)
+{
+ struct bufcookie *bc = cookie;
+ if (bc->freemem)
+ solv_free(bc->freemem);
+ solv_free(bc);
+ return 0;
+}
+
+FILE *
+solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
+{
+ struct bufcookie *bc;
+ FILE *fp;
+ if (*mode != 'r' && *mode != 'w')
+ return 0;
+ bc = solv_calloc(1, sizeof(*bc));
+ bc->freemem = 0;
+ bc->bufp = bufp;
+ if (!buflp)
+ {
+ bc->bufl_int = *mode == 'w' ? 0 : strlen(*bufp);
+ buflp = &bc->bufl_int;
+ }
+ bc->buflp = buflp;
+ if (*mode == 'w')
+ {
+ *bc->bufp = solv_extend(0, 0, 1, 1, 4095); /* always zero-terminate */
+ (*bc->bufp)[0] = 0;
+ *bc->buflp = 0;
+ }
+ fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
+ if (!strcmp(mode, "rf")) /* auto-free */
+ bc->freemem = *bufp;
+ if (!fp)
+ {
+ if (*mode == 'w')
+ *bc->bufp = solv_free(*bc->bufp);
+ cookie_bufclose(bc);
+ }
+ return fp;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009-2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef SOLV_XFOPEN_H
+#define SOLV_XFOPEN_H
+
+extern FILE *solv_xfopen(const char *fn, const char *mode);
+extern FILE *solv_xfopen_fd(const char *fn, int fd, const char *mode);
+extern FILE *solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode);
+extern int solv_xfopen_iscompressed(const char *fn);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "poolarch.h"
+#include "poolvendor.h"
+#include "repo.h"
+#include "repo_solv.h"
+#include "solver.h"
+#include "solverdebug.h"
+#include "chksum.h"
+#include "testcase.h"
+#include "selection.h"
+#include "solv_xfopen.h"
+
+#define DISABLE_JOIN2
+#include "tools_util.h"
+
+static struct job2str {
+ Id job;
+ const char *str;
+} job2str[] = {
+ { SOLVER_NOOP, "noop" },
+ { SOLVER_INSTALL, "install" },
+ { SOLVER_ERASE, "erase" },
+ { SOLVER_UPDATE, "update" },
+ { SOLVER_WEAKENDEPS, "weakendeps" },
+ { SOLVER_MULTIVERSION, "multiversion" },
+ { SOLVER_MULTIVERSION, "noobsoletes" }, /* old name */
+ { SOLVER_LOCK, "lock" },
+ { SOLVER_DISTUPGRADE, "distupgrade" },
+ { SOLVER_VERIFY, "verify" },
+ { SOLVER_DROP_ORPHANED, "droporphaned" },
+ { SOLVER_USERINSTALLED, "userinstalled" },
+ { SOLVER_ALLOWUNINSTALL, "allowuninstall" },
+ { 0, 0 }
+};
+
+static struct jobflags2str {
+ Id flag;
+ const char *str;
+} jobflags2str[] = {
+ { SOLVER_WEAK, "weak" },
+ { SOLVER_ESSENTIAL, "essential" },
+ { SOLVER_CLEANDEPS, "cleandeps" },
+ { SOLVER_ORUPDATE, "orupdate" },
+ { SOLVER_FORCEBEST, "forcebest" },
+ { SOLVER_TARGETED, "targeted" },
+ { SOLVER_NOTBYUSER, "notbyuser" },
+ { SOLVER_SETEV, "setev" },
+ { SOLVER_SETEVR, "setevr" },
+ { SOLVER_SETARCH, "setarch" },
+ { SOLVER_SETVENDOR, "setvendor" },
+ { SOLVER_SETREPO, "setrepo" },
+ { SOLVER_NOAUTOSET, "noautoset" },
+ { 0, 0 }
+};
+
+static struct resultflags2str {
+ Id flag;
+ const char *str;
+} resultflags2str[] = {
+ { TESTCASE_RESULT_TRANSACTION, "transaction" },
+ { TESTCASE_RESULT_PROBLEMS, "problems" },
+ { TESTCASE_RESULT_ORPHANED, "orphaned" },
+ { TESTCASE_RESULT_RECOMMENDED, "recommended" },
+ { TESTCASE_RESULT_UNNEEDED, "unneeded" },
+ { TESTCASE_RESULT_ALTERNATIVES, "alternatives" },
+ { TESTCASE_RESULT_RULES, "rules" },
+ { TESTCASE_RESULT_GENID, "genid" },
+ { 0, 0 }
+};
+
+static struct solverflags2str {
+ Id flag;
+ const char *str;
+ int def;
+} solverflags2str[] = {
+ { SOLVER_FLAG_ALLOW_DOWNGRADE, "allowdowngrade", 0 },
+ { SOLVER_FLAG_ALLOW_NAMECHANGE, "allownamechange", 1 },
+ { SOLVER_FLAG_ALLOW_ARCHCHANGE, "allowarchchange", 0 },
+ { SOLVER_FLAG_ALLOW_VENDORCHANGE, "allowvendorchange", 0 },
+ { SOLVER_FLAG_ALLOW_UNINSTALL, "allowuninstall", 0 },
+ { SOLVER_FLAG_NO_UPDATEPROVIDE, "noupdateprovide", 0 },
+ { SOLVER_FLAG_SPLITPROVIDES, "splitprovides", 0 },
+ { SOLVER_FLAG_IGNORE_RECOMMENDED, "ignorerecommended", 0 },
+ { SOLVER_FLAG_ADD_ALREADY_RECOMMENDED, "addalreadyrecommended", 0 },
+ { SOLVER_FLAG_NO_INFARCHCHECK, "noinfarchcheck", 0 },
+ { SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES, "keepexplicitobsoletes", 0 },
+ { SOLVER_FLAG_BEST_OBEY_POLICY, "bestobeypolicy", 0 },
+ { SOLVER_FLAG_NO_AUTOTARGET, "noautotarget", 0 },
+ { SOLVER_FLAG_DUP_ALLOW_DOWNGRADE, "dupallowdowngrade", 1 },
+ { SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE, "dupallowarchchange", 1 },
+ { SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE, "dupallowvendorchange", 1 },
+ { SOLVER_FLAG_DUP_ALLOW_NAMECHANGE, "dupallownamechange", 1 },
+ { SOLVER_FLAG_KEEP_ORPHANS, "keeporphans", 0 },
+ { SOLVER_FLAG_BREAK_ORPHANS, "breakorphans", 0 },
+ { SOLVER_FLAG_FOCUS_INSTALLED, "focusinstalled", 0 },
+ { SOLVER_FLAG_YUM_OBSOLETES, "yumobsoletes", 0 },
+ { SOLVER_FLAG_NEED_UPDATEPROVIDE, "needupdateprovide", 0 },
+ { 0, 0, 0 }
+};
+
+static struct poolflags2str {
+ Id flag;
+ const char *str;
+ int def;
+} poolflags2str[] = {
+ { POOL_FLAG_PROMOTEEPOCH, "promoteepoch", 0 },
+ { POOL_FLAG_FORBIDSELFCONFLICTS, "forbidselfconflicts", 0 },
+ { POOL_FLAG_OBSOLETEUSESPROVIDES, "obsoleteusesprovides", 0 },
+ { POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES, "implicitobsoleteusesprovides", 0 },
+ { POOL_FLAG_OBSOLETEUSESCOLORS, "obsoleteusescolors", 0 },
+ { POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS, "implicitobsoleteusescolors", 0 },
+ { POOL_FLAG_NOINSTALLEDOBSOLETES, "noinstalledobsoletes", 0 },
+ { POOL_FLAG_HAVEDISTEPOCH, "havedistepoch", 0 },
+ { POOL_FLAG_NOOBSOLETESMULTIVERSION, "noobsoletesmultiversion", 0 },
+ { POOL_FLAG_ADDFILEPROVIDESFILTERED, "addfileprovidesfiltered", 0 },
+ { POOL_FLAG_NOWHATPROVIDESAUX, "nowhatprovidesaux", 0 },
+ { 0, 0, 0 }
+};
+
+static struct disttype2str {
+ Id type;
+ const char *str;
+} disttype2str[] = {
+ { DISTTYPE_RPM, "rpm" },
+ { DISTTYPE_DEB, "deb" },
+ { DISTTYPE_ARCH, "arch" },
+ { DISTTYPE_HAIKU, "haiku" },
+ { 0, 0 }
+};
+
+static struct selflags2str {
+ Id flag;
+ const char *str;
+} selflags2str[] = {
+ { SELECTION_NAME, "name" },
+ { SELECTION_PROVIDES, "provides" },
+ { SELECTION_FILELIST, "filelist" },
+ { SELECTION_CANON, "canon" },
+ { SELECTION_DOTARCH, "dotarch" },
+ { SELECTION_REL, "rel" },
+ { SELECTION_INSTALLED_ONLY, "installedonly" },
+ { SELECTION_GLOB, "glob" },
+ { SELECTION_FLAT, "flat" },
+ { SELECTION_NOCASE, "nocase" },
+ { SELECTION_SOURCE_ONLY, "sourceonly" },
+ { SELECTION_WITH_SOURCE, "withsource" },
+ { 0, 0 }
+};
+
+static const char *features[] = {
+#ifdef ENABLE_LINKED_PKGS
+ "linked_packages",
+#endif
+#ifdef ENABLE_COMPLEX_DEPS
+ "complex_deps",
+#endif
+ 0
+};
+
+typedef struct strqueue {
+ char **str;
+ int nstr;
+} Strqueue;
+
+#define STRQUEUE_BLOCK 63
+
+static void
+strqueue_init(Strqueue *q)
+{
+ q->str = 0;
+ q->nstr = 0;
+}
+
+static void
+strqueue_free(Strqueue *q)
+{
+ int i;
+ for (i = 0; i < q->nstr; i++)
+ solv_free(q->str[i]);
+ q->str = solv_free(q->str);
+ q->nstr = 0;
+}
+
+static void
+strqueue_push(Strqueue *q, const char *s)
+{
+ q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
+ q->str[q->nstr++] = solv_strdup(s);
+}
+
+static void
+strqueue_pushjoin(Strqueue *q, const char *s1, const char *s2, const char *s3)
+{
+ q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
+ q->str[q->nstr++] = solv_dupjoin(s1, s2, s3);
+}
+
+static int
+strqueue_sort_cmp(const void *ap, const void *bp, void *dp)
+{
+ const char *a = *(const char **)ap;
+ const char *b = *(const char **)bp;
+ return strcmp(a ? a : "", b ? b : "");
+}
+
+static void
+strqueue_sort(Strqueue *q)
+{
+ if (q->nstr > 1)
+ solv_sort(q->str, q->nstr, sizeof(*q->str), strqueue_sort_cmp, 0);
+}
+
+static void
+strqueue_sort_u(Strqueue *q)
+{
+ int i, j;
+ strqueue_sort(q);
+ for (i = j = 0; i < q->nstr; i++)
+ if (!j || strqueue_sort_cmp(q->str + i, q->str + j - 1, 0) != 0)
+ q->str[j++] = q->str[i];
+ q->nstr = j;
+}
+
+static char *
+strqueue_join(Strqueue *q)
+{
+ int i, l = 0;
+ char *r, *rp;
+ for (i = 0; i < q->nstr; i++)
+ if (q->str[i])
+ l += strlen(q->str[i]) + 1;
+ l++; /* trailing \0 */
+ r = solv_malloc(l);
+ rp = r;
+ for (i = 0; i < q->nstr; i++)
+ if (q->str[i])
+ {
+ strcpy(rp, q->str[i]);
+ rp += strlen(rp);
+ *rp++ = '\n';
+ }
+ *rp = 0;
+ return r;
+}
+
+static void
+strqueue_split(Strqueue *q, const char *s)
+{
+ const char *p;
+ if (!s)
+ return;
+ while ((p = strchr(s, '\n')) != 0)
+ {
+ q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
+ q->str[q->nstr] = solv_malloc(p - s + 1);
+ if (p > s)
+ memcpy(q->str[q->nstr], s, p - s);
+ q->str[q->nstr][p - s] = 0;
+ q->nstr++;
+ s = p + 1;
+ }
+ if (*s)
+ strqueue_push(q, s);
+}
+
+static void
+strqueue_diff(Strqueue *sq1, Strqueue *sq2, Strqueue *osq)
+{
+ int i = 0, j = 0;
+ while (i < sq1->nstr && j < sq2->nstr)
+ {
+ int r = strqueue_sort_cmp(sq1->str + i, sq2->str + j, 0);
+ if (!r)
+ i++, j++;
+ else if (r < 0)
+ strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
+ else
+ strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
+ }
+ while (i < sq1->nstr)
+ strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
+ while (j < sq2->nstr)
+ strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
+}
+
+
+static const char *
+testcase_id2str(Pool *pool, Id id, int isname)
+{
+ const char *s = pool_id2str(pool, id);
+ const char *ss;
+ char *s2, *s2p;
+ int bad = 0, paren = 0, parenbad = 0;
+
+ if (id == 0)
+ return "<NULL>";
+ if (id == 1)
+ return "\\00";
+ if (strchr("[(<=>!", *s))
+ bad++;
+ if (!strncmp(s, "namespace:", 10))
+ bad++;
+ for (ss = s + bad; *ss; ss++)
+ {
+ if (*ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
+ bad++;
+ if (*ss == '(')
+ paren = paren == 0 ? 1 : -1;
+ else if (*ss == ')')
+ {
+ paren = paren == 1 ? 0 : -1;
+ if (!paren)
+ parenbad += 2;
+ }
+ }
+ if (isname && ss - s > 4 && !strcmp(ss - 4, ":any"))
+ bad++;
+ if (!paren && !(bad - parenbad))
+ return s;
+
+ /* we need escaping! */
+ s2 = s2p = pool_alloctmpspace(pool, strlen(s) + bad * 2 + 1);
+ if (!strncmp(s, "namespace:", 10))
+ {
+ strcpy(s2p, "namespace\\3a");
+ s2p += 12;
+ s += 10;
+ }
+ ss = s;
+ for (; *ss; ss++)
+ {
+ *s2p++ = *ss;
+ if ((ss == s && strchr("[(<=>!", *s)) || *ss == ' ' || *ss == '\\' || *(unsigned char *)ss < 32 || *ss == '(' || *ss == ')')
+ {
+ s2p[-1] = '\\';
+ solv_bin2hex((unsigned char *)ss, 1, s2p);
+ s2p += 2;
+ }
+ }
+ *s2p = 0;
+ if (isname && s2p - s2 > 4 && !strcmp(s2p - 4, ":any"))
+ strcpy(s2p - 4, "\\3aany");
+ return s2;
+}
+
+struct oplist {
+ Id flags;
+ const char *opname;
+} oplist[] = {
+ { REL_EQ, "=" },
+ { REL_GT | REL_LT | REL_EQ, "<=>" },
+ { REL_LT | REL_EQ, "<=" },
+ { REL_GT | REL_EQ, ">=" },
+ { REL_GT, ">" },
+ { REL_GT | REL_LT, "<>" },
+ { REL_AND, "&" },
+ { REL_OR , "|" },
+ { REL_WITH , "+" },
+ { REL_NAMESPACE , "<NAMESPACE>" },
+ { REL_ARCH, "." },
+ { REL_MULTIARCH, "<MULTIARCH>" },
+ { REL_FILECONFLICT, "<FILECONFLICT>" },
+ { REL_COND, "<IF>" },
+ { REL_COMPAT, "compat >=" },
+ { REL_KIND, "<KIND>" },
+ { REL_ELSE, "<ELSE>" },
+ { REL_LT, "<" },
+ { 0, 0 }
+};
+
+static const char *
+testcase_dep2str_complex(Pool *pool, Id id, int addparens)
+{
+ Reldep *rd;
+ char *s;
+ const char *s2;
+ int needparens;
+ struct oplist *op;
+
+ if (!ISRELDEP(id))
+ return testcase_id2str(pool, id, 1);
+ rd = GETRELDEP(pool, id);
+
+ /* check for special shortcuts */
+ if (rd->flags == REL_NAMESPACE && !ISRELDEP(rd->name) && !strncmp(pool_id2str(pool, rd->name), "namespace:", 10))
+ {
+ const char *ns = pool_id2str(pool, rd->name);
+ int nslen = strlen(ns);
+ /* special namespace formatting */
+ const char *evrs = testcase_dep2str_complex(pool, rd->evr, 0);
+ s = pool_tmpappend(pool, evrs, ns, "()");
+ memmove(s + nslen + 1, s, strlen(s) - nslen - 2);
+ memcpy(s, ns, nslen);
+ s[nslen] = '(';
+ return s;
+ }
+ if (rd->flags == REL_MULTIARCH && !ISRELDEP(rd->name) && rd->evr == ARCH_ANY)
+ {
+ /* special :any suffix */
+ const char *ns = testcase_id2str(pool, rd->name, 1);
+ return pool_tmpappend(pool, ns, ":any", 0);
+ }
+
+ needparens = 0;
+ if (ISRELDEP(rd->name))
+ {
+ Reldep *rd2 = GETRELDEP(pool, rd->name);
+ needparens = 1;
+ if (rd->flags > 7 && rd->flags != REL_COMPAT && rd2->flags && rd2->flags <= 7)
+ needparens = 0;
+ }
+ s = (char *)testcase_dep2str_complex(pool, rd->name, needparens);
+
+ if (addparens)
+ {
+ s = pool_tmpappend(pool, s, "(", 0);
+ memmove(s + 1, s, strlen(s + 1));
+ s[0] = '(';
+ }
+ for (op = oplist; op->flags; op++)
+ if (rd->flags == op->flags)
+ break;
+ if (op->flags)
+ {
+ s = pool_tmpappend(pool, s, " ", op->opname);
+ s = pool_tmpappend(pool, s, " ", 0);
+ }
+ else
+ {
+ char buf[64];
+ sprintf(buf, " <%u> ", rd->flags);
+ s = pool_tmpappend(pool, s, buf, 0);
+ }
+
+ needparens = 0;
+ if (ISRELDEP(rd->evr))
+ {
+ Reldep *rd2 = GETRELDEP(pool, rd->evr);
+ needparens = 1;
+ if (rd->flags > 7 && rd2->flags && rd2->flags <= 7)
+ needparens = 0;
+ if (rd->flags == REL_AND && rd2->flags == REL_AND)
+ needparens = 0; /* chain */
+ if (rd->flags == REL_OR && rd2->flags == REL_OR)
+ needparens = 0; /* chain */
+ if (rd->flags > 0 && rd->flags < 8 && rd2->flags == REL_COMPAT)
+ needparens = 0; /* chain */
+ }
+ if (!ISRELDEP(rd->evr))
+ s2 = testcase_id2str(pool, rd->evr, 0);
+ else
+ s2 = testcase_dep2str_complex(pool, rd->evr, needparens);
+ if (addparens)
+ s = pool_tmpappend(pool, s, s2, ")");
+ else
+ s = pool_tmpappend(pool, s, s2, 0);
+ pool_freetmpspace(pool, s2);
+ return s;
+}
+
+const char *
+testcase_dep2str(Pool *pool, Id id)
+{
+ return testcase_dep2str_complex(pool, id, 0);
+}
+
+
+/* Convert a simple string. Also handle the :any suffix */
+static Id
+testcase_str2dep_simple(Pool *pool, const char **sp, int isname)
+{
+ int haveesc = 0;
+ int paren = 0;
+ int isany = 0;
+ Id id;
+ const char *s;
+ for (s = *sp; *s; s++)
+ {
+ if (*s == '\\')
+ haveesc++;
+ if (*s == ' ' || *(unsigned char *)s < 32)
+ break;
+ if (*s == '(')
+ paren++;
+ if (*s == ')' && paren-- <= 0)
+ break;
+ }
+ if (isname && s - *sp > 4 && !strncmp(s - 4, ":any", 4))
+ {
+ isany = 1;
+ s -= 4;
+ }
+ if (!haveesc)
+ {
+ if (s - *sp == 6 && !strncmp(*sp, "<NULL>", 6))
+ id = 0;
+ else
+ id = pool_strn2id(pool, *sp, s - *sp, 1);
+ }
+ else if (s - *sp == 3 && !strncmp(*sp, "\\00", 3))
+ id = 1;
+ else
+ {
+ char buf[128], *bp, *bp2;
+ const char *sp2;
+ bp = s - *sp >= 128 ? solv_malloc(s - *sp + 1) : buf;
+ for (bp2 = bp, sp2 = *sp; sp2 < s;)
+ {
+ *bp2++ = *sp2++;
+ if (bp2[-1] == '\\')
+ solv_hex2bin(&sp2, (unsigned char *)bp2 - 1, 1);
+ }
+ *bp2 = 0;
+ id = pool_str2id(pool, bp, 1);
+ if (bp != buf)
+ solv_free(bp);
+ }
+ if (isany)
+ {
+ id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
+ s += 4;
+ }
+ *sp = s;
+ return id;
+}
+
+
+static Id
+testcase_str2dep_complex(Pool *pool, const char **sp, int relop)
+{
+ const char *s = *sp;
+ Id flags, id, id2, namespaceid = 0;
+ struct oplist *op;
+
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (!strncmp(s, "namespace:", 10))
+ {
+ /* special namespace hack */
+ const char *s2;
+ for (s2 = s + 10; *s2 && *s2 != '('; s2++)
+ ;
+ if (*s2 == '(')
+ {
+ namespaceid = pool_strn2id(pool, s, s2 - s, 1);
+ s = s2;
+ }
+ }
+ if (*s == '(')
+ {
+ s++;
+ id = testcase_str2dep_complex(pool, &s, 0);
+ if (!s || *s != ')')
+ {
+ *sp = 0;
+ return 0;
+ }
+ s++;
+ }
+ else
+ id = testcase_str2dep_simple(pool, &s, relop ? 0 : 1);
+ if (namespaceid)
+ id = pool_rel2id(pool, namespaceid, id, REL_NAMESPACE, 1);
+
+ for (;;)
+ {
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (!*s || *s == ')' || (relop && strncmp(s, "compat >= ", 10) != 0))
+ {
+ *sp = s;
+ return id;
+ }
+
+ /* we have an op! Find the end */
+ flags = -1;
+ if (s[0] == '<' && (s[1] >= '0' && s[1] <= '9'))
+ {
+ const char *s2;
+ for (s2 = s + 1; *s2 >= '0' && *s2 <= '9'; s2++)
+ ;
+ if (*s2 == '>')
+ {
+ flags = strtoul(s + 1, 0, 10);
+ s = s2 + 1;
+ }
+ }
+ if (flags == -1)
+ {
+ for (op = oplist; op->flags; op++)
+ if (!strncmp(s, op->opname, strlen(op->opname)))
+ break;
+ if (!op->flags)
+ {
+ *sp = 0;
+ return 0;
+ }
+ flags = op->flags;
+ s += strlen(op->opname);
+ }
+ id2 = testcase_str2dep_complex(pool, &s, flags > 0 && flags < 8);
+ if (!s)
+ {
+ *sp = 0;
+ return 0;
+ }
+ id = pool_rel2id(pool, id, id2, flags, 1);
+ }
+}
+
+Id
+testcase_str2dep(Pool *pool, const char *s)
+{
+ Id id = testcase_str2dep_complex(pool, &s, 0);
+ return s && !*s ? id : 0;
+}
+
+/**********************************************************/
+
+const char *
+testcase_repoid2str(Pool *pool, Id repoid)
+{
+ Repo *repo = pool_id2repo(pool, repoid);
+ if (repo->name)
+ {
+ char *r = pool_tmpjoin(pool, repo->name, 0, 0);
+ char *rp;
+ for (rp = r; *rp; rp++)
+ if (*rp == ' ' || *rp == '\t')
+ *rp = '_';
+ return r;
+ }
+ else
+ {
+ char buf[20];
+ sprintf(buf, "#%d", repoid);
+ return pool_tmpjoin(pool, buf, 0, 0);
+ }
+}
+
+const char *
+testcase_solvid2str(Pool *pool, Id p)
+{
+ Solvable *s = pool->solvables + p;
+ const char *n, *e, *a;
+ char *str, buf[20];
+
+ if (p == SYSTEMSOLVABLE)
+ return "@SYSTEM";
+ n = pool_id2str(pool, s->name);
+ e = pool_id2str(pool, s->evr);
+ a = pool_id2str(pool, s->arch);
+ str = pool_alloctmpspace(pool, strlen(n) + strlen(e) + strlen(a) + 3);
+ sprintf(str, "%s-%s.%s", n, e, a);
+ if (!s->repo)
+ return pool_tmpappend(pool, str, "@", 0);
+ if (s->repo->name)
+ {
+ int l = strlen(str);
+ char *str2 = pool_tmpappend(pool, str, "@", s->repo->name);
+ for (; str2[l]; l++)
+ if (str2[l] == ' ' || str2[l] == '\t')
+ str2[l] = '_';
+ return str2;
+ }
+ sprintf(buf, "@#%d", s->repo->repoid);
+ return pool_tmpappend(pool, str, buf, 0);
+}
+
+Repo *
+testcase_str2repo(Pool *pool, const char *str)
+{
+ int repoid;
+ Repo *repo = 0;
+ if (str[0] == '#' && (str[1] >= '0' && str[1] <= '9'))
+ {
+ int j;
+ repoid = 0;
+ for (j = 1; str[j] >= '0' && str[j] <= '9'; j++)
+ repoid = repoid * 10 + (str[j] - '0');
+ if (!str[j] && repoid > 0 && repoid < pool->nrepos)
+ repo = pool_id2repo(pool, repoid);
+ }
+ if (!repo)
+ {
+ FOR_REPOS(repoid, repo)
+ {
+ int i, l;
+ if (!repo->name)
+ continue;
+ l = strlen(repo->name);
+ for (i = 0; i < l; i++)
+ {
+ int c = repo->name[i];
+ if (c == ' ' || c == '\t')
+ c = '_';
+ if (c != str[i])
+ break;
+ }
+ if (i == l && !str[l])
+ break;
+ }
+ if (repoid >= pool->nrepos)
+ repo = 0;
+ }
+ return repo;
+}
+
+Id
+testcase_str2solvid(Pool *pool, const char *str)
+{
+ int i, l = strlen(str);
+ int repostart;
+ Repo *repo;
+ Id arch;
+
+ if (!l)
+ return 0;
+ if (*str == '@' && !strcmp(str, "@SYSTEM"))
+ return SYSTEMSOLVABLE;
+ repo = 0;
+ for (i = l - 1; i >= 0; i--)
+ if (str[i] == '@' && (repo = testcase_str2repo(pool, str + i + 1)) != 0)
+ break;
+ if (i < 0)
+ i = l;
+ repostart = i;
+ /* now find the arch (if present) */
+ arch = 0;
+ for (i = repostart - 1; i > 0; i--)
+ if (str[i] == '.')
+ {
+ arch = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
+ if (arch)
+ repostart = i;
+ break;
+ }
+ /* now find the name */
+ for (i = repostart - 1; i > 0; i--)
+ {
+ if (str[i] == '-')
+ {
+ Id nid, evrid, p, pp;
+ nid = pool_strn2id(pool, str, i, 0);
+ if (!nid)
+ continue;
+ evrid = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
+ if (!evrid)
+ continue;
+ FOR_PROVIDES(p, pp, nid)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->name != nid || s->evr != evrid)
+ continue;
+ if (repo && s->repo != repo)
+ continue;
+ if (arch && s->arch != arch)
+ continue;
+ return p;
+ }
+ }
+ }
+ return 0;
+}
+
+const char *
+testcase_job2str(Pool *pool, Id how, Id what)
+{
+ char *ret;
+ const char *jobstr;
+ const char *selstr;
+ const char *pkgstr;
+ int i, o;
+ Id select = how & SOLVER_SELECTMASK;
+
+ for (i = 0; job2str[i].str; i++)
+ if ((how & SOLVER_JOBMASK) == job2str[i].job)
+ break;
+ jobstr = job2str[i].str ? job2str[i].str : "unknown";
+ if (select == SOLVER_SOLVABLE)
+ {
+ selstr = " pkg ";
+ pkgstr = testcase_solvid2str(pool, what);
+ }
+ else if (select == SOLVER_SOLVABLE_NAME)
+ {
+ selstr = " name ";
+ pkgstr = testcase_dep2str(pool, what);
+ }
+ else if (select == SOLVER_SOLVABLE_PROVIDES)
+ {
+ selstr = " provides ";
+ pkgstr = testcase_dep2str(pool, what);
+ }
+ else if (select == SOLVER_SOLVABLE_ONE_OF)
+ {
+ Id p;
+ selstr = " oneof ";
+ pkgstr = 0;
+ while ((p = pool->whatprovidesdata[what++]) != 0)
+ {
+ const char *s = testcase_solvid2str(pool, p);
+ if (pkgstr)
+ {
+ pkgstr = pool_tmpappend(pool, pkgstr, " ", s);
+ pool_freetmpspace(pool, s);
+ }
+ else
+ pkgstr = s;
+ }
+ if (!pkgstr)
+ pkgstr = "nothing";
+ }
+ else if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Repo *repo = pool_id2repo(pool, what);
+ selstr = " repo ";
+ if (!repo->name)
+ {
+ char buf[20];
+ sprintf(buf, "#%d", repo->repoid);
+ pkgstr = pool_tmpjoin(pool, buf, 0, 0);
+ }
+ else
+ pkgstr = pool_tmpjoin(pool, repo->name, 0, 0);
+ }
+ else if (select == SOLVER_SOLVABLE_ALL)
+ {
+ selstr = " all ";
+ pkgstr = "packages";
+ }
+ else
+ {
+ selstr = " unknown ";
+ pkgstr = "unknown";
+ }
+ ret = pool_tmpjoin(pool, jobstr, selstr, pkgstr);
+ o = strlen(ret);
+ ret = pool_tmpappend(pool, ret, " ", 0);
+ for (i = 0; jobflags2str[i].str; i++)
+ if ((how & jobflags2str[i].flag) != 0)
+ ret = pool_tmpappend(pool, ret, ",", jobflags2str[i].str);
+ if (!ret[o + 1])
+ ret[o] = 0;
+ else
+ {
+ ret[o + 1] = '[';
+ ret = pool_tmpappend(pool, ret, "]", 0);
+ }
+ return ret;
+}
+
+static int
+str2selflags(Pool *pool, char *s) /* modifies the string! */
+{
+ int i, selflags = 0;
+ while (s)
+ {
+ char *se = strchr(s, ',');
+ if (se)
+ *se++ = 0;
+ for (i = 0; selflags2str[i].str; i++)
+ if (!strcmp(s, selflags2str[i].str))
+ {
+ selflags |= selflags2str[i].flag;
+ break;
+ }
+ if (!selflags2str[i].str)
+ pool_debug(pool, SOLV_ERROR, "str2job: unknown selection flag '%s'\n", s);
+ s = se;
+ }
+ return selflags;
+}
+
+static int
+str2jobflags(Pool *pool, char *s) /* modifies the string */
+{
+ int i, jobflags = 0;
+ while (s)
+ {
+ char *se = strchr(s, ',');
+ if (se)
+ *se++ = 0;
+ for (i = 0; jobflags2str[i].str; i++)
+ if (!strcmp(s, jobflags2str[i].str))
+ {
+ jobflags |= jobflags2str[i].flag;
+ break;
+ }
+ if (!jobflags2str[i].str)
+ pool_debug(pool, SOLV_ERROR, "str2job: unknown job flag '%s'\n", s);
+ s = se;
+ }
+ return jobflags;
+}
+
+Id
+testcase_str2job(Pool *pool, const char *str, Id *whatp)
+{
+ int i;
+ Id job;
+ Id what;
+ char *s;
+ char **pieces = 0;
+ int npieces = 0;
+
+ *whatp = 0;
+ /* so we can patch it */
+ s = pool_tmpjoin(pool, str, 0, 0);
+ /* split it in pieces */
+ for (;;)
+ {
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (!*s)
+ break;
+ pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
+ pieces[npieces++] = s;
+ while (*s && *s != ' ' && *s != '\t')
+ s++;
+ if (*s)
+ *s++ = 0;
+ }
+ if (npieces < 3)
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
+ solv_free(pieces);
+ return -1;
+ }
+
+ for (i = 0; job2str[i].str; i++)
+ if (!strcmp(pieces[0], job2str[i].str))
+ break;
+ if (!job2str[i].str)
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: unknown job '%s'\n", str);
+ solv_free(pieces);
+ return -1;
+ }
+ job = job2str[i].job;
+ what = 0;
+ if (npieces > 3)
+ {
+ char *flags = pieces[npieces - 1];
+ if (*flags == '[' && flags[strlen(flags) - 1] == ']')
+ {
+ npieces--;
+ flags++;
+ flags[strlen(flags) - 1] = 0;
+ job |= str2jobflags(pool, flags);
+ }
+ }
+ if (!strcmp(pieces[1], "pkg"))
+ {
+ if (npieces != 3)
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: bad pkg selector in '%s'\n", str);
+ solv_free(pieces);
+ return -1;
+ }
+ job |= SOLVER_SOLVABLE;
+ what = testcase_str2solvid(pool, pieces[2]);
+ if (!what)
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[2]);
+ solv_free(pieces);
+ return -1;
+ }
+ }
+ else if (!strcmp(pieces[1], "name") || !strcmp(pieces[1], "provides"))
+ {
+ /* join em again for dep2str... */
+ char *sp;
+ for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
+ if (*sp == 0)
+ *sp = ' ';
+ what = 0;
+ if (pieces[1][0] == 'p' && strncmp(pieces[2], "namespace:", 10) == 0)
+ {
+ char *spe = strchr(pieces[2], '(');
+ int l = strlen(pieces[2]);
+ if (spe && pieces[2][l - 1] == ')')
+ {
+ /* special namespace provides */
+ if (strcmp(spe, "(<NULL>)") != 0)
+ {
+ pieces[2][l - 1] = 0;
+ what = testcase_str2dep(pool, spe + 1);
+ pieces[2][l - 1] = ')';
+ }
+ what = pool_rel2id(pool, pool_strn2id(pool, pieces[2], spe - pieces[2], 1), what, REL_NAMESPACE, 1);
+ }
+ }
+ if (!what)
+ what = testcase_str2dep(pool, pieces[2]);
+ if (pieces[1][0] == 'n')
+ job |= SOLVER_SOLVABLE_NAME;
+ else
+ job |= SOLVER_SOLVABLE_PROVIDES;
+ }
+ else if (!strcmp(pieces[1], "oneof"))
+ {
+ Queue q;
+ job |= SOLVER_SOLVABLE_ONE_OF;
+ queue_init(&q);
+ if (npieces > 3 && strcmp(pieces[2], "nothing") != 0)
+ {
+ for (i = 2; i < npieces; i++)
+ {
+ Id p = testcase_str2solvid(pool, pieces[i]);
+ if (!p)
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[i]);
+ queue_free(&q);
+ solv_free(pieces);
+ return -1;
+ }
+ queue_push(&q, p);
+ }
+ }
+ what = pool_queuetowhatprovides(pool, &q);
+ queue_free(&q);
+ }
+ else if (!strcmp(pieces[1], "repo"))
+ {
+ Repo *repo;
+ if (npieces != 3)
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
+ solv_free(pieces);
+ return -1;
+ }
+ repo = testcase_str2repo(pool, pieces[2]);
+ if (!repo)
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: unknown repo '%s'\n", pieces[2]);
+ solv_free(pieces);
+ return -1;
+ }
+ job |= SOLVER_SOLVABLE_REPO;
+ what = repo->repoid;
+ }
+ else if (!strcmp(pieces[1], "all"))
+ {
+ if (npieces != 3 && strcmp(pieces[2], "packages") != 0)
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
+ solv_free(pieces);
+ return -1;
+ }
+ job |= SOLVER_SOLVABLE_ALL;
+ what = 0;
+ }
+ else
+ {
+ pool_debug(pool, SOLV_ERROR, "str2job: unknown selection in '%s'\n", str);
+ solv_free(pieces);
+ return -1;
+ }
+ *whatp = what;
+ solv_free(pieces);
+ return job;
+}
+
+int
+addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue)
+{
+ Id job;
+ int i, r;
+ int selflags;
+ Queue sel;
+
+ for (i = 0; job2str[i].str; i++)
+ if (!strcmp(pieces[0], job2str[i].str))
+ break;
+ if (!job2str[i].str)
+ {
+ pool_debug(pool, SOLV_ERROR, "selstr2job: unknown job '%s'\n", pieces[0]);
+ return -1;
+ }
+ job = job2str[i].job;
+ if (npieces > 3)
+ {
+ char *flags = pieces[npieces - 1];
+ if (*flags == '[' && flags[strlen(flags) - 1] == ']')
+ {
+ npieces--;
+ flags++;
+ flags[strlen(flags) - 1] = 0;
+ job |= str2jobflags(pool, flags);
+ }
+ }
+ if (npieces < 4)
+ {
+ pool_debug(pool, SOLV_ERROR, "selstr2job: no selection flags\n");
+ return -1;
+ }
+ selflags = str2selflags(pool, pieces[3]);
+ queue_init(&sel);
+ r = selection_make(pool, &sel, pieces[2], selflags);
+ for (i = 0; i < sel.count; i += 2)
+ queue_push2(jobqueue, job | sel.elements[i], sel.elements[i + 1]);
+ queue_free(&sel);
+ return r;
+}
+
+static void
+writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off)
+{
+ Pool *pool = repo->pool;
+ Id id, *dp;
+ int tagwritten = 0;
+ const char *idstr;
+
+ if (!off)
+ return;
+ dp = repo->idarraydata + off;
+ while ((id = *dp++) != 0)
+ {
+ if (key == SOLVABLE_REQUIRES && id == SOLVABLE_PREREQMARKER)
+ {
+ if (tagwritten)
+ fprintf(fp, "-%s\n", tag);
+ tagwritten = 0;
+ tag = "Prq:";
+ continue;
+ }
+ if (key == SOLVABLE_PROVIDES && id == SOLVABLE_FILEMARKER)
+ continue;
+ idstr = testcase_dep2str(pool, id);
+ if (!tagwritten)
+ {
+ fprintf(fp, "+%s\n", tag);
+ tagwritten = 1;
+ }
+ fprintf(fp, "%s\n", idstr);
+ }
+ if (tagwritten)
+ fprintf(fp, "-%s\n", tag);
+}
+
+static void
+writefilelist(Repo *repo, FILE *fp, const char *tag, Solvable *s)
+{
+ Pool *pool = repo->pool;
+ int tagwritten = 0;
+ Dataiterator di;
+
+ dataiterator_init(&di, pool, repo, s - pool->solvables, SOLVABLE_FILELIST, 0, 0);
+ while (dataiterator_step(&di))
+ {
+ const char *s = repodata_dir2str(di.data, di.kv.id, di.kv.str);
+ if (!tagwritten)
+ {
+ fprintf(fp, "+%s\n", tag);
+ tagwritten = 1;
+ }
+ fprintf(fp, "%s\n", s);
+ }
+ if (tagwritten)
+ fprintf(fp, "-%s\n", tag);
+ dataiterator_free(&di);
+}
+
+int
+testcase_write_testtags(Repo *repo, FILE *fp)
+{
+ Pool *pool = repo->pool;
+ Solvable *s;
+ Id p;
+ const char *name;
+ const char *evr;
+ const char *arch;
+ const char *release;
+ const char *tmp;
+ unsigned int ti;
+
+ fprintf(fp, "=Ver: 3.0\n");
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ name = pool_id2str(pool, s->name);
+ evr = pool_id2str(pool, s->evr);
+ arch = pool_id2str(pool, s->arch);
+ release = strrchr(evr, '-');
+ if (!release)
+ release = evr + strlen(evr);
+ fprintf(fp, "=Pkg: %s %.*s %s %s\n", name, (int)(release - evr), evr, *release && release[1] ? release + 1 : "-", arch);
+ tmp = solvable_lookup_str(s, SOLVABLE_SUMMARY);
+ if (tmp)
+ fprintf(fp, "=Sum: %s\n", tmp);
+ writedeps(repo, fp, "Req:", SOLVABLE_REQUIRES, s, s->requires);
+ writedeps(repo, fp, "Prv:", SOLVABLE_PROVIDES, s, s->provides);
+ writedeps(repo, fp, "Obs:", SOLVABLE_OBSOLETES, s, s->obsoletes);
+ writedeps(repo, fp, "Con:", SOLVABLE_CONFLICTS, s, s->conflicts);
+ writedeps(repo, fp, "Rec:", SOLVABLE_RECOMMENDS, s, s->recommends);
+ writedeps(repo, fp, "Sup:", SOLVABLE_SUPPLEMENTS, s, s->supplements);
+ writedeps(repo, fp, "Sug:", SOLVABLE_SUGGESTS, s, s->suggests);
+ writedeps(repo, fp, "Enh:", SOLVABLE_ENHANCES, s, s->enhances);
+ if (s->vendor)
+ fprintf(fp, "=Vnd: %s\n", pool_id2str(pool, s->vendor));
+ ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
+ if (ti)
+ fprintf(fp, "=Tim: %u\n", ti);
+ writefilelist(repo, fp, "Fls:", s);
+ }
+ return 0;
+}
+
+static inline Offset
+adddep(Repo *repo, Offset olddeps, char *str, Id marker)
+{
+ Id id = *str == '/' ? pool_str2id(repo->pool, str, 1) : testcase_str2dep(repo->pool, str);
+ return repo_addid_dep(repo, olddeps, id, marker);
+}
+
+static void
+finish_v2_solvable(Pool *pool, Repodata *data, Solvable *s, char *filelist, int nfilelist)
+{
+ if (nfilelist)
+ {
+ int l;
+ Id did;
+ for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
+ {
+ char *p = strrchr(filelist + l, '/');
+ if (!p)
+ continue;
+ *p++ = 0;
+ did = repodata_str2dir(data, filelist + l, 1);
+ p[-1] = '/';
+ if (!did)
+ did = repodata_str2dir(data, "/", 1);
+ repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
+ }
+ }
+ s->supplements = repo_fix_supplements(s->repo, s->provides, s->supplements, 0);
+ s->conflicts = repo_fix_conflicts(s->repo, s->conflicts);
+}
+
+/* stripped down version of susetags parser used for testcases */
+int
+testcase_add_testtags(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ char *line, *linep;
+ int aline;
+ int tag;
+ Repodata *data;
+ Solvable *s;
+ char *sp[5];
+ unsigned int t;
+ int intag;
+ char *filelist = 0;
+ int afilelist = 0;
+ int nfilelist = 0;
+ int tagsversion = 0;
+ int addselfprovides = 1; /* for compat reasons */
+
+ data = repo_add_repodata(repo, flags);
+ s = 0;
+ intag = 0;
+
+ aline = 1024;
+ line = solv_malloc(aline);
+ linep = line;
+ for (;;)
+ {
+ if (linep - line + 16 > aline)
+ {
+ aline = linep - line;
+ line = solv_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[-1] = 0;
+ linep = line + intag;
+ if (intag)
+ {
+ if (line[intag] == '-' && !strncmp(line + 1, line + intag + 1, intag - 2))
+ {
+ intag = 0;
+ linep = line;
+ continue;
+ }
+ }
+ else if (line[0] == '+' && line[1] && line[1] != ':')
+ {
+ char *tagend = strchr(line, ':');
+ if (!tagend)
+ continue;
+ line[0] = '=';
+ tagend[1] = ' ';
+ intag = tagend + 2 - line;
+ linep = line + intag;
+ continue;
+ }
+ if (*line != '=' || !line[1] || !line[2] || !line[3] || line[4] != ':')
+ continue;
+ tag = line[1] << 16 | line[2] << 8 | line[3];
+ switch(tag)
+ {
+ case 'V' << 16 | 'e' << 8 | 'r':
+ tagsversion = atoi(line + 6);
+ addselfprovides = tagsversion < 3 || strstr(line + 6, "addselfprovides") != 0;
+ break;
+ case 'P' << 16 | 'k' << 8 | 'g':
+ if (s)
+ {
+ if (tagsversion == 2)
+ finish_v2_solvable(pool, data, s, filelist, nfilelist);
+ if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ }
+ nfilelist = 0;
+ if (split(line + 5, sp, 5) != 4)
+ break;
+ s = pool_id2solvable(pool, repo_add_solvable(repo));
+ s->name = pool_str2id(pool, sp[0], 1);
+ /* join back version and release */
+ if (sp[2] && !(sp[2][0] == '-' && !sp[2][1]))
+ sp[2][-1] = '-';
+ s->evr = makeevr(pool, sp[1]);
+ s->arch = pool_str2id(pool, sp[3], 1);
+ break;
+ case 'S' << 16 | 'u' << 8 | 'm':
+ repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 6);
+ break;
+ case 'V' << 16 | 'n' << 8 | 'd':
+ s->vendor = pool_str2id(pool, line + 6, 1);
+ break;
+ case 'T' << 16 | 'i' << 8 | 'm':
+ t = atoi(line + 6);
+ if (t)
+ repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
+ break;
+ case 'R' << 16 | 'e' << 8 | 'q':
+ s->requires = adddep(repo, s->requires, line + 6, -SOLVABLE_PREREQMARKER);
+ break;
+ case 'P' << 16 | 'r' << 8 | 'q':
+ s->requires = adddep(repo, s->requires, line + 6, SOLVABLE_PREREQMARKER);
+ break;
+ case 'P' << 16 | 'r' << 8 | 'v':
+ /* version 2 had the file list at the end of the provides */
+ if (tagsversion == 2)
+ {
+ if (line[6] == '/')
+ {
+ int l = strlen(line + 6) + 1;
+ if (nfilelist + l > afilelist)
+ {
+ afilelist = nfilelist + l + 512;
+ filelist = solv_realloc(filelist, afilelist);
+ }
+ memcpy(filelist + nfilelist, line + 6, l);
+ nfilelist += l;
+ break;
+ }
+ if (nfilelist)
+ {
+ int l;
+ for (l = 0; l < nfilelist; l += strlen(filelist + l) + 1)
+ s->provides = repo_addid_dep(repo, s->provides, pool_str2id(pool, filelist + l, 1), 0);
+ nfilelist = 0;
+ }
+ }
+ s->provides = adddep(repo, s->provides, line + 6, 0);
+ break;
+ case 'F' << 16 | 'l' << 8 | 's':
+ {
+ char *p = strrchr(line + 6, '/');
+ Id did;
+ if (!p)
+ break;
+ *p++ = 0;
+ did = repodata_str2dir(data, line + 6, 1);
+ if (!did)
+ did = repodata_str2dir(data, "/", 1);
+ repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, did, p);
+ break;
+ }
+ case 'O' << 16 | 'b' << 8 | 's':
+ s->obsoletes = adddep(repo, s->obsoletes, line + 6, 0);
+ break;
+ case 'C' << 16 | 'o' << 8 | 'n':
+ s->conflicts = adddep(repo, s->conflicts, line + 6, 0);
+ break;
+ case 'R' << 16 | 'e' << 8 | 'c':
+ s->recommends = adddep(repo, s->recommends, line + 6, 0);
+ break;
+ case 'S' << 16 | 'u' << 8 | 'p':
+ s->supplements = adddep(repo, s->supplements, line + 6, 0);
+ break;
+ case 'S' << 16 | 'u' << 8 | 'g':
+ s->suggests = adddep(repo, s->suggests, line + 6, 0);
+ break;
+ case 'E' << 16 | 'n' << 8 | 'h':
+ s->enhances = adddep(repo, s->enhances, line + 6, 0);
+ break;
+ default:
+ break;
+ }
+ }
+ if (s)
+ {
+ if (tagsversion == 2)
+ finish_v2_solvable(pool, data, s, filelist, nfilelist);
+ if (addselfprovides && s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+ s->provides = repo_addid_dep(s->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+ }
+ solv_free(line);
+ solv_free(filelist);
+ repodata_free_dircache(data);
+ if (!(flags & REPO_NO_INTERNALIZE))
+ repodata_internalize(data);
+ return 0;
+}
+
+const char *
+testcase_getpoolflags(Pool *pool)
+{
+ const char *str = 0;
+ int i, v;
+ for (i = 0; poolflags2str[i].str; i++)
+ {
+ v = pool_get_flag(pool, poolflags2str[i].flag);
+ if (v == poolflags2str[i].def)
+ continue;
+ str = pool_tmpappend(pool, str, v ? " " : " !", poolflags2str[i].str);
+ }
+ return str ? str + 1 : "";
+}
+
+int
+testcase_setpoolflags(Pool *pool, const char *str)
+{
+ const char *p = str, *s;
+ int i, v;
+ for (;;)
+ {
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ p++;
+ v = 1;
+ if (*p == '!')
+ {
+ p++;
+ v = 0;
+ }
+ if (!*p)
+ break;
+ s = p;
+ while (*p && *p != ' ' && *p != '\t' && *p != ',')
+ p++;
+ for (i = 0; poolflags2str[i].str; i++)
+ if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0)
+ break;
+ if (!poolflags2str[i].str)
+ {
+ pool_debug(pool, SOLV_ERROR, "setpoolflags: unknown flag '%.*s'\n", (int)(p - s), s);
+ return 0;
+ }
+ pool_set_flag(pool, poolflags2str[i].flag, v);
+ }
+ return 1;
+}
+
+void
+testcase_resetpoolflags(Pool *pool)
+{
+ int i;
+ for (i = 0; poolflags2str[i].str; i++)
+ pool_set_flag(pool, poolflags2str[i].flag, poolflags2str[i].def);
+}
+
+const char *
+testcase_getsolverflags(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ const char *str = 0;
+ int i, v;
+ for (i = 0; solverflags2str[i].str; i++)
+ {
+ v = solver_get_flag(solv, solverflags2str[i].flag);
+ if (v == solverflags2str[i].def)
+ continue;
+ str = pool_tmpappend(pool, str, v ? " " : " !", solverflags2str[i].str);
+ }
+ return str ? str + 1 : "";
+}
+
+int
+testcase_setsolverflags(Solver *solv, const char *str)
+{
+ const char *p = str, *s;
+ int i, v;
+ for (;;)
+ {
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ p++;
+ v = 1;
+ if (*p == '!')
+ {
+ p++;
+ v = 0;
+ }
+ if (!*p)
+ break;
+ s = p;
+ while (*p && *p != ' ' && *p != '\t' && *p != ',')
+ p++;
+ for (i = 0; solverflags2str[i].str; i++)
+ if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0)
+ break;
+ if (!solverflags2str[i].str)
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "setsolverflags: unknown flag '%.*s'\n", (int)(p - s), s);
+ return 0;
+ }
+ solver_set_flag(solv, solverflags2str[i].flag, v);
+ }
+ return 1;
+}
+
+void
+testcase_resetsolverflags(Solver *solv)
+{
+ int i;
+ for (i = 0; solverflags2str[i].str; i++)
+ solver_set_flag(solv, solverflags2str[i].flag, solverflags2str[i].def);
+}
+
+static const char *
+testcase_ruleid(Solver *solv, Id rid)
+{
+ Strqueue sq;
+ Queue q;
+ int i;
+ Chksum *chk;
+ const unsigned char *md5;
+ int md5l;
+ const char *s;
+
+ queue_init(&q);
+ strqueue_init(&sq);
+ solver_ruleliterals(solv, rid, &q);
+ for (i = 0; i < q.count; i++)
+ {
+ Id p = q.elements[i];
+ s = testcase_solvid2str(solv->pool, p > 0 ? p : -p);
+ if (p < 0)
+ s = pool_tmpjoin(solv->pool, "!", s, 0);
+ strqueue_push(&sq, s);
+ }
+ queue_free(&q);
+ strqueue_sort_u(&sq);
+ chk = solv_chksum_create(REPOKEY_TYPE_MD5);
+ for (i = 0; i < sq.nstr; i++)
+ solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
+ md5 = solv_chksum_get(chk, &md5l);
+ s = pool_bin2hex(solv->pool, md5, md5l);
+ chk = solv_chksum_free(chk, 0);
+ strqueue_free(&sq);
+ return s;
+}
+
+static const char *
+testcase_problemid(Solver *solv, Id problem)
+{
+ Strqueue sq;
+ Queue q;
+ Chksum *chk;
+ const unsigned char *md5;
+ int i, md5l;
+ const char *s;
+
+ /* we build a hash of all rules that define the problem */
+ queue_init(&q);
+ strqueue_init(&sq);
+ solver_findallproblemrules(solv, problem, &q);
+ for (i = 0; i < q.count; i++)
+ strqueue_push(&sq, testcase_ruleid(solv, q.elements[i]));
+ queue_free(&q);
+ strqueue_sort_u(&sq);
+ chk = solv_chksum_create(REPOKEY_TYPE_MD5);
+ for (i = 0; i < sq.nstr; i++)
+ solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
+ md5 = solv_chksum_get(chk, &md5l);
+ s = pool_bin2hex(solv->pool, md5, 4);
+ chk = solv_chksum_free(chk, 0);
+ strqueue_free(&sq);
+ return s;
+}
+
+static const char *
+testcase_solutionid(Solver *solv, Id problem, Id solution)
+{
+ Id intid;
+ Chksum *chk;
+ const unsigned char *md5;
+ int md5l;
+ const char *s;
+
+ intid = solver_solutionelement_internalid(solv, problem, solution);
+ /* internal stuff! handle with care! */
+ if (intid < 0)
+ {
+ /* it's a job */
+ s = testcase_job2str(solv->pool, solv->job.elements[-intid - 1], solv->job.elements[-intid]);
+ }
+ else
+ {
+ /* it's a rule */
+ s = testcase_ruleid(solv, intid);
+ }
+ chk = solv_chksum_create(REPOKEY_TYPE_MD5);
+ solv_chksum_add(chk, s, strlen(s) + 1);
+ md5 = solv_chksum_get(chk, &md5l);
+ s = pool_bin2hex(solv->pool, md5, 4);
+ chk = solv_chksum_free(chk, 0);
+ return s;
+}
+
+static const char *
+testcase_alternativeid(Solver *solv, int type, Id id, Id from)
+{
+ const char *s;
+ Pool *pool = solv->pool;
+ Chksum *chk;
+ const unsigned char *md5;
+ int md5l;
+ chk = solv_chksum_create(REPOKEY_TYPE_MD5);
+ if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
+ {
+ s = testcase_solvid2str(pool, from);
+ solv_chksum_add(chk, s, strlen(s) + 1);
+ s = testcase_dep2str(pool, id);
+ solv_chksum_add(chk, s, strlen(s) + 1);
+ }
+ else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
+ {
+ s = testcase_ruleid(solv, id);
+ solv_chksum_add(chk, s, strlen(s) + 1);
+ }
+ md5 = solv_chksum_get(chk, &md5l);
+ s = pool_bin2hex(pool, md5, 4);
+ chk = solv_chksum_free(chk, 0);
+ return s;
+}
+
+static struct class2str {
+ Id class;
+ const char *str;
+} class2str[] = {
+ { SOLVER_TRANSACTION_ERASE, "erase" },
+ { SOLVER_TRANSACTION_INSTALL, "install" },
+ { SOLVER_TRANSACTION_REINSTALLED, "reinstall" },
+ { SOLVER_TRANSACTION_DOWNGRADED, "downgrade" },
+ { SOLVER_TRANSACTION_CHANGED, "change" },
+ { SOLVER_TRANSACTION_UPGRADED, "upgrade" },
+ { SOLVER_TRANSACTION_OBSOLETED, "obsolete" },
+ { SOLVER_TRANSACTION_MULTIINSTALL, "multiinstall" },
+ { SOLVER_TRANSACTION_MULTIREINSTALL, "multireinstall" },
+ { 0, 0 }
+};
+
+static int
+dump_genid(Pool *pool, Strqueue *sq, Id id, int cnt)
+{
+ struct oplist *op;
+ char cntbuf[20];
+ const char *s;
+
+ if (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ for (op = oplist; op->flags; op++)
+ if (rd->flags == op->flags)
+ break;
+ cnt = dump_genid(pool, sq, rd->name, cnt);
+ cnt = dump_genid(pool, sq, rd->evr, cnt);
+ sprintf(cntbuf, "genid %2d: genid ", cnt++);
+ s = pool_tmpjoin(pool, cntbuf, "op ", op->flags ? op->opname : "unknown");
+ }
+ else
+ {
+ sprintf(cntbuf, "genid %2d: genid ", cnt++);
+ s = pool_tmpjoin(pool, cntbuf, id ? "lit " : "null", id ? pool_id2str(pool, id) : 0);
+ }
+ strqueue_push(sq, s);
+ return cnt;
+}
+
+char *
+testcase_solverresult(Solver *solv, int resultflags)
+{
+ Pool *pool = solv->pool;
+ int i, j;
+ Id p, op;
+ const char *s;
+ char *result;
+ Strqueue sq;
+
+ strqueue_init(&sq);
+ if ((resultflags & TESTCASE_RESULT_TRANSACTION) != 0)
+ {
+ Transaction *trans = solver_create_transaction(solv);
+ Queue q;
+
+ queue_init(&q);
+ for (i = 0; class2str[i].str; i++)
+ {
+ queue_empty(&q);
+ transaction_classify_pkgs(trans, SOLVER_TRANSACTION_KEEP_PSEUDO, class2str[i].class, 0, 0, &q);
+ for (j = 0; j < q.count; j++)
+ {
+ p = q.elements[j];
+ op = 0;
+ if (pool->installed && pool->solvables[p].repo == pool->installed)
+ op = transaction_obs_pkg(trans, p);
+ s = pool_tmpjoin(pool, class2str[i].str, " ", testcase_solvid2str(pool, p));
+ if (op)
+ s = pool_tmpjoin(pool, s, " ", testcase_solvid2str(pool, op));
+ strqueue_push(&sq, s);
+ }
+ }
+ queue_free(&q);
+ transaction_free(trans);
+ }
+ if ((resultflags & TESTCASE_RESULT_PROBLEMS) != 0)
+ {
+ char *probprefix, *solprefix;
+ int problem, solution, element;
+ int pcnt, scnt;
+
+ pcnt = solver_problem_count(solv);
+ for (problem = 1; problem <= pcnt; problem++)
+ {
+ Id rid, from, to, dep;
+ SolverRuleinfo rinfo;
+ rid = solver_findproblemrule(solv, problem);
+ s = testcase_problemid(solv, problem);
+ probprefix = solv_dupjoin("problem ", s, 0);
+ rinfo = solver_ruleinfo(solv, rid, &from, &to, &dep);
+ s = pool_tmpjoin(pool, probprefix, " info ", solver_problemruleinfo2str(solv, rinfo, from, to, dep));
+ strqueue_push(&sq, s);
+ scnt = solver_solution_count(solv, problem);
+ for (solution = 1; solution <= scnt; solution++)
+ {
+ s = testcase_solutionid(solv, problem, solution);
+ solprefix = solv_dupjoin(probprefix, " solution ", s);
+ element = 0;
+ while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &op)) != 0)
+ {
+ if (p == SOLVER_SOLUTION_JOB)
+ s = pool_tmpjoin(pool, solprefix, " deljob ", testcase_job2str(pool, solv->job.elements[op - 1], solv->job.elements[op]));
+ else if (p > 0 && op == 0)
+ s = pool_tmpjoin(pool, solprefix, " erase ", testcase_solvid2str(pool, p));
+ else if (p > 0 && op > 0)
+ {
+ s = pool_tmpjoin(pool, solprefix, " replace ", testcase_solvid2str(pool, p));
+ s = pool_tmpappend(pool, s, " ", testcase_solvid2str(pool, op));
+ }
+ else if (p < 0 && op > 0)
+ s = pool_tmpjoin(pool, solprefix, " allow ", testcase_solvid2str(pool, op));
+ else
+ s = pool_tmpjoin(pool, solprefix, " unknown", 0);
+ strqueue_push(&sq, s);
+ }
+ solv_free(solprefix);
+ }
+ solv_free(probprefix);
+ }
+ }
+
+ if ((resultflags & TESTCASE_RESULT_ORPHANED) != 0)
+ {
+ Queue q;
+
+ queue_init(&q);
+ solver_get_orphaned(solv, &q);
+ for (i = 0; i < q.count; i++)
+ {
+ s = pool_tmpjoin(pool, "orphaned ", testcase_solvid2str(pool, q.elements[i]), 0);
+ strqueue_push(&sq, s);
+ }
+ queue_free(&q);
+ }
+
+ if ((resultflags & TESTCASE_RESULT_RECOMMENDED) != 0)
+ {
+ Queue qr, qs;
+
+ queue_init(&qr);
+ queue_init(&qs);
+ solver_get_recommendations(solv, &qr, &qs, 0);
+ for (i = 0; i < qr.count; i++)
+ {
+ s = pool_tmpjoin(pool, "recommended ", testcase_solvid2str(pool, qr.elements[i]), 0);
+ strqueue_push(&sq, s);
+ }
+ for (i = 0; i < qs.count; i++)
+ {
+ s = pool_tmpjoin(pool, "suggested ", testcase_solvid2str(pool, qs.elements[i]), 0);
+ strqueue_push(&sq, s);
+ }
+ queue_free(&qr);
+ queue_free(&qs);
+ }
+
+ if ((resultflags & TESTCASE_RESULT_UNNEEDED) != 0)
+ {
+ Queue q, qf;
+
+ queue_init(&q);
+ queue_init(&qf);
+ solver_get_unneeded(solv, &q, 0);
+ solver_get_unneeded(solv, &qf, 1);
+ for (i = j = 0; i < q.count; i++)
+ {
+ /* we rely on qf containing a subset of q in the same order */
+ if (j < qf.count && q.elements[i] == qf.elements[j])
+ {
+ s = pool_tmpjoin(pool, "unneeded_filtered ", testcase_solvid2str(pool, q.elements[i]), 0);
+ j++;
+ }
+ else
+ s = pool_tmpjoin(pool, "unneeded ", testcase_solvid2str(pool, q.elements[i]), 0);
+ strqueue_push(&sq, s);
+ }
+ queue_free(&q);
+ queue_free(&qf);
+ }
+ if ((resultflags & TESTCASE_RESULT_ALTERNATIVES) != 0)
+ {
+ char *altprefix;
+ Queue q, rq;
+ int cnt;
+ Id alternative;
+ queue_init(&q);
+ queue_init(&rq);
+ cnt = solver_alternatives_count(solv);
+ for (alternative = 1; alternative <= cnt; alternative++)
+ {
+ Id id, from, chosen;
+ char num[20];
+ int type = solver_get_alternative(solv, alternative, &id, &from, &chosen, &q, 0);
+ altprefix = solv_dupjoin("alternative ", testcase_alternativeid(solv, type, id, from), " ");
+ strcpy(num, " 0 ");
+ if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
+ {
+ char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, from));
+ s = pool_tmpappend(pool, s, " recommends ", testcase_dep2str(pool, id));
+ strqueue_push(&sq, s);
+ }
+ else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
+ {
+ /* map choice rules back to pkg rules */
+ if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
+ id = solver_rule2pkgrule(solv, id);
+ solver_allruleinfos(solv, id, &rq);
+ for (i = 0; i < rq.count; i += 4)
+ {
+ int rtype = rq.elements[i];
+ if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
+ {
+ const char *js = testcase_job2str(pool, rq.elements[i + 2], rq.elements[i + 3]);
+ char *s = pool_tmpjoin(pool, altprefix, num, " job ");
+ s = pool_tmpappend(pool, s, js, 0);
+ strqueue_push(&sq, s);
+ }
+ else if (rtype == SOLVER_RULE_PKG_REQUIRES)
+ {
+ char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, rq.elements[i + 1]));
+ s = pool_tmpappend(pool, s, " requires ", testcase_dep2str(pool, rq.elements[i + 3]));
+ strqueue_push(&sq, s);
+ }
+ }
+ }
+ for (i = 0; i < q.count; i++)
+ {
+ Id p = q.elements[i];
+ if (i >= 9)
+ num[0] = '0' + (i + 1) / 10;
+ num[1] = '0' + (i + 1) % 10;
+ if (-p == chosen)
+ s = pool_tmpjoin(pool, altprefix, num, "+ ");
+ else if (p < 0)
+ s = pool_tmpjoin(pool, altprefix, num, "- ");
+ else if (p >= 0)
+ s = pool_tmpjoin(pool, altprefix, num, " ");
+ s = pool_tmpappend(pool, s, testcase_solvid2str(pool, p < 0 ? -p : p), 0);
+ strqueue_push(&sq, s);
+ }
+ solv_free(altprefix);
+ }
+ queue_free(&q);
+ queue_free(&rq);
+ }
+ if ((resultflags & TESTCASE_RESULT_RULES) != 0)
+ {
+ /* dump all rules */
+ Id rid;
+ SolverRuleinfo rclass;
+ Queue q;
+ int i;
+
+ queue_init(&q);
+ for (rid = 1; (rclass = solver_ruleclass(solv, rid)) != SOLVER_RULE_UNKNOWN; rid++)
+ {
+ char *prefix;
+ switch (rclass)
+ {
+ case SOLVER_RULE_PKG:
+ prefix = "pkg ";
+ break;
+ case SOLVER_RULE_UPDATE:
+ prefix = "update ";
+ break;
+ case SOLVER_RULE_FEATURE:
+ prefix = "feature ";
+ break;
+ case SOLVER_RULE_JOB:
+ prefix = "job ";
+ break;
+ case SOLVER_RULE_DISTUPGRADE:
+ prefix = "distupgrade ";
+ break;
+ case SOLVER_RULE_INFARCH:
+ prefix = "infarch ";
+ break;
+ case SOLVER_RULE_CHOICE:
+ prefix = "choice ";
+ break;
+ case SOLVER_RULE_LEARNT:
+ prefix = "learnt ";
+ break;
+ case SOLVER_RULE_BEST:
+ prefix = "best ";
+ break;
+ case SOLVER_RULE_YUMOBS:
+ prefix = "yumobs ";
+ break;
+ default:
+ prefix = "unknown ";
+ break;
+ }
+ prefix = solv_dupjoin("rule ", prefix, testcase_ruleid(solv, rid));
+ solver_ruleliterals(solv, rid, &q);
+ if (rclass == SOLVER_RULE_FEATURE && q.count == 1 && q.elements[0] == -SYSTEMSOLVABLE)
+ continue;
+ for (i = 0; i < q.count; i++)
+ {
+ Id p = q.elements[i];
+ const char *s;
+ if (p < 0)
+ s = pool_tmpjoin(pool, prefix, " -", testcase_solvid2str(pool, -p));
+ else
+ s = pool_tmpjoin(pool, prefix, " ", testcase_solvid2str(pool, p));
+ strqueue_push(&sq, s);
+ }
+ solv_free(prefix);
+ }
+ queue_free(&q);
+ }
+ if ((resultflags & TESTCASE_RESULT_GENID) != 0)
+ {
+ for (i = 0 ; i < solv->job.count; i += 2)
+ {
+ Id id, id2;
+ if (solv->job.elements[i] != (SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES))
+ continue;
+ id = solv->job.elements[i + 1];
+ s = testcase_dep2str(pool, id);
+ strqueue_push(&sq, pool_tmpjoin(pool, "genid dep ", s, 0));
+ if ((id2 = testcase_str2dep(pool, s)) != id)
+ {
+ s = pool_tmpjoin(pool, "genid roundtrip error: ", testcase_dep2str(pool, id2), 0);
+ strqueue_push(&sq, s);
+ }
+ dump_genid(pool, &sq, id, 1);
+ }
+ }
+ strqueue_sort(&sq);
+ result = strqueue_join(&sq);
+ strqueue_free(&sq);
+ return result;
+}
+
+
+int
+testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
+{
+ Pool *pool = solv->pool;
+ Repo *repo;
+ int i;
+ Id arch, repoid;
+ Id lowscore;
+ FILE *fp;
+ Strqueue sq;
+ char *cmd, *out;
+ const char *s;
+
+ if (!testcasename)
+ testcasename = "testcase.t";
+ if (!resultname)
+ resultname = "solver.result";
+
+ if (mkdir(dir, 0777) && errno != EEXIST)
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not create directory '%s'\n", dir);
+ return 0;
+ }
+ strqueue_init(&sq);
+ FOR_REPOS(repoid, repo)
+ {
+ const char *name = testcase_repoid2str(pool, repoid);
+ char priobuf[50];
+ if (repo->subpriority)
+ sprintf(priobuf, "%d.%d", repo->priority, repo->subpriority);
+ else
+ sprintf(priobuf, "%d", repo->priority);
+ out = pool_tmpjoin(pool, name, ".repo", ".gz");
+ cmd = pool_tmpjoin(pool, "repo ", name, " ");
+ cmd = pool_tmpappend(pool, cmd, priobuf, " ");
+ cmd = pool_tmpappend(pool, cmd, "testtags ", out);
+ strqueue_push(&sq, cmd);
+ out = pool_tmpjoin(pool, dir, "/", out);
+ if (!(fp = solv_xfopen(out, "w")))
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
+ strqueue_free(&sq);
+ return 0;
+ }
+ testcase_write_testtags(repo, fp);
+ if (fclose(fp))
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+ strqueue_free(&sq);
+ return 0;
+ }
+ }
+ /* hmm, this is not optimal... we currently search for the lowest score */
+ lowscore = 0;
+ arch = pool->solvables[SYSTEMSOLVABLE].arch;
+ for (i = 0; i < pool->lastarch; i++)
+ {
+ if (pool->id2arch[i] == 1 && !lowscore)
+ arch = i;
+ if (pool->id2arch[i] > 0x10000 && (!lowscore || pool->id2arch[i] < lowscore))
+ {
+ arch = i;
+ lowscore = pool->id2arch[i];
+ }
+ }
+ cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "unset", 0);
+ for (i = 0; disttype2str[i].str != 0; i++)
+ if (pool->disttype == disttype2str[i].type)
+ break;
+ pool_tmpappend(pool, cmd, " ", disttype2str[i].str ? disttype2str[i].str : "unknown");
+ if (pool->installed)
+ cmd = pool_tmpappend(pool, cmd, " ", testcase_repoid2str(pool, pool->installed->repoid));
+ strqueue_push(&sq, cmd);
+ s = testcase_getpoolflags(solv->pool);
+ if (*s)
+ {
+ cmd = pool_tmpjoin(pool, "poolflags ", s, 0);
+ strqueue_push(&sq, cmd);
+ }
+
+ if (pool->vendorclasses)
+ {
+ cmd = 0;
+ for (i = 0; pool->vendorclasses[i]; i++)
+ {
+ cmd = pool_tmpappend(pool, cmd ? cmd : "vendorclass", " ", pool->vendorclasses[i]);
+ if (!pool->vendorclasses[i + 1])
+ {
+ strqueue_push(&sq, cmd);
+ cmd = 0;
+ i++;
+ }
+ }
+ }
+
+ /* dump disabled packages (must come before the namespace/job lines) */
+ if (pool->considered)
+ {
+ Id p;
+ FOR_POOL_SOLVABLES(p)
+ if (!MAPTST(pool->considered, p))
+ {
+ cmd = pool_tmpjoin(pool, "disable pkg ", testcase_solvid2str(pool, p), 0);
+ strqueue_push(&sq, cmd);
+ }
+ }
+
+ s = testcase_getsolverflags(solv);
+ if (*s)
+ {
+ cmd = pool_tmpjoin(pool, "solverflags ", s, 0);
+ strqueue_push(&sq, cmd);
+ }
+
+ /* now dump all the ns callback values we know */
+ if (pool->nscallback)
+ {
+ Id rid;
+ int d;
+ for (rid = 1; rid < pool->nrels; rid++)
+ {
+ Reldep *rd = pool->rels + rid;
+ if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS)
+ continue;
+ /* evaluate all namespace ids, skip empty results */
+ d = pool_whatprovides(pool, MAKERELDEP(rid));
+ if (!d || !pool->whatprovidesdata[d])
+ continue;
+ cmd = pool_tmpjoin(pool, "namespace ", pool_id2str(pool, rd->name), "(");
+ cmd = pool_tmpappend(pool, cmd, pool_id2str(pool, rd->evr), ")");
+ for (; pool->whatprovidesdata[d]; d++)
+ cmd = pool_tmpappend(pool, cmd, " ", testcase_solvid2str(pool, pool->whatprovidesdata[d]));
+ strqueue_push(&sq, cmd);
+ }
+ }
+
+ for (i = 0; i < solv->job.count; i += 2)
+ {
+ cmd = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
+ cmd = pool_tmpjoin(pool, "job ", cmd, 0);
+ strqueue_push(&sq, cmd);
+ }
+
+ if (resultflags)
+ {
+ char *result;
+ cmd = 0;
+ for (i = 0; resultflags2str[i].str; i++)
+ if ((resultflags & resultflags2str[i].flag) != 0)
+ cmd = pool_tmpappend(pool, cmd, cmd ? "," : 0, resultflags2str[i].str);
+ cmd = pool_tmpjoin(pool, "result ", cmd ? cmd : "?", 0);
+ cmd = pool_tmpappend(pool, cmd, " ", resultname);
+ strqueue_push(&sq, cmd);
+ result = testcase_solverresult(solv, resultflags);
+ if (!strcmp(resultname, "<inline>"))
+ {
+ int i;
+ Strqueue rsq;
+ strqueue_init(&rsq);
+ strqueue_split(&rsq, result);
+ for (i = 0; i < rsq.nstr; i++)
+ {
+ cmd = pool_tmpjoin(pool, "#>", rsq.str[i], 0);
+ strqueue_push(&sq, cmd);
+ }
+ strqueue_free(&rsq);
+ }
+ else
+ {
+ out = pool_tmpjoin(pool, dir, "/", resultname);
+ if (!(fp = fopen(out, "w")))
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
+ solv_free(result);
+ strqueue_free(&sq);
+ return 0;
+ }
+ if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+ solv_free(result);
+ strqueue_free(&sq);
+ fclose(fp);
+ return 0;
+ }
+ if (fclose(fp))
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+ strqueue_free(&sq);
+ return 0;
+ }
+ }
+ solv_free(result);
+ }
+
+ cmd = strqueue_join(&sq);
+ out = pool_tmpjoin(pool, dir, "/", testcasename);
+ if (!(fp = fopen(out, "w")))
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
+ strqueue_free(&sq);
+ return 0;
+ }
+ if (*cmd && fwrite(cmd, strlen(cmd), 1, fp) != 1)
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+ strqueue_free(&sq);
+ fclose(fp);
+ return 0;
+ }
+ if (fclose(fp))
+ {
+ pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+ strqueue_free(&sq);
+ return 0;
+ }
+ solv_free(cmd);
+ strqueue_free(&sq);
+ return 1;
+}
+
+static char *
+read_inline_file(FILE *fp, char **bufp, char **bufpp, int *buflp)
+{
+ char *result = solv_malloc(1024);
+ char *rp = result;
+ int resultl = 1024;
+
+ for (;;)
+ {
+ size_t rl;
+ if (rp - result + 256 >= resultl)
+ {
+ resultl = rp - result;
+ result = solv_realloc(result, resultl + 1024);
+ rp = result + resultl;
+ resultl += 1024;
+ }
+ if (!fgets(rp, resultl - (rp - result), fp))
+ *rp = 0;
+ rl = strlen(rp);
+ if (rl && (rp == result || rp[-1] == '\n'))
+ {
+ if (rl > 1 && rp[0] == '#' && rp[1] == '>')
+ {
+ memmove(rp, rp + 2, rl - 2);
+ rl -= 2;
+ }
+ else
+ {
+ while (rl + 16 > *buflp)
+ {
+ *bufp = solv_realloc(*bufp, *buflp + 512);
+ *buflp += 512;
+ }
+ memmove(*bufp, rp, rl);
+ if ((*bufp)[rl - 1] == '\n')
+ {
+ ungetc('\n', fp);
+ rl--;
+ }
+ (*bufp)[rl] = 0;
+ (*bufpp) = *bufp + rl;
+ rl = 0;
+ }
+ }
+ if (rl <= 0)
+ {
+ *rp = 0;
+ break;
+ }
+ rp += rl;
+ }
+ return result;
+}
+
+static char *
+read_file(FILE *fp)
+{
+ char *result = solv_malloc(1024);
+ char *rp = result;
+ int resultl = 1024;
+
+ for (;;)
+ {
+ size_t rl;
+ if (rp - result + 256 >= resultl)
+ {
+ resultl = rp - result;
+ result = solv_realloc(result, resultl + 1024);
+ rp = result + resultl;
+ resultl += 1024;
+ }
+ rl = fread(rp, 1, resultl - (rp - result), fp);
+ if (rl <= 0)
+ {
+ *rp = 0;
+ break;
+ }
+ rp += rl;
+ }
+ return result;
+}
+
+static int
+str2resultflags(Pool *pool, char *s) /* modifies the string! */
+{
+ int i, resultflags = 0;
+ while (s)
+ {
+ char *se = strchr(s, ',');
+ if (se)
+ *se++ = 0;
+ for (i = 0; resultflags2str[i].str; i++)
+ if (!strcmp(s, resultflags2str[i].str))
+ {
+ resultflags |= resultflags2str[i].flag;
+ break;
+ }
+ if (!resultflags2str[i].str)
+ pool_debug(pool, SOLV_ERROR, "result: unknown flag '%s'\n", s);
+ s = se;
+ }
+ return resultflags;
+}
+
+Solver *
+testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp)
+{
+ Solver *solv;
+ char *buf, *bufp;
+ int bufl;
+ char *testcasedir, *s;
+ int l;
+ char **pieces = 0;
+ int npieces = 0;
+ int prepared = 0;
+ int closefp = !fp;
+ int poolflagsreset = 0;
+ int missing_features = 0;
+ Id *genid = 0;
+ int ngenid = 0;
+
+ if (!fp && !(fp = fopen(testcase, "r")))
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", testcase);
+ return 0;
+ }
+ testcasedir = solv_strdup(testcase);
+ if ((s = strrchr(testcasedir, '/')) != 0)
+ s[1] = 0;
+ else
+ *testcasedir = 0;
+ bufl = 1024;
+ buf = solv_malloc(bufl);
+ bufp = buf;
+ solv = 0;
+ for (;;)
+ {
+ if (bufp - buf + 16 > bufl)
+ {
+ bufl = bufp - buf;
+ buf = solv_realloc(buf, bufl + 512);
+ bufp = buf + bufl;
+ bufl += 512;
+ }
+ if (!fgets(bufp, bufl - (bufp - buf), fp))
+ break;
+ bufp = buf;
+ l = strlen(buf);
+ if (!l || buf[l - 1] != '\n')
+ {
+ bufp += l;
+ continue;
+ }
+ buf[--l] = 0;
+ s = buf;
+ while (*s && (*s == ' ' || *s == '\t'))
+ s++;
+ if (!*s || *s == '#')
+ continue;
+ npieces = 0;
+ /* split it in pieces */
+ for (;;)
+ {
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (!*s)
+ break;
+ pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
+ pieces[npieces++] = s;
+ while (*s && *s != ' ' && *s != '\t')
+ s++;
+ if (*s)
+ *s++ = 0;
+ }
+ pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
+ pieces[npieces] = 0;
+ if (!strcmp(pieces[0], "repo") && npieces >= 4)
+ {
+ Repo *repo = repo_create(pool, pieces[1]);
+ FILE *rfp;
+ int prio, subprio;
+ const char *rdata;
+
+ prepared = 0;
+ if (!poolflagsreset)
+ {
+ poolflagsreset = 1;
+ testcase_resetpoolflags(pool); /* hmm */
+ }
+ if (sscanf(pieces[2], "%d.%d", &prio, &subprio) != 2)
+ {
+ subprio = 0;
+ prio = atoi(pieces[2]);
+ }
+ repo->priority = prio;
+ repo->subpriority = subprio;
+ if (strcmp(pieces[3], "empty") != 0)
+ {
+ const char *repotype = pool_tmpjoin(pool, pieces[3], 0, 0); /* gets overwritten in <inline> case */
+ if (!strcmp(pieces[4], "<inline>"))
+ {
+ char *idata = read_inline_file(fp, &buf, &bufp, &bufl);
+ rdata = "<inline>";
+ rfp = solv_xfopen_buf(rdata, &idata, 0, "rf");
+ }
+ else
+ {
+ rdata = pool_tmpjoin(pool, testcasedir, pieces[4], 0);
+ rfp = solv_xfopen(rdata, "r");
+ }
+ if (!rfp)
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
+ }
+ else if (!strcmp(repotype, "testtags"))
+ {
+ testcase_add_testtags(repo, rfp, 0);
+ fclose(rfp);
+ }
+ else if (!strcmp(repotype, "solv"))
+ {
+ repo_add_solv(repo, rfp, 0);
+ fclose(rfp);
+ }
+#if 0
+ else if (!strcmp(repotype, "helix"))
+ {
+ extern int repo_add_helix(Repo *repo, FILE *fp, int flags);
+ repo_add_helix(repo, rfp, 0);
+ fclose(rfp);
+ }
+#endif
+ else
+ {
+ fclose(rfp);
+ pool_debug(pool, SOLV_ERROR, "testcase_read: unknown repo type for repo '%s'\n", repo->name);
+ }
+ }
+ }
+ else if (!strcmp(pieces[0], "system") && npieces >= 3)
+ {
+ int i;
+
+ /* must set the disttype before the arch */
+ prepared = 0;
+ if (strcmp(pieces[2], "*") != 0)
+ {
+ char *dp = pieces[2];
+ while (dp && *dp)
+ {
+ char *dpe = strchr(dp, ',');
+ if (dpe)
+ *dpe = 0;
+ for (i = 0; disttype2str[i].str != 0; i++)
+ if (!strcmp(disttype2str[i].str, dp))
+ break;
+ if (dpe)
+ *dpe++ = ',';
+ if (disttype2str[i].str)
+ {
+#ifdef MULTI_SEMANTICS
+ if (pool->disttype != disttype2str[i].type)
+ pool_setdisttype(pool, disttype2str[i].type);
+#endif
+ if (pool->disttype == disttype2str[i].type)
+ break;
+ }
+ dp = dpe;
+ }
+ if (!(dp && *dp))
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: system: could not change disttype to '%s'\n", pieces[2]);
+ missing_features = 1;
+ }
+ }
+ if (strcmp(pieces[1], "unset") == 0)
+ pool_setarch(pool, 0);
+ else if (pieces[1][0] == ':')
+ pool_setarchpolicy(pool, pieces[1] + 1);
+ else
+ pool_setarch(pool, pieces[1]);
+ if (npieces > 3)
+ {
+ Repo *repo = testcase_str2repo(pool, pieces[3]);
+ if (!repo)
+ pool_debug(pool, SOLV_ERROR, "testcase_read: system: unknown repo '%s'\n", pieces[3]);
+ else
+ pool_set_installed(pool, repo);
+ }
+ }
+ else if (!strcmp(pieces[0], "job") && npieces > 1)
+ {
+ char *sp;
+ Id how, what;
+ if (prepared <= 0)
+ {
+ pool_addfileprovides(pool);
+ pool_createwhatprovides(pool);
+ prepared = 1;
+ }
+ if (npieces >= 3 && !strcmp(pieces[2], "selection"))
+ {
+ addselectionjob(pool, pieces + 1, npieces - 1, job);
+ continue;
+ }
+ /* rejoin */
+ for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
+ if (*sp == 0)
+ *sp = ' ';
+ how = testcase_str2job(pool, pieces[1], &what);
+ if (how >= 0 && job)
+ queue_push2(job, how, what);
+ }
+ else if (!strcmp(pieces[0], "vendorclass") && npieces > 1)
+ {
+ pool_addvendorclass(pool, (const char **)(pieces + 1));
+ }
+ else if (!strcmp(pieces[0], "namespace") && npieces > 1)
+ {
+ int i = strlen(pieces[1]);
+ s = strchr(pieces[1], '(');
+ if (!s && pieces[1][i - 1] != ')')
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: bad namespace '%s'\n", pieces[1]);
+ }
+ else
+ {
+ Id name, evr, id;
+ Queue q;
+ queue_init(&q);
+ *s = 0;
+ pieces[1][i - 1] = 0;
+ name = pool_str2id(pool, pieces[1], 1);
+ evr = pool_str2id(pool, s + 1, 1);
+ *s = '(';
+ pieces[1][i - 1] = ')';
+ id = pool_rel2id(pool, name, evr, REL_NAMESPACE, 1);
+ for (i = 2; i < npieces; i++)
+ queue_push(&q, testcase_str2solvid(pool, pieces[i]));
+ /* now do the callback */
+ if (prepared <= 0)
+ {
+ pool_addfileprovides(pool);
+ pool_createwhatprovides(pool);
+ prepared = 1;
+ }
+ pool->whatprovides_rel[GETRELID(id)] = pool_queuetowhatprovides(pool, &q);
+ queue_free(&q);
+ }
+ }
+ else if (!strcmp(pieces[0], "poolflags"))
+ {
+ int i;
+ if (!poolflagsreset)
+ {
+ poolflagsreset = 1;
+ testcase_resetpoolflags(pool); /* hmm */
+ }
+ for (i = 1; i < npieces; i++)
+ testcase_setpoolflags(pool, pieces[i]);
+ }
+ else if (!strcmp(pieces[0], "solverflags") && npieces > 1)
+ {
+ int i;
+ if (!solv)
+ {
+ solv = solver_create(pool);
+ testcase_resetsolverflags(solv);
+ }
+ for (i = 1; i < npieces; i++)
+ testcase_setsolverflags(solv, pieces[i]);
+ }
+ else if (!strcmp(pieces[0], "result") && npieces > 1)
+ {
+ char *result = 0;
+ int resultflags = str2resultflags(pool, pieces[1]);
+ const char *rdata;
+ if (npieces > 2)
+ {
+ rdata = pool_tmpjoin(pool, testcasedir, pieces[2], 0);
+ if (!strcmp(pieces[2], "<inline>"))
+ result = read_inline_file(fp, &buf, &bufp, &bufl);
+ else
+ {
+ FILE *rfp = fopen(rdata, "r");
+ if (!rfp)
+ pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
+ else
+ {
+ result = read_file(rfp);
+ fclose(rfp);
+ }
+ }
+ }
+ if (resultp)
+ *resultp = result;
+ else
+ solv_free(result);
+ if (resultflagsp)
+ *resultflagsp = resultflags;
+ }
+ else if (!strcmp(pieces[0], "nextjob") && npieces == 1)
+ {
+ break;
+ }
+ else if (!strcmp(pieces[0], "disable") && npieces == 3)
+ {
+ Id p;
+ if (strcmp(pieces[1], "pkg"))
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: bad disable type '%s'\n", pieces[1]);
+ continue;
+ }
+ if (!prepared)
+ pool_createwhatprovides(pool);
+ prepared = -1;
+ if (!pool->considered)
+ {
+ pool->considered = solv_calloc(1, sizeof(Map));
+ map_init(pool->considered, pool->nsolvables);
+ map_setall(pool->considered);
+ }
+ p = testcase_str2solvid(pool, pieces[2]);
+ if (p)
+ MAPCLR(pool->considered, p);
+ else
+ pool_debug(pool, SOLV_ERROR, "disable: unknown package '%s'\n", pieces[2]);
+ }
+ else if (!strcmp(pieces[0], "feature"))
+ {
+ int i, j;
+ for (i = 1; i < npieces; i++)
+ {
+ for (j = 0; features[j]; j++)
+ if (!strcmp(pieces[i], features[j]))
+ break;
+ if (!features[j])
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: missing feature '%s'\n", pieces[i]);
+ missing_features++;
+ }
+ }
+ if (missing_features)
+ break;
+ }
+ else if (!strcmp(pieces[0], "genid") && npieces > 1)
+ {
+ Id id;
+ /* rejoin */
+ if (npieces > 2)
+ {
+ char *sp;
+ for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
+ if (*sp == 0)
+ *sp = ' ';
+ }
+ genid = solv_extend(genid, ngenid, 1, sizeof(*genid), 7);
+ if (!strcmp(pieces[1], "op") && npieces > 2)
+ {
+ struct oplist *op;
+ for (op = oplist; op->flags; op++)
+ if (!strncmp(pieces[2], op->opname, strlen(op->opname)))
+ break;
+ if (!op->flags)
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown op '%s'\n", pieces[2]);
+ break;
+ }
+ if (ngenid < 2)
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: genid: out of stack\n");
+ break;
+ }
+ ngenid -= 2;
+ id = pool_rel2id(pool, genid[ngenid] , genid[ngenid + 1], op->flags, 1);
+ }
+ else if (!strcmp(pieces[1], "lit"))
+ id = pool_str2id(pool, npieces > 2 ? pieces[2] : "", 1);
+ else if (!strcmp(pieces[1], "null"))
+ id = 0;
+ else if (!strcmp(pieces[1], "dep"))
+ id = testcase_str2dep(pool, pieces[2]);
+ else
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown command '%s'\n", pieces[1]);
+ break;
+ }
+ genid[ngenid++] = id;
+ }
+ else
+ {
+ pool_debug(pool, SOLV_ERROR, "testcase_read: cannot parse command '%s'\n", pieces[0]);
+ }
+ }
+ while (job && ngenid > 0)
+ queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, genid[--ngenid]);
+ genid = solv_free(genid);
+ buf = solv_free(buf);
+ pieces = solv_free(pieces);
+ solv_free(testcasedir);
+ if (!prepared)
+ {
+ pool_addfileprovides(pool);
+ pool_createwhatprovides(pool);
+ }
+ if (!solv)
+ {
+ solv = solver_create(pool);
+ testcase_resetsolverflags(solv);
+ }
+ if (closefp)
+ fclose(fp);
+ if (missing_features)
+ {
+ solver_free(solv);
+ solv = 0;
+ if (resultflagsp)
+ *resultflagsp = 77; /* hack for testsolv */
+ }
+ return solv;
+}
+
+char *
+testcase_resultdiff(const char *result1, const char *result2)
+{
+ Strqueue sq1, sq2, osq;
+ char *r;
+ strqueue_init(&sq1);
+ strqueue_init(&sq2);
+ strqueue_init(&osq);
+ strqueue_split(&sq1, result1);
+ strqueue_split(&sq2, result2);
+ strqueue_sort(&sq1);
+ strqueue_sort(&sq2);
+ strqueue_diff(&sq1, &sq2, &osq);
+ r = osq.nstr ? strqueue_join(&osq) : 0;
+ strqueue_free(&sq1);
+ strqueue_free(&sq2);
+ strqueue_free(&osq);
+ return r;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include "pool.h"
+#include "repo.h"
+#include "solver.h"
+
+#define TESTCASE_RESULT_TRANSACTION (1 << 0)
+#define TESTCASE_RESULT_PROBLEMS (1 << 1)
+#define TESTCASE_RESULT_ORPHANED (1 << 2)
+#define TESTCASE_RESULT_RECOMMENDED (1 << 3)
+#define TESTCASE_RESULT_UNNEEDED (1 << 4)
+#define TESTCASE_RESULT_ALTERNATIVES (1 << 5)
+#define TESTCASE_RESULT_RULES (1 << 6)
+#define TESTCASE_RESULT_GENID (1 << 7)
+
+extern Id testcase_str2dep(Pool *pool, const char *s);
+extern const char *testcase_dep2str(Pool *pool, Id id);
+extern const char *testcase_repoid2str(Pool *pool, Id repoid);
+extern const char *testcase_solvid2str(Pool *pool, Id p);
+extern Repo *testcase_str2repo(Pool *pool, const char *str);
+extern Id testcase_str2solvid(Pool *pool, const char *str);
+extern const char *testcase_job2str(Pool *pool, Id how, Id what);
+extern Id testcase_str2job(Pool *pool, const char *str, Id *whatp);
+extern int testcase_write_testtags(Repo *repo, FILE *fp);
+extern int testcase_add_testtags(Repo *repo, FILE *fp, int flags);
+extern const char *testcase_getsolverflags(Solver *solv);
+extern int testcase_setsolverflags(Solver *solv, const char *str);
+extern void testcase_resetsolverflags(Solver *solv);
+extern char *testcase_solverresult(Solver *solv, int flags);
+extern int testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname);
+extern Solver *testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp);
+extern char *testcase_resultdiff(const char *result1, const char *result2);
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * util.h
+ *
+ */
+
+#ifndef LIBSOLV_TOOLS_UTIL_H
+#define LIBSOLV_TOOLS_UTIL_H
+
+static inline Id
+makeevr(Pool *pool, const char *s)
+{
+ if (!strncmp(s, "0:", 2) && s[2])
+ s += 2;
+ return pool_str2id(pool, s, 1);
+}
+
+/**
+ * split a string
+ */
+#ifndef DISABLE_SPLIT
+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;
+}
+#endif
+
+#ifndef DISABLE_JOIN2
+
+struct joindata {
+ char *tmp;
+ int tmpl;
+};
+
+/* this join does not depend on parsedata */
+static char *
+join2(struct joindata *jd, const char *s1, const char *s2, const char *s3)
+{
+ int l = 1;
+ char *p;
+
+ if (s1)
+ l += strlen(s1);
+ if (s2)
+ l += strlen(s2);
+ if (s3)
+ l += strlen(s3);
+ if (l > jd->tmpl)
+ {
+ jd->tmpl = l + 256;
+ jd->tmp = solv_realloc(jd->tmp, jd->tmpl);
+ }
+ p = jd->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);
+ }
+ *p = 0;
+ return jd->tmp;
+}
+
+static inline char *
+join_dup(struct joindata *jd, const char *s)
+{
+ return s ? join2(jd, s, 0, 0) : 0;
+}
+
+static inline void
+join_freemem(struct joindata *jd)
+{
+ if (jd->tmp)
+ free(jd->tmp);
+ jd->tmp = 0;
+ jd->tmpl = 0;
+}
+
+#endif
+
+#endif /* LIBSOLV_TOOLS_UTIL_H */
--- /dev/null
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: libsolv
+Description: Library for solving packages and reading repositories
+Version: @VERSION@
+Libs: -L${libdir} -lsolvext -lsolv
+Cflags: -I${includedir}
--- /dev/null
+-------------------------------------------------------------------
+Mon Dec 14 15:48:01 CET 2015 - mls@suse.de
+
+- change product links to also look at timestamps [bnc#956443]
+- rework multiversion orphaned handling [bnc#957606]
+- support key type changes in repodata_internalize()
+- allow serialization of REPOKEY_TYPE_DELETED
+- improve appdata handling of installed packages
+- improve performance when run under xen
+- bump version to 0.6.15
+
+-------------------------------------------------------------------
+Mon Oct 5 13:27:25 CEST 2015 - mls@suse.de
+
+- fix bug in recommends handling [bnc#948482]
+- also check installed packages in multiversion handling
+- fix build on Mageia
+- bump version to 0.6.14
+
+-------------------------------------------------------------------
+Fri Sep 25 11:54:02 CEST 2015 - mls@suse.de
+
+- support a generic string for pattern-visible() [bnc#900769]
+- add a SOLVER_ALLOWUNINSTALL job type
+- add ordercycle introspection
+- fix mkmask handling of a zero size
+- support 'recommends' in repo_mdk.c
+- support filelist parsing in installcheck
+- bump version to 0.6.13
+
+-------------------------------------------------------------------
+Tue Sep 1 13:37:11 CEST 2015 - mls@suse.de
+
+- added tcl bindings
+- improve debian ar archive handling
+- bindings: set the CLOEXEC flags in xfopen
+- bindings: support testcase writing [bnc#946752]
+- support REL_ELSE as evr of REL_COND
+- bump version to 0.6.12
+
+-------------------------------------------------------------------
+Tue Jun 2 11:41:10 CEST 2015 - mls@suse.de
+
+- add forgotten sha-512 support to data_skip
+- speed up whatprovides lookup with a new helper array
+- fix dup with allowuninstall
+- improve alreadyinstalled handling of supplements
+- some code cleanup
+- bump version to 0.6.11
+
+-------------------------------------------------------------------
+Sat May 2 11:44:08 UTC 2015 - mrueckert@suse.de
+
+- you really want to use rbconfig there
+
+-------------------------------------------------------------------
+Wed Mar 18 11:04:34 CET 2015 - mls@suse.de
+
+- fix bug in dislike_old_versions that could lead to a segfault
+ [bnc#922352]
+- bump version to 0.6.10
+
+-------------------------------------------------------------------
+Mon Mar 9 16:42:35 CET 2015 - mls@suse.de
+
+- rework splitprovides handling [bnc#921332]
+- improve package choosing code
+- new testcase dependency format
+- add alternatives introspection
+- make reorder_dq_for_jobrules also look at recommends/suggests
+- rework branch handling
+- add parser for rpm rich deps
+- bump version to 0.6.9
+
+-------------------------------------------------------------------
+Wed Jan 14 08:58:46 CET 2015 - ma@suse.de
+
+- fixes to build with swig 3.0.3
+- bump version to 0.6.8
+
+-------------------------------------------------------------------
+Fri Dec 19 08:59:27 CET 2014 - ma@suse.de
+
+- add product:regflavor attribute [bnc#896224]
+- bump version to 0.6.7
+
+-------------------------------------------------------------------
+Tue Oct 7 14:39:23 CEST 2014 - mls@suse.de
+
+- fix bug in reorder_dq_for_jobrules leading to crashes
+ [bnc#899907]
+- rename rpm rules to pkg rules
+- add manpages for the tools
+- bump version to 0.6.6
+
+-------------------------------------------------------------------
+Thu Sep 11 17:33:04 CEST 2014 - mls@suse.de
+
+- support DUCHANGES_ONLYADD flag in diskusage calculation
+ [bnc#783100]
+- add whatmatchesdep to bindings
+- support pool->considered in testcases
+- always call selection_filelist if SELECTION_FILELIST is set
+- support yum style obsolete handling for package splits
+- bump version to 0.6.5
+
+-------------------------------------------------------------------
+Tue Jul 8 14:13:40 CEST 2014 - mls@suse.de
+
+- expand solver_identical fix to applications [bnc#885830]
+- fix instbuddy generation code
+- improve autominimizing implementation to also look at
+ supplements
+- bump version to 0.6.4
+
+-------------------------------------------------------------------
+Fri Jun 13 08:28:12 CEST 2014 - ma@suse.de
+
+- quick fix for [bnc#881320]
+- bump version to 0.6.3
+
+-------------------------------------------------------------------
+Fri Jun 6 11:37:00 CEST 2014 - ma@suse.de
+
+- Provide PRODUCT_REGISTER_TARGET for available products [bnc#881320]
+- bump version to 0.6.2
+
+-------------------------------------------------------------------
+Thu Apr 17 14:47:42 CEST 2014 - mls@suse.de
+
+- support BREAK_ORPHANS and KEEP_ORPHANS solver flags
+- adapt to AppStream 0.6
+- reduce memory usage in repo_write and repodata_internalize
+- make repodata_stringify return the result string
+- bump version to 0.6.1
+
+-------------------------------------------------------------------
+Mon Apr 7 15:36:07 CEST 2014 - mls@suse.de
+
+- add support for sha224/sha384/sha512
+- add userinstalled helper functions
+- improve dataiterator bindings (in an incompatible way)
+- automatically free pool in bindings
+- bump version to 0.6.0 (ABI + bindings API breakage)
+
+-------------------------------------------------------------------
+Wed Mar 26 15:08:46 CET 2014 - mls@suse.de
+
+- fix handling of packages with no update/feature rule [bnc#870195]
+- fix crash when in internalize() when the schemadata gets
+ reallocated
+- fix access to uninitialized memory in repo_empty()
+- a couple of optimizations
+- support REPOKEYWORDS in content parser
+- bindings: don't let str(Datamatch) change the strings
+- fix basename optimization for STRINGEND
+- bump version to 0.5.1
+
+-------------------------------------------------------------------
+Wed Feb 26 15:08:35 CET 2014 - mls@suse.de
+
+- improve appdata.xml parsing [bnc#865293]
+- repo_helix: parse application elements
+- bump version to 0.5.0
+
+-------------------------------------------------------------------
+Fri Feb 21 16:23:58 CET 2014 - mls@suse.de
+
+- fix bug in solver_get_unneeded that could lead to an
+ endless loop [bnc#828764]
+- adapt to new rpm tags for weak dependencies
+- fix pseudo packages obsoleting other pseudo packages
+- optimize unfulfilled rule handling a bit
+- bump version to 0.4.5
+
+-------------------------------------------------------------------
+Fri Feb 14 11:03:18 CET 2014 - mls@suse.de
+
+- always keep job/jobflags in selection_filter()
+- implement COND handling in supplements/enhances
+- improve OR handling in supplements/enhances
+- fix typo in application backlink creation
+- support PRODUCT_ENDOFLIFE
+- store repoid as flexarray
+- also translate autoproduct strings
+- bump version to 0.4.4
+
+-------------------------------------------------------------------
+Mon Jan 27 17:15:41 CET 2014 - mls@suse.de
+
+- add support for autogenerated products
+- support repoid array in product definition
+- bump version to 0.4.3
+
+-------------------------------------------------------------------
+Fri Jan 24 13:50:03 CET 2014 - mls@suse.de
+
+- add -X option to susetags2solv [bnc#860271]
+- fix typos in pool_job2str
+- support DISTRO in content parser
+- make addfilelist more resistant against corrupt rpms
+- encode flags into rpmdb cookie
+- add identical and evrcmp methods in bindings
+- copy checksums in repo_add_rpmdb_reffp
+- repo_autopattern: make sure that the category is valid utf8
+- fix double-free if the number of languages is reduced to zero
+- bump version to 0.4.2
+
+-------------------------------------------------------------------
+Mon Dec 9 11:53:06 CET 2013 - mls@suse.de
+
+- make repo2solv.sh work when appdata support is off
+- enable appdata support for SUSE
+
+-------------------------------------------------------------------
+Tue Dec 3 14:30:17 CET 2013 - mls@suse.de
+
+- support appdata parsing and auto-pattern generation in tools
+- adapt to python3
+- tweak findproblemrule heuristic for conflicts
+- add m68k/ppc64le architectures
+- plug weakrulemap memory hole
+- support debian multiarch annotation
+- support product/pattern/application links
+- bump version to 0.4.1
+
+-------------------------------------------------------------------
+Tue Sep 24 11:14:25 CEST 2013 - mls@suse.de
+
+- do not add back cleandeps_updatepkgs packages [bnc#841781]
+
+-------------------------------------------------------------------
+Mon Sep 23 11:31:28 CEST 2013 - mls@suse.de
+
+- added repodata_lookup_binary
+- fix pattern obsoleting real packages [bnc#834376]
+- add selection_make_matchdeps function to query dependencies
+ other than provides
+- bump version to 0.4.0
+
+-------------------------------------------------------------------
+Wed Aug 21 14:39:54 CEST 2013 - mls@suse.de
+
+- add describe_decision() and unset() methods to bindings
+- do recommends pruning after selecting the highest versions
+- improve filelist handling
+- be more tolerant about bad xml: an empty epoch attribute means no epoch
+- fix another edge case will dup mode and multiversion packages [bnc#828389]
+- bring installcheck up to speed
+
+-------------------------------------------------------------------
+Mon Jun 17 15:54:44 CEST 2013 - mls@suse.de
+
+- add SOLVER_RULE_JOB_UNSUPPORTED and SOLVER_RULE_JOB_UNKNOWN_PACKAGE
+ for better problem reporting
+- start with library documentation
+- adapt to obsoletes handling of new rpm versions
+
+-------------------------------------------------------------------
+Wed Mar 6 16:55:57 CET 2013 - mls@suse.de
+
+- fix dataiterator returning random data in some cases
+- add changelog parser
+- fix nasty bug in selection_filter_rel
+- allow re-run of an existing solver
+- bump version to 0.3.0
+
+-------------------------------------------------------------------
+Mon Jan 14 16:01:04 CET 2013 - mls@suse.de
+
+- trivial_installable: check vendor of affected package to see if
+ a patch should be ignored [bnc#736100]
+- fix trivial installable requires handling
+- bump version to 0.2.4
+
+-------------------------------------------------------------------
+Tue Dec 18 19:20:19 CET 2012 - mls@suse.de
+
+- fix potential access to freed memory
+- improve find_problemrule speed
+- add support for special namespaceprovides jobs
+- bump version to 0.2.3
+
+-------------------------------------------------------------------
+Wed Dec 5 14:37:39 CET 2012 - mls@suse.de
+
+- many Selection improvements
+- fix perl binding memory issue
+- improve file list matching
+- support targeted up/dup with cleandeps
+- bump version to 0.2.2
+
+-------------------------------------------------------------------
+Fri Nov 23 13:57:19 CET 2012 - mls@suse.de
+
+- support targeted up/dup
+- support best rules to enforce the installation of the best package
+- implement selection class to ease package selection
+- fix obsolete handling for debian packages
+- add pool_lookup_deltalocation helper
+- bump version to 0.2.1
+
+-------------------------------------------------------------------
+Thu Oct 18 16:58:10 CEST 2012 - mls@suse.de
+
+- fix susetags parser, it ignored the filelist of the last
+ solvable
+- fix encoding of large values
+- bump version to 0.2.0
+
+-------------------------------------------------------------------
+Mon Jun 25 13:40:58 CEST 2012 - mls@suse.de
+
+- fix typo in repodata_merge_attrs [bnc#767510]
+
+-------------------------------------------------------------------
+Wed May 30 14:46:48 CEST 2012 - mls@suse.de
+
+- fix build for older suse versions
+- fix memory corruption in unneeded calculation when there are
+ product buddies
+
+-------------------------------------------------------------------
+Tue May 8 10:59:39 CEST 2012 - ma@suse.de
+
+- build with swig-2.0.6
+
+-------------------------------------------------------------------
+Mon Apr 23 15:52:26 CEST 2012 - mls@suse.de
+
+- added testcase framework
+- add solver_get_unneeded() to get a list of no longer needed
+ installed packages
+- changed duprule generation to ignore uninstallable packages [bnc#750485]
+- fix memory leaks
+- speed up whatprovides generation
+- support 64bit nums, return files sizes in bytes
+- return errors instead of calling exit()
+- support tilde in rpm version comparison [bnc#466994]
+- 0.1.0
+
+-------------------------------------------------------------------
+Tue Feb 7 16:33:10 CET 2012 - mls@suse.de
+
+- add findutils to the requires of libsolv-tools [bnc#743634]
+
+-------------------------------------------------------------------
+Wed Feb 1 14:06:59 CET 2012 - mls@suse.de
+
+- add cleandeps support for install/update
+- check for cleandeps mistakes (untested)
+
+-------------------------------------------------------------------
+Fri Jan 27 14:10:34 CET 2012 - mls@suse.de
+
+- make repo type code selection modular
+- add more solvable_ functions
+- add dependency getter/setter code to bindings
+- cleanup dup handling
+- hide more internals
+
+-------------------------------------------------------------------
+Mon Oct 24 13:26:18 CEST 2011 - ma@suse.de
+
+- mls fixed package provides/obsoletes, but forgot to write
+ a changes entry.
+
+-------------------------------------------------------------------
+Tue Oct 18 16:18:39 CEST 2011 - ma@suse.de
+
+- Add arch arvm7tnhl and armv7thl
+
+-------------------------------------------------------------------
+Fri Apr 29 14:35:59 CEST 2011 - mls@suse.de
+
+- bup version to 0.17.0 to make it different from 11.4
+- 0.17.0
+
+-------------------------------------------------------------------
+Thu Mar 24 10:04:16 UTC 2011 - mls@suse.de
+
+- add missing else part in rpmdbid2db()
+
+-------------------------------------------------------------------
+Thu Feb 24 17:44:05 CET 2011 - mls@suse.de
+
+- ignore to be dropped orhaned packages when calculating
+ candidates for recommends/supplements installation
+
+-------------------------------------------------------------------
+Wed Feb 2 09:24:42 UTC 2011 - kkaempf@novell.com
+
+- Split off 'applayer' and 'bindings' as a separate package
+ to make the bindings more independant of libsatsolver
+- 0.16.4
+
+-------------------------------------------------------------------
+Tue Jan 25 09:52:48 CET 2011 - ma@suse.de
+
+- Apply patch introducing armv7nhl:armv7h
+
+-------------------------------------------------------------------
+Fri Oct 22 15:51:11 CEST 2010 - ma@suse.de
+
+- updateinfoxml: Correctly parse 'issued date' field.
+- 0.16.1
+
+-------------------------------------------------------------------
+Thu Sep 9 17:30:36 CEST 2010 - mls@suse.de
+
+- bump version to 0.16 to make it different from code11_3 branch
+- 0.16.0
+
+-------------------------------------------------------------------
+Mon Sep 6 13:50:02 UTC 2010 - dmacvicar@novell.com
+
+- ruby bindings: fix bugs regarding include path loading
+ (was hardcoded) and refactor the way the library path is defined
+ (only once in a helper)
+
+-------------------------------------------------------------------
+Mon Sep 6 12:38:21 UTC 2010 - dmacvicar@novell.com
+
+- SLE10SP3 also has vendor_ruby
+
+-------------------------------------------------------------------
+Wed Aug 18 14:11:08 UTC 2010 - kkaempf@novell.com
+
+- refactor ruby-satsolver, pure-Ruby extensions added
+- 0.15.1
+
+-------------------------------------------------------------------
+Thu May 6 17:39:20 CEST 2010 - mls@suse.de
+
+- fix bug in cleandeps code
+- bump version to 0.15 to make it different from code11_2 branch
+- 0.15.0
+
+-------------------------------------------------------------------
+Tue Mar 23 17:24:46 CET 2010 - ma@suse.de
+
+- Parse an installed products <shortsummary> tag [bnc#586303]
+
+-------------------------------------------------------------------
+Mon Mar 22 18:45:42 CET 2010 - mls@suse.de
+
+- dataiterator: reset parent when jumping to a solvid [bnc#589640]
+- 0.14.17
+
+-------------------------------------------------------------------
+Thu Mar 11 22:13:26 CET 2010 - ma@suse.de
+
+- parse global repository ids. [bnc#377568]
+- fix pattern parsing in repomd format. [bnc#585000]
+- 0.14.16
+
+-------------------------------------------------------------------
+Thu Mar 11 12:18:23 CET 2010 - mls@suse.de
+
+- fix language code lookup for fallback languages [bnc#584644]
+- change solvable_lookup_str_lang interface a bit for libzypp
+
+-------------------------------------------------------------------
+Fri Feb 19 17:31:51 CET 2010 - mls@suse.de
+
+- make dup rules work when system repo is not first [bnc#581276]
+- parse pattern visibility flag in repomd format
+- 0.14.15
+
+-------------------------------------------------------------------
+Fri Jan 29 14:48:34 CET 2010 - mls@suse.de
+
+- speed up createwhatprovides when many solvables provide the same dep
+- fix choice rule creation for real (bnc#551637)
+- 0.14.14
+
+-------------------------------------------------------------------
+Mon Jan 18 14:42:27 CET 2010 - mls@suse.de
+
+- set repository:toolversion to 1.0 in common_write
+- 0.14.13
+
+-------------------------------------------------------------------
+Mon Dec 21 14:29:24 CET 2009 - mls@suse.de
+
+- disable update rule in noobsoletes case if installed package is to
+ be kept [bnc#564239]
+- work around rpm bug when --prefix is used [bnc#565525]
+- add support for sparc architecture [bnc#566291]
+- 0.14.12
+
+-------------------------------------------------------------------
+Mon Dec 7 13:59:08 CET 2009 - ma@suse.de
+
+- Support optionally compressed product(s).xml in rpmmd metadata.
+- 0.14.11
+
+-------------------------------------------------------------------
+Mon Nov 2 14:10:23 CET 2009 - mls@suse.de
+
+- look at infarch/dup rules when creating choice rules, makes dup
+ also install 32bit packages [bnc#551637]
+- 0.14.10
+
+-------------------------------------------------------------------
+Wed Oct 14 16:21:32 CEST 2009 - mls@suse.de
+
+- ignore products element so that repo2solv works
+- support MULTI_SEMANTICS
+- add --withsrc option for installcheck
+- add SOLVER_DROP_ORPHANED job type
+- fix dropped package handling in removedisabledconflicts
+- 0.14.9
+
+-------------------------------------------------------------------
+Thu Sep 24 10:27:42 CEST 2009 - mls@suse.de
+
+- fix bug in solvable_lookup_str_base
+
+-------------------------------------------------------------------
+Wed Sep 23 11:10:08 CEST 2009 - mls@suse.de
+
+- get missing translations from other solvables [bnc#386449]
+- also look at triggers when ordering packages
+- add support for REPOKEY_TYPE_BINARY
+- 0.14.8
+
+-------------------------------------------------------------------
+Wed Sep 16 10:56:44 CEST 2009 - mls@suse.de
+
+- use repomdxml to query for the location [bnc#501425]
+- fix assertion failue... again
+- fix fp leak [bnc#535468]
+- 0.14.7
+
+-------------------------------------------------------------------
+Fri Sep 4 11:40:47 CEST 2009 - mls@suse.de
+
+- fix multiversion handling for real
+- fix speed regression in repo_susetags
+- close fd leak [bnc#527506]
+- remove db.h workaround
+- 0.14.6
+
+-------------------------------------------------------------------
+Mon Aug 31 13:57:29 CEST 2009 - mls@suse.de
+
+- fix to build with rpm-4.7
+
+-------------------------------------------------------------------
+Fri Aug 28 11:06:31 CEST 2009 - ma@suse.de
+
+- add support for SOLVER_SOLVABLE_REPO
+- 0.14.5
+
+-------------------------------------------------------------------
+Thu Jul 30 12:49:38 CEST 2009 - mls@suse.de
+
+- speed up file list parsing
+- speed up addfileprovides
+- fix bugs in pubkey handling
+- fix bug in filelist handling when there are no abs paths
+- 0.14.4
+
+-------------------------------------------------------------------
+Fri Jul 17 14:07:07 CEST 2009 - mls@suse.de
+
+- add satversion.h header file
+- many changes regarding the load callback
+- more dataiterator control functions
+- add transaction ordering code
+- add support for file conflict checking
+- 0.14.3
+
+-------------------------------------------------------------------
+Mon Jun 22 16:01:02 CEST 2009 - mls@suse.de
+
+- create libsatsolverext.a
+- 0.14.2
+
+-------------------------------------------------------------------
+Wed May 6 19:52:39 CEST 2009 - ma@suse.de
+
+- Fix to support sha256 hashes (bnc#501425)
+
+-------------------------------------------------------------------
+Wed Apr 1 10:32:43 CEST 2009 - kkaempf@suse.de
+
+- 0.14.1
+ Core changes
+ - fix potential NULL deref in srcrpm handling (mls)
+ - clean up and fix suggested/recommended list creation code (mls)
+ - kill obsolete and broken patchxml support (mls)
+ - replace old solver_problemruleinfo with new solver_ruleinfo
+ function (mls)
+ - add solvable_selfprovidedep() function (mls)
+ - add noinfarchcheck solver option (mls)
+ - clone job queue to make the code more flexible (mls)
+ - rewrite policy rule disabling/re-enabling (bnc#481836) (mls)
+ - fix out-of-bounds in solver_printproblem() (mls)
+ Bindings changes
+ - provide 'Repo.attr(String)' accessor function (kkaempf)
+ - provide 'Solver.sizechange' to compute the changes of the
+ installed size after a transaction is applied (kkaempf)
+ - solver result iterators for Python (jblunck)
+ - add %newobject to track newly created objects better (kkaempf)
+ - generate rdoc documentation from swig input files (kkaempf)
+ - add a no-op Pool destructor (bnc#483252) (kkaempf)
+ - provide access to all flags and settings (kkaempf)
+
+-------------------------------------------------------------------
+Wed Mar 4 14:39:00 CET 2009 - mls@suse.de
+
+- fix problem_to_solutions segfault
+- bump version to 0.14 to make it different from code11 branch
+- 0.14.0
+
+-------------------------------------------------------------------
+Mon Mar 2 18:20:22 CET 2009 - mls@suse.de
+
+- add solver_trivial_installable() to fix multiversion patches [bnc#480303]
+- 0.13.5
+
+-------------------------------------------------------------------
+Wed Feb 18 16:48:41 CET 2009 - ma@suse.de
+
+- Use correct namespace (e.g. pattern:) even if solvable has no name [bnc#470011]
+
+-------------------------------------------------------------------
+Fri Jan 30 14:26:42 CET 2009 - mls@suse.de
+
+- fix weakmap boundary check
+- 0.13.3
+
+-------------------------------------------------------------------
+Tue Jan 27 13:12:30 CET 2009 - ma@suse.de
+
+- Ignore trailing whitespace in content file parser.
+
+-------------------------------------------------------------------
+Mon Jan 26 13:55:35 CET 2009 - mls@suse.de
+
+- fix segfault caused by whatprovides reallocation [bnc#468313]
+- 0.13.1
+
+-------------------------------------------------------------------
+Tue Jan 20 17:12:17 CET 2009 - ma@suse.de
+
+- repo_rpmdb: Fix conversion to UTF8
+
+-------------------------------------------------------------------
+Fri Dec 5 14:43:55 CET 2008 - kkaempf@suse.de
+
+- enhance bindings to provide reasons for solver decisions
+
+-------------------------------------------------------------------
+Mon Dec 1 11:50:12 CET 2008 - mls@suse.de
+
+- prefer patterns again until there's a real fix [bnc#450226]
+
+-------------------------------------------------------------------
+Mon Dec 1 11:13:55 CET 2008 - mls@suse.de
+
+- susetags: fix file dependency parsing [bnc#450286]
+
+-------------------------------------------------------------------
+Fri Nov 28 18:29:08 CET 2008 - mls@suse.de
+
+- correct problem solving algorithm
+- 0.13.0
+
+-------------------------------------------------------------------
+Fri Nov 21 16:54:44 CET 2008 - dmacvicar@suse.de
+
+- remove unused updaterepokey, replaced by repo
+ product information
+
+-------------------------------------------------------------------
+Thu Nov 20 10:32:01 CET 2008 - dmacvicar@suse.de
+
+- support content, distro and updates tag properly
+- remove the unused products tag
+
+-------------------------------------------------------------------
+Mon Nov 17 14:30:08 CET 2008 - ma@suse.de
+
+- Parse RELEASE tag from contentfile (bnc #444978)
+- 0.12.3
+
+-------------------------------------------------------------------
+Fri Nov 14 18:00:19 CET 2008 - mls@suse.de
+
+- fix bugs in multiversion handling
+- bring perl bindings up to speed
+- 0.12.2
+
+-------------------------------------------------------------------
+Fri Nov 7 18:26:07 CET 2008 - ma@suse.de
+
+- fix SOLVID_POS dataiterator handling
+
+-------------------------------------------------------------------
+Fri Nov 7 11:56:37 CET 2008 - kkaempf@suse.de
+
+- make repo_zyppdb more robust (bnc#441043)
+
+-------------------------------------------------------------------
+Thu Nov 6 09:49:51 CET 2008 - kkaempf@suse.de
+
+- Add 'user_data' argument to all applayer iterator callbacks
+ (bnc#418491)
+
+-------------------------------------------------------------------
+Wed Oct 29 12:36:57 CET 2008 - ma@suse.de
+
+- Add 'sh4' architectures.
+
+-------------------------------------------------------------------
+Tue Oct 28 16:55:03 CET 2008 - ma@suse.de
+
+- Add 'arm' architectures.
+
+-------------------------------------------------------------------
+Mon Oct 27 10:54:36 CET 2008 - kkaempf@suse.de
+
+- Improve error detection and reporting when parsing 'content' file
+
+-------------------------------------------------------------------
+Fri Oct 24 16:03:13 CEST 2008 - ma@suse.de
+
+- Remember the /etc/products.d enties filename in .solv
+ (bnc #432932)
+- 0.12.1
+
+-------------------------------------------------------------------
+Thu Oct 23 10:49:40 CEST 2008 - mrueckert@suse.de
+
+- add zlib-devel as buildrequires, cmake is looking for it
+ explicitely. rpm-devel used to require it but you dont really
+ need it to link against librpm*
+- requires rpm-devel in the devel package, it is required to link
+ against libsatsolver.
+
+-------------------------------------------------------------------
+Wed Oct 22 13:00:56 CEST 2008 - mls@suse.de
+
+- add pool_set_installed()
+- drop "installed" arguments from some functions
+- 0.12.0
+
+-------------------------------------------------------------------
+Wed Oct 22 13:00:25 CEST 2008 - dmacvicar@suse.de
+
+- support the new standarized tags available in
+ createrepo > 0.9.6
+ saving of the distro tag still missing.
+
+-------------------------------------------------------------------
+Thu Oct 16 00:50:47 CEST 2008 - mls@suse.de
+
+- make iterator work with completely empty repos [bnc#435838]
+
+-------------------------------------------------------------------
+Tue Oct 14 18:28:57 CEST 2008 - mls@suse.de
+
+- the big solv data change
+ * incompatible new file format
+ * repodata handles are solvable ids
+ * no more extra handles
+ * no need to call repodata_extend anymore
+- work around solver dup repo priority bug, real fix follows soon
+- implement releasever
+- repo_products.c is now more robust
+
+-------------------------------------------------------------------
+Mon Oct 13 16:50:14 CEST 2008 - kkaempf@suse.de
+
+- make product parsing more robust (bnc#433362)
+
+-------------------------------------------------------------------
+Thu Oct 2 18:46:55 CEST 2008 - ma@suse.de
+
+- Product arttributes: removed FLAVOR and REFERENCES, added PRODUCTLINE.
+- revision 11233
+- 0.11.0
+
+-------------------------------------------------------------------
+Tue Sep 30 13:03:10 CEST 2008 - ma@suse.de
+
+- repo_content.c: fix broken dependency parsing.
+- revision 11214
+
+-------------------------------------------------------------------
+Mon Sep 29 14:53:09 CEST 2008 - ma@suse.de
+
+- rpms2solv failed to write out most solvable data (bnc #422338).
+- revision 11201
+
+-------------------------------------------------------------------
+Fri Sep 26 11:54:34 CEST 2008 - kkaempf@suse.de
+
+- new fallback strategy for installed products in rpmdb2solv
+ try /etc/products.d (code11 style) first
+ if this fails, try /var/lib/zypp/db/products/*
+ if this fails, fallback to /etc/*-release
+ (bnc#429177)
+- 0.10.16
+
+-------------------------------------------------------------------
+Thu Sep 25 17:14:42 CEST 2008 - kkaempf@suse.de
+
+- fully support Dataiterator in Python and Ruby bindings.
+- 0.10.15
+
+-------------------------------------------------------------------
+Thu Sep 25 13:53:54 CEST 2008 - dmacvicar@suse.de
+
+- add support for keywords in susedata
+
+-------------------------------------------------------------------
+Wed Sep 24 10:55:01 CEST 2008 - kkaempf@suse.de
+
+- parse /etc/<xyz>-release if no /etc/products.d present
+ (bnc#429177)
+- 0.10.14
+
+-------------------------------------------------------------------
+Mon Sep 22 12:51:51 CEST 2008 - dmacvicar@suse.de
+
+- ability to parse suseinfo.xml for extended repomd.xml attributes
+- fix susedata.xml parsing
+- add CPE attribute to installed product
+- real fix for segfault in multiarch parsing (bnc#427271)
+- 0.10.13
+
+-------------------------------------------------------------------
+Thu Sep 18 14:44:31 CEST 2008 - dmacvicar@suse.de
+
+- fix segfault in multiarch parsing (bnc#427271)
+
+-------------------------------------------------------------------
+Wed Sep 17 14:10:23 CEST 2008 - mls@suse.de
+
+- fix segfault in provides iterator
+- 0.10.12
+
+-------------------------------------------------------------------
+Fri Sep 12 17:32:02 CEST 2008 - dmacvicar@suse.de
+
+- support for susedata.xml
+
+-------------------------------------------------------------------
+Fri Sep 12 14:01:11 CEST 2008 - dmacvicar@suse.de
+
+- add repo_add_poolstr_array
+- move updates="key,key.." to repomd.xml
+- make product url ids more extensible
+- 0.10.11
+
+-------------------------------------------------------------------
+Thu Sep 11 14:30:16 CEST 2008 - dmacvicar@suse.de
+
+- add REPOSITORY_UPDATES to match product -> repos
+- make updateinfo.xml support id attribute in collection that
+ leads to insert that the repository updates that id.
+
+-------------------------------------------------------------------
+Wed Sep 10 18:11:10 CEST 2008 - dmacvicar@suse.de
+
+- create one product per BASEARCHS
+
+-------------------------------------------------------------------
+Wed Sep 10 14:19:26 CEST 2008 - ma@suse.de
+
+- repo_products: Parse schemeversion, propagate product
+ updaterepokey and flavor. Fix segfault on malformed
+ xml.
+- 0.10.10
+
+-------------------------------------------------------------------
+Wed Sep 10 11:57:27 CEST 2008 - dmacvicar@suse.de
+
+- accept the PATTERNS tag in content file
+
+-------------------------------------------------------------------
+Tue Sep 9 21:26:31 CEST 2008 - kkaempf@suse.de
+
+- rpmdb2solv changes:
+ - fix bug when parsing multiple products
+ - adapt to .prod file as of 9/9/08 7:20pm
+- 0.10.9
+
+-------------------------------------------------------------------
+Tue Sep 9 17:52:30 CEST 2008 - ma@suse.de
+
+- Reenable -Werror and fix bindings.
+- 0.10.8
+
+-------------------------------------------------------------------
+Tue Sep 9 11:50:39 CEST 2008 - dmacvicar@suse.de
+
+- disable -Werror for swig generated stuff
+
+-------------------------------------------------------------------
+Fri Sep 5 18:59:35 CEST 2008 - kkaempf@suse.de
+
+- adapt /etc/product.d parser to generated .prod files.
+
+-------------------------------------------------------------------
+Fri Sep 5 13:29:47 CEST 2008 - ma@suse.de
+
+- tools/repo_susetags.c: Parse packages vendor (bnc #422493).
+- 0.10.7
+
+-------------------------------------------------------------------
+Thu Sep 4 12:30:06 CEST 2008 - kkaempf@suse.de
+
+- tools/rpmdb2solv: Adapt to xml-based /etc/products.d
+- tools/rpmdb2solv: Add '-a <attribute>' to print
+ distribution.target attribute of baseproduct.
+
+-------------------------------------------------------------------
+Tue Sep 2 12:17:03 CEST 2008 - mls@suse.de
+
+- make solver includes use "" instead of <>, fixes bnc#415920
+- implement otherproviders()
+- make patches do nevr matching
+- make patch conflicts work with multiversion
+- new job commands, now combinded from job type and select type
+- support for distupgrade mode
+- make SOLVER_ERASE_SOLVABLE work
+- also check obsoletes when disabling update rules
+- 0.10.6
+
+-------------------------------------------------------------------
+Fri Aug 22 18:04:12 CEST 2008 - dmacvicar@suse.de
+
+- add support for extensible metadata over primary +
+ diskusage
+- 0.10.5
+
+-------------------------------------------------------------------
+Fri Aug 15 16:29:00 CEST 2008 - kkaempf@suse.de
+
+- ensure existance of product solvable in repo_content(bnc#417594)
+
+-------------------------------------------------------------------
+Fri Aug 15 15:00:32 CEST 2008 - kkaempf@suse.de
+
+- follow /etc/products.d/baseproduct and mark product as 'base'
+
+-------------------------------------------------------------------
+Fri Aug 15 14:26:29 CEST 2008 - kkaempf@suse.de
+
+- Implement pre-code11 fallback for products, parse /etc/*-release
+ if /etc/products.d is not available.
+
+-------------------------------------------------------------------
+Wed Aug 13 18:06:41 CEST 2008 - kkaempf@suse.de
+
+- provide installtime for installed products.
+
+-------------------------------------------------------------------
+Wed Aug 13 15:42:36 CEST 2008 - dmacvicar@suse.de
+
+- include new file search capability commited by matz
+ (SEARCH_FILES)
+- 0.10.4
+
+-------------------------------------------------------------------
+Wed Aug 13 10:31:01 CEST 2008 - kkaempf@suse.de
+
+- Honor rpmdb2solv's '-r <root>' also for the products.d path.
+
+-------------------------------------------------------------------
+Tue Aug 12 14:22:29 CEST 2008 - kkaempf@suse.de
+
+- Add .prod parsing for 'installed' products to rpmdb2solv.
+- Improve python-bindings, add iterators.
+
+-------------------------------------------------------------------
+Thu Aug 7 22:00:55 CEST 2008 - dmacvicar@suse.de
+
+- implement relogin suggested support (fate#304889)
+
+-------------------------------------------------------------------
+Thu Aug 7 13:36:59 CEST 2008 - ma@suse.de
+
+- Susetags: Allow whitespace in file provides generated by
+ autobuild (bnc#415115)
+
+-------------------------------------------------------------------
+Fri Aug 1 18:59:22 CEST 2008 - dmacvicar@suse.de
+
+- insert the checksum in rpmmd generated solv files
+ (bnc#414002)
+- 0.10.3
+
+-------------------------------------------------------------------
+Tue Jul 29 10:53:22 CEST 2008 - mls@suse.de
+
+- resolve job rules before installing system packages [bnc#411086]
+- no more freshens. R.I.P.
+- make repo2solv.sh also take repomd.xml in count
+- install repomdxml2solv
+- add Packager, Build Host, Distribution
+- disallow arch/vendor changes even if the package name changes
+
+-------------------------------------------------------------------
+Sat Jul 12 02:32:15 CEST 2008 - dmacvicar@suse.de
+
+- infrastructure to save generated and expiration time
+ stamp in rpm-md repositories (fate #301904)
+- 0.10.1
+
+-------------------------------------------------------------------
+Wed Jul 9 16:25:36 CEST 2008 - ma@suse.de
+
+- Fix repo_content dependency parsing. Parser may lose up to
+ two trailing dependencies.
+
+-------------------------------------------------------------------
+Tue Jul 1 14:54:38 CEST 2008 - kkaempf@suse.de
+
+- rename language bindings to {perl,python,ruby}-satsolver
+ to follow naming conventions.
+
+-------------------------------------------------------------------
+Mon Jun 30 23:55:20 CEST 2008 - dmacvicar@suse.de
+
+- forward port
+- add message tag to updateinfo.xml for displaying
+ messages in the user interface
+- Fix missing self provides for patches (bnc #397132).
+- do not reorder binary rules if they are not rpm rules [bnc#397456]
+- 0.10.0
+
+-------------------------------------------------------------------
+Mon Jun 2 11:47:32 CEST 2008 - coolo@suse.de
+
+- calculate recommendation list also if ignorealreadyrecommended is set,
+ as some recommendations would be missing otherwise
+- make dependency output less confusing (bnc#396309)
+- 0.9.0
+
+-------------------------------------------------------------------
+Tue May 27 22:34:05 CEST 2008 - coolo@suse.de
+
+- compile with RPM_OPT_FLAGS
+
+-------------------------------------------------------------------
+Fri May 23 21:07:01 CEST 2008 - mls@suse.de
+
+- add "zypper" flag
+- add "ignorealreadyrecommended" aka "zypper" solver option
+
+-------------------------------------------------------------------
+Thu May 22 20:16:22 CEST 2008 - coolo@suse.de
+
+- fixed language support in patterns (bnc#386524)
+
+-------------------------------------------------------------------
+Mon May 19 14:53:01 CEST 2008 - dmacvicar@suse.de
+
+- make solvable_look_bool more robust by allowing both
+ the void or the num == 1 strategy.
+
+-------------------------------------------------------------------
+Thu May 15 16:05:50 CEST 2008 - coolo@suse.de
+
+- fix susetags parser
+
+------------------------------------------------------------------
+Wed May 14 16:41:26 CEST 2008 - dmacvicar@suse.de
+
+- mls fix of satisfied patterns
+- 0.0.33
+
+-------------------------------------------------------------------
+Tue May 13 17:14:26 CEST 2008 - dmacvicar@suse.de
+
+- use repodata_set_void for pattern visible attr
+
+-------------------------------------------------------------------
+Tue May 13 14:11:30 CEST 2008 - jreidinger@suse.cz
+
+- read description dir path from content file (bnc #389414)
+
+-------------------------------------------------------------------
+Tue May 13 12:20:58 CEST 2008 - dmacvicar@suse.de
+
+- a boolean is not a num attribute set to 1, but just a existing void
+ attribute. (bnc#388818)
+
+-------------------------------------------------------------------
+Mon May 12 10:16:20 CEST 2008 - coolo@suse.de
+
+- provide libsatsolver to fix requires of debuginfo
+
+-------------------------------------------------------------------
+Sat May 10 15:33:15 CEST 2008 - coolo@suse.de
+
+- resubmit clean tar
+
+-------------------------------------------------------------------
+Fri May 9 21:56:43 CEST 2008 - ma@suse.de
+
+- Parse the products LABEL in content file to SUMMARY.
+
+-------------------------------------------------------------------
+Fri May 9 20:26:52 CEST 2008 - dmacvicar@suse.de
+
+- recognize 1 as true for reboot suggested and
+ restart suggested (bnc#388818)
+
+-------------------------------------------------------------------
+Fri May 9 16:04:42 CEST 2008 - kkaempf@suse.de
+
+- move 'helix2solv' from satsolver-tools to satsolver-devel package
+ (bnc#388595)
+
+-------------------------------------------------------------------
+Mon May 5 16:24:14 CEST 2008 - coolo@suse.de
+
+- add obsoleteusesprovides and implicitobsoleteusesprovides solver
+ flags
+- speed up solving by not recreating the watch chains every time
+ some rule is enabled/disabled. To do this, the "disabled" flag
+ had to be moved from w1 to d.
+- fix bug that broke rule disabling when "forceResolve" was true
+- fix bug in update rule calculation
+- speed up solver a bit by creating a queue holding all assertion
+ rules, so we do not have to scan all rules for assertions
+- parse also DISTPRODUCT and DISTVERSION (for registration), and the other
+ (often unused) attributes of products.
+
+-------------------------------------------------------------------
+Mon Apr 28 19:36:54 CEST 2008 - coolo@suse.de
+
+- (De-)Serialize structured types. Dataiterator and repo_search support
+ them too, but not yet nested, so that is unsupported for now.
+- skipping kinds in matcher when a flag is specified.
+- make --force behave a bit more like --noforce
+
+-------------------------------------------------------------------
+Fri Apr 25 19:23:51 CEST 2008 - coolo@suse.de
+
+- detect and skip empty lines (bnc#381828)
+- fix endless loop
+- move debug functions to solverdebug.c
+- do not delete negative bitfield entries [bnc#381908]
+- add "showinstalledrecommended" option to make the solver
+ put installed packages on the suggestions/recommendations
+ queues
+- Fix content parsing if PRODUCT isn't the first entry.
+- add more statistics
+- add two assertions
+- add support for susetags filelist
+- plug mem join2 leak
+- fix anchoring of filelist data
+- susetags move files added to provides back into filelist
+- ignore packages.FL for now
+
+-------------------------------------------------------------------
+Sun Apr 20 18:25:14 CEST 2008 - coolo@suse.de
+
+- fix build
+
+-------------------------------------------------------------------
+Sat Apr 19 08:10:51 CEST 2008 - coolo@suse.de
+
+- fix probleminfo if solvable conflicts with itself and has no requires
+- Fix parsing dep lines of content files (bnc #380396).
+- C++-guard also solver.h
+- add support for feature rules
+- fix a couple of small bugs
+- use new interface
+
+-------------------------------------------------------------------
+Wed Apr 16 17:44:20 CEST 2008 - coolo@suse.de
+
+- fix (rare) case of crashing solver
+
+-------------------------------------------------------------------
+Tue Apr 15 09:06:36 CEST 2008 - coolo@suse.de
+
+- some fixes around updateinfo parsing
+
+-------------------------------------------------------------------
+Wed Apr 9 16:09:36 CEST 2008 - jkupec@suse.cz
+
+- enable regex matching in Dataiterator
+
+-------------------------------------------------------------------
+Fri Apr 4 15:45:44 CEST 2008 - coolo@suse.de
+
+- package deptestomatic in -devel
+
+-------------------------------------------------------------------
+Mon Mar 31 16:25:03 CEST 2008 - coolo@suse.de
+
+- truly restart when analyze_unsolvable is hit (fixes bnc#368209)
+- make it work with really large directories
+
+-------------------------------------------------------------------
+Mon Mar 24 15:31:18 CET 2008 - coolo@suse.de
+
+- install rpms2solv
+
+-------------------------------------------------------------------
+Mon Mar 17 16:10:22 CET 2008 - matz@suse.de
+
+- Initialize all allocated array members for blocky arrays (when it
+ matters, e.g. when extending also in blocks - bnc#371137)
+
+-------------------------------------------------------------------
+Fri Mar 14 08:37:47 CET 2008 - coolo@suse.de
+
+- fix build on other distris
+- fix requires of tools
+
+-------------------------------------------------------------------
+Wed Mar 12 11:45:21 CET 2008 - coolo@suse.de
+
+- store datadir for susetags
+- fixing assertion in rules learning
+
+-------------------------------------------------------------------
+Fri Mar 7 10:51:09 CET 2008 - coolo@suse.de
+
+- several fixes in whatprovides (possibly root cause of bnc#367210)
+
+-------------------------------------------------------------------
+Mon Feb 25 12:59:00 CET 2008 - coolo@suse.de
+
+- fixing rpmdb2solv argument handling
+
+-------------------------------------------------------------------
+Fri Feb 22 11:09:05 CET 2008 - coolo@suse.de
+
+- support rpmdb2solv -r for chroots
+
+-------------------------------------------------------------------
+Thu Feb 21 18:14:41 CET 2008 - coolo@suse.de
+
+- fix susetags parser for so called full trees
+
+-------------------------------------------------------------------
+Wed Feb 20 13:23:31 CET 2008 - coolo@suse.de
+
+- no longer link against db43 but against rpmlib
+
+-------------------------------------------------------------------
+Tue Feb 19 15:01:55 CET 2008 - coolo@suse.de
+
+- fix requires/obsoletes
+
+-------------------------------------------------------------------
+Tue Feb 19 08:10:01 CET 2008 - coolo@suse.de
+
+- mls is back from vacation - several fixes and enhancements
+
+-------------------------------------------------------------------
+Fri Feb 15 11:04:09 CET 2008 - coolo@suse.de
+
+- several fixes for the solv files
+
+-------------------------------------------------------------------
+Tue Feb 12 18:29:43 CET 2008 - kkaempf@suse.de
+
+- add libappsatsolver, an application layer to ease coding
+ against sat-solver.
+
+-------------------------------------------------------------------
+Tue Feb 12 17:14:04 CET 2008 - coolo@suse.de
+
+- let susetags parse vendors
+
+-------------------------------------------------------------------
+Thu Feb 7 18:41:05 CET 2008 - coolo@suse.de
+
+- rename libsatsolver in satsolver-tools
+- several updates and fixes
+
+-------------------------------------------------------------------
+Fri Feb 1 13:59:53 CET 2008 - coolo@suse.de
+
+- really don't strip
+
+-------------------------------------------------------------------
+Mon Jan 14 17:14:03 CET 2008 - coolo@suse.de
+
+- update from SVN:
+ - various fixes
+ - less logging
+
+-------------------------------------------------------------------
+Tue Jan 8 16:02:26 CET 2008 - coolo@suse.de
+
+- update to SVN again and don't strip
+
+-------------------------------------------------------------------
+Sat Dec 22 19:02:57 CET 2007 - coolo@suse.de
+
+- update to SVN so libzypp compiles again
+
+-------------------------------------------------------------------
+Fri Nov 30 16:01:39 CET 2007 - schubi@suse.de
+
+- update for libzypp integration
+
+-------------------------------------------------------------------
+Fri Nov 16 12:17:13 CET 2007 - coolo@suse.de
+
+- update to SVN again to make libzypp compilable again
+
+-------------------------------------------------------------------
+Wed Nov 14 16:10:21 CET 2007 - schubi@suse.de
+
+- further develpment. bugfix, logging, docu,...
+
+-------------------------------------------------------------------
+Mon Nov 12 13:44:52 CET 2007 - coolo@suse.de
+
+- update to the latest version from SVN
+ - compilation fixes
+ - policy engine
+
+-------------------------------------------------------------------
+Thu Nov 8 13:14:20 CET 2007 - coolo@suse.de
+
+- update to the latest version from SVN
+
+-------------------------------------------------------------------
+Tue Oct 2 15:26:36 CEST 2007 - kkaempf@suse.de
+
+- Initial release
+
--- /dev/null
+#
+# spec file for package libsolv
+#
+# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
+#
+
+Name: libsolv
+Version: @VERSION@
+Release: 0
+Url: https://github.com/openSUSE/libsolv
+Source: libsolv-%{version}.tar.bz2
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+
+%bcond_without enable_static
+%bcond_without disable_shared
+%bcond_without perl_binding
+%bcond_without python_binding
+%bcond_without ruby_binding
+%bcond_with zypp
+
+%if 0%{?mandriva_version}
+# force this version on mandriva
+BuildRequires: libneon0.26-devel
+%endif
+%if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
+BuildRequires: db-devel
+%endif
+%if 0%{?suse_version}
+%if 0%{?suse_version} < 1030
+BuildRequires: expat
+%else
+BuildRequires: libexpat-devel
+%endif
+%if 0%{?suse_version} < 1100
+BuildRequires: graphviz
+%endif
+%if 0%{?suse_version} > 1020
+BuildRequires: fdupes
+%endif
+%else
+BuildRequires: expat-devel
+%endif
+BuildRequires: cmake
+BuildRequires: gcc-c++
+BuildRequires: rpm-devel
+BuildRequires: zlib-devel
+
+%if %{with perl_binding}
+BuildRequires: perl
+%if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
+BuildRequires: perl-devel
+%endif
+BuildRequires: swig
+%endif
+%if %{with ruby_binding}
+%global ruby_vendorarch %(ruby -r rbconfig -e "puts RbConfig::CONFIG['vendorarchdir'].nil? ? RbConfig::CONFIG['sitearchdir'] : RbConfig::CONFIG['vendorarchdir']")
+BuildRequires: ruby
+BuildRequires: ruby-devel
+BuildRequires: swig
+%endif
+%if %{with python_binding}
+%global python_sitearch %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib(True);")
+BuildRequires: python-devel
+BuildRequires: swig
+%endif
+
+Summary: A new approach to package dependency solving
+License: BSD-3-Clause
+Group: Development/Libraries/C and C++
+
+%description
+A new approach to package dependency solving
+
+%if !%{with disable_shared}
+%package -n libsolv@LIBSOLV_SOVERSION@
+Summary: A new approach to package dependency solving
+Group: Development/Libraries/C and C++
+
+%description -n libsolv@LIBSOLV_SOVERSION@
+A new approach to package dependency solving
+
+%endif
+%package devel
+Summary: A new approach to package dependency solving
+Group: Development/Libraries/C and C++
+%if !%{with disable_shared}
+Requires: libsolv@LIBSOLV_SOVERSION@ = %version
+%endif
+Requires: rpm-devel
+Conflicts: libsatsolver-devel
+
+%description devel
+Development files for libsolv, a new approach to package dependency solving
+
+%package tools
+Summary: A new approach to package dependency solving
+Group: Development/Libraries/C and C++
+Obsoletes: satsolver-tools < 0.18
+Provides: satsolver-tools = 0.18
+Conflicts: satsolver-tools-obsolete
+Requires: gzip bzip2 coreutils findutils
+
+%description tools
+A new approach to package dependency solving.
+
+%package demo
+Summary: Applications demoing the libsolv library
+Group: System/Management
+Requires: curl
+%if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
+Requires: gnupg2
+%endif
+%if 0%{?suse_version}
+Requires: gpg2
+%endif
+Conflicts: libsatsolver-demo
+
+%description demo
+Applications demoing the libsolv library.
+
+%package -n ruby-solv
+Summary: Ruby bindings for the libsolv library
+Group: Development/Languages/Ruby
+
+%description -n ruby-solv
+Ruby bindings for sat solver.
+
+%package -n python-solv
+%if 0%{?py_requires:1} && %{with python_binding}
+%py_requires
+%endif
+Summary: Python bindings for the libsolv library
+Group: Development/Languages/Python
+
+%description -n python-solv
+Python bindings for sat solver.
+
+%package -n perl-solv
+Requires: perl = %{perl_version}
+Summary: Perl bindings for the libsolv library
+Group: Development/Languages/Perl
+
+%description -n perl-solv
+Perl bindings for sat solver.
+
+%prep
+%setup -n libsolv-%{version}
+
+%build
+export CFLAGS="$RPM_OPT_FLAGS"
+export CXXFLAGS="$CFLAGS"
+
+CMAKE_FLAGS=
+%if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
+CMAKE_FLAGS="-DFEDORA=1"
+%endif
+%if 0%{?suse_version}
+CMAKE_FLAGS="-DSUSE=1 -DENABLE_APPDATA=1 -DENABLE_COMPS=1"
+%endif
+
+cmake $CMAKE_FLAGS \
+ -DCMAKE_INSTALL_PREFIX=%{_prefix} \
+ -DLIB=%{_lib} \
+ -DCMAKE_VERBOSE_MAKEFILE=TRUE \
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+ %{?with_enable_static:-DENABLE_STATIC=1} \
+ %{?with_disable_shared:-DDISABLE_SHARED=1} \
+ %{?with_perl_binding:-DENABLE_PERL=1} \
+ %{?with_python_binding:-DENABLE_PYTHON=1} \
+ %{?with_ruby_binding:-DENABLE_RUBY=1} \
+ %{?with_zypp:-DENABLE_SUSEREPO=1 -DENABLE_HELIXREPO=1} \
+ -DUSE_VENDORDIRS=1 \
+ -DCMAKE_SKIP_RPATH=1
+make %{?jobs:-j %jobs}
+
+%install
+make DESTDIR=$RPM_BUILD_ROOT install
+%if %{with python_binding}
+%if 0%{?suse_version}
+pushd $RPM_BUILD_ROOT/%{python_sitearch}
+python %py_libdir/py_compile.py *.py
+python -O %py_libdir/py_compile.py *.py
+popd
+%endif
+%endif
+%if %{with disable_shared}
+# we want to leave the .a file untouched
+export NO_BRP_STRIP_DEBUG=true
+%endif
+
+%clean
+rm -rf "$RPM_BUILD_ROOT"
+
+%if !%{with disable_shared}
+%post -n libsolv@LIBSOLV_SOVERSION@ -p /sbin/ldconfig
+
+%postun -n libsolv@LIBSOLV_SOVERSION@ -p /sbin/ldconfig
+
+%files -n libsolv@LIBSOLV_SOVERSION@
+%defattr(-,root,root)
+%doc LICENSE*
+%{_libdir}/libsolv.so.*
+%{_libdir}/libsolvext.so.*
+%endif
+
+%files tools
+%defattr(-,root,root)
+%if 0%{?suse_version}
+%exclude %{_bindir}/helix2solv
+%exclude %{_mandir}/man1/helix2solv*
+%endif
+%exclude %{_bindir}/solv
+%{_bindir}/*
+%{_mandir}/man1/*
+
+%files devel
+%defattr(-,root,root)
+%if %{with enable_static}
+%{_libdir}/libsolv.a
+%{_libdir}/libsolvext.a
+%endif
+%if !%{with disable_shared}
+%{_libdir}/libsolv.so
+%{_libdir}/libsolvext.so
+%endif
+%{_includedir}/solv
+%if 0%{?suse_version}
+%{_bindir}/helix2solv
+%{_mandir}/man1/helix2solv*
+%endif
+%{_datadir}/cmake/Modules/*
+%{_libdir}/pkgconfig/libsolv.pc
+%{_mandir}/man3/*
+
+%files demo
+%defattr(-,root,root)
+%{_bindir}/solv
+
+%if %{with perl_binding}
+%files -n perl-solv
+%defattr(-,root,root)
+%{perl_vendorarch}/*
+%endif
+
+%if %{with ruby_binding}
+%files -n ruby-solv
+%defattr(-,root,root)
+%{ruby_vendorarch}/*
+%endif
+
+%if %{with python_binding}
+%files -n python-solv
+%defattr(-,root,root)
+%{python_sitearch}/*
+%endif
+
+%changelog
--- /dev/null
+
+INCLUDE (CheckFunctionExists)
+CHECK_FUNCTION_EXISTS (qsort_r HAVE_QSORT_R)
+CHECK_FUNCTION_EXISTS (__qsort_r HAVE___QSORT_R)
+
+IF (HAVE_QSORT_R)
+ ADD_DEFINITIONS (-DHAVE_QSORT_R=1)
+ENDIF (HAVE_QSORT_R)
+
+IF (HAVE___QSORT_R)
+ ADD_DEFINITIONS (-DHAVE___QSORT_R=1)
+ENDIF (HAVE___QSORT_R)
+
+ADD_DEFINITIONS (-DLIBSOLV_INTERNAL=1)
+
+SET (libsolv_SRCS
+ bitmap.c poolarch.c poolvendor.c poolid.c strpool.c dirpool.c
+ solver.c solverdebug.c repo_solv.c repo_write.c evr.c pool.c
+ queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c
+ transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c
+ chksum.c md5.c sha1.c sha2.c solvversion.c selection.c)
+
+SET (libsolv_HEADERS
+ bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h
+ poolid.h pooltypes.h queue.h solvable.h solver.h solverdebug.h
+ repo.h repodata.h repo_solv.h repo_write.h util.h selection.h
+ strpool.h dirpool.h knownid.h transaction.h rules.h problems.h
+ chksum.h dataiterator.h ${CMAKE_BINARY_DIR}/src/solvversion.h)
+
+SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+IF (HAVE_LINKER_VERSION_SCRIPT)
+SET (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_FLAGS} -Wl,--version-script=${CMAKE_SOURCE_DIR}/src/libsolv.ver")
+ENDIF (HAVE_LINKER_VERSION_SCRIPT)
+
+IF (DISABLE_SHARED)
+ADD_LIBRARY (libsolv STATIC ${libsolv_SRCS})
+ELSE (DISABLE_SHARED)
+ADD_LIBRARY (libsolv SHARED ${libsolv_SRCS})
+ENDIF (DISABLE_SHARED)
+
+SET_TARGET_PROPERTIES(libsolv PROPERTIES OUTPUT_NAME "solv")
+SET_TARGET_PROPERTIES(libsolv PROPERTIES SOVERSION ${LIBSOLV_SOVERSION})
+SET_TARGET_PROPERTIES(libsolv PROPERTIES INSTALL_NAME_DIR ${LIB_INSTALL_DIR})
+
+INSTALL (FILES ${libsolv_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/solv")
+INSTALL (TARGETS libsolv LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+
+IF (ENABLE_STATIC AND NOT DISABLE_SHARED)
+ADD_LIBRARY (libsolv_static STATIC ${libsolv_SRCS})
+SET_TARGET_PROPERTIES(libsolv_static PROPERTIES OUTPUT_NAME "solv")
+SET_TARGET_PROPERTIES(libsolv_static PROPERTIES SOVERSION ${LIBSOLV_SOVERSION})
+INSTALL (TARGETS libsolv_static LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+ENDIF (ENABLE_STATIC AND NOT DISABLE_SHARED)
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * bitmap.c
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "bitmap.h"
+#include "util.h"
+
+/* constructor */
+void
+map_init(Map *m, int n)
+{
+ m->size = (n + 7) >> 3;
+ m->map = m->size ? solv_calloc(m->size, 1) : 0;
+}
+
+/* destructor */
+void
+map_free(Map *m)
+{
+ m->map = solv_free(m->map);
+ m->size = 0;
+}
+
+/* copy constructor t <- s */
+void
+map_init_clone(Map *t, Map *s)
+{
+ t->size = s->size;
+ if (s->size)
+ {
+ t->map = solv_malloc(s->size);
+ memcpy(t->map, s->map, s->size);
+ }
+ else
+ t->map = 0;
+}
+
+/* grow a map */
+void
+map_grow(Map *m, int n)
+{
+ n = (n + 7) >> 3;
+ if (m->size < n)
+ {
+ m->map = solv_realloc(m->map, n);
+ memset(m->map + m->size, 0, n - m->size);
+ m->size = n;
+ }
+}
+
+/* bitwise-ands maps t and s, stores the result in t. */
+void
+map_and(Map *t, Map *s)
+{
+ unsigned char *ti, *si, *end;
+ ti = t->map;
+ si = s->map;
+ end = ti + (t->size < s->size ? t->size : s->size);
+ while (ti < end)
+ *ti++ &= *si++;
+}
+
+/* bitwise-ors maps t and s, stores the result in t. */
+void
+map_or(Map *t, Map *s)
+{
+ unsigned char *ti, *si, *end;
+ if (t->size < s->size)
+ map_grow(t, s->size << 3);
+ ti = t->map;
+ si = s->map;
+ end = ti + (t->size < s->size ? t->size : s->size);
+ while (ti < end)
+ *ti++ |= *si++;
+}
+
+/* remove all set bits in s from t. */
+void
+map_subtract(Map *t, Map *s)
+{
+ unsigned char *ti, *si, *end;
+ ti = t->map;
+ si = s->map;
+ end = ti + (t->size < s->size ? t->size : s->size);
+ while (ti < end)
+ *ti++ &= ~*si++;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * Copyright (c) 2007-2011, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * bitmap.h
+ *
+ */
+
+#ifndef LIBSOLV_BITMAP_H
+#define LIBSOLV_BITMAP_H
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _Map {
+ unsigned char *map;
+ int size;
+} Map;
+
+#define MAPZERO(m) (memset((m)->map, 0, (m)->size))
+/* set all bits */
+#define MAPSETALL(m) (memset((m)->map, 0xff, (m)->size))
+/* set bit */
+#define MAPSET(m, n) ((m)->map[(n) >> 3] |= 1 << ((n) & 7))
+/* clear bit */
+#define MAPCLR(m, n) ((m)->map[(n) >> 3] &= ~(1 << ((n) & 7)))
+/* test bit */
+#define MAPTST(m, n) ((m)->map[(n) >> 3] & (1 << ((n) & 7)))
+
+extern void map_init(Map *m, int n);
+extern void map_init_clone(Map *t, Map *s);
+extern void map_grow(Map *m, int n);
+extern void map_free(Map *m);
+extern void map_and(Map *t, Map *s);
+extern void map_or(Map *t, Map *s);
+extern void map_subtract(Map *t, Map *s);
+
+static inline void map_empty(Map *m)
+{
+ MAPZERO(m);
+}
+static inline void map_set(Map *m, int n)
+{
+ MAPSET(m, n);
+}
+static inline void map_setall(Map *m)
+{
+ MAPSETALL(m);
+}
+static inline void map_clr(Map *m, int n)
+{
+ MAPCLR(m, n);
+}
+static inline int map_tst(Map *m, int n)
+{
+ return MAPTST(m, n);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_BITMAP_H */
--- /dev/null
+/*
+ * Copyright (c) 2008-2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "util.h"
+#include "chksum.h"
+
+#include "md5.h"
+#include "sha1.h"
+#include "sha2.h"
+
+struct _Chksum {
+ Id type;
+ int done;
+ unsigned char result[64];
+ union {
+ MD5_CTX md5;
+ SHA1_CTX sha1;
+ SHA224_CTX sha224;
+ SHA256_CTX sha256;
+ SHA384_CTX sha384;
+ SHA512_CTX sha512;
+ } c;
+};
+
+Chksum *
+solv_chksum_create(Id type)
+{
+ Chksum *chk;
+ chk = solv_calloc(1, sizeof(*chk));
+ chk->type = type;
+ switch(type)
+ {
+ case REPOKEY_TYPE_MD5:
+ solv_MD5_Init(&chk->c.md5);
+ return chk;
+ case REPOKEY_TYPE_SHA1:
+ solv_SHA1_Init(&chk->c.sha1);
+ return chk;
+ case REPOKEY_TYPE_SHA224:
+ solv_SHA224_Init(&chk->c.sha224);
+ return chk;
+ case REPOKEY_TYPE_SHA256:
+ solv_SHA256_Init(&chk->c.sha256);
+ return chk;
+ case REPOKEY_TYPE_SHA384:
+ solv_SHA384_Init(&chk->c.sha384);
+ return chk;
+ case REPOKEY_TYPE_SHA512:
+ solv_SHA512_Init(&chk->c.sha512);
+ return chk;
+ default:
+ break;
+ }
+ free(chk);
+ return 0;
+}
+
+Chksum *
+solv_chksum_create_clone(Chksum *chk)
+{
+ return solv_memdup(chk, sizeof(*chk));
+}
+
+int
+solv_chksum_len(Id type)
+{
+ switch (type)
+ {
+ case REPOKEY_TYPE_MD5:
+ return 16;
+ case REPOKEY_TYPE_SHA1:
+ return 20;
+ case REPOKEY_TYPE_SHA224:
+ return 28;
+ case REPOKEY_TYPE_SHA256:
+ return 32;
+ case REPOKEY_TYPE_SHA384:
+ return 48;
+ case REPOKEY_TYPE_SHA512:
+ return 64;
+ default:
+ return 0;
+ }
+}
+
+Chksum *
+solv_chksum_create_from_bin(Id type, const unsigned char *buf)
+{
+ Chksum *chk;
+ int l = solv_chksum_len(type);
+ if (buf == 0 || l == 0)
+ return 0;
+ chk = solv_calloc(1, sizeof(*chk));
+ chk->type = type;
+ chk->done = 1;
+ memcpy(chk->result, buf, l);
+ return chk;
+}
+
+void
+solv_chksum_add(Chksum *chk, const void *data, int len)
+{
+ if (chk->done)
+ return;
+ switch(chk->type)
+ {
+ case REPOKEY_TYPE_MD5:
+ solv_MD5_Update(&chk->c.md5, (void *)data, len);
+ return;
+ case REPOKEY_TYPE_SHA1:
+ solv_SHA1_Update(&chk->c.sha1, data, len);
+ return;
+ case REPOKEY_TYPE_SHA224:
+ solv_SHA224_Update(&chk->c.sha224, data, len);
+ return;
+ case REPOKEY_TYPE_SHA256:
+ solv_SHA256_Update(&chk->c.sha256, data, len);
+ return;
+ case REPOKEY_TYPE_SHA384:
+ solv_SHA384_Update(&chk->c.sha384, data, len);
+ return;
+ case REPOKEY_TYPE_SHA512:
+ solv_SHA512_Update(&chk->c.sha512, data, len);
+ return;
+ default:
+ return;
+ }
+}
+
+const unsigned char *
+solv_chksum_get(Chksum *chk, int *lenp)
+{
+ if (chk->done)
+ {
+ if (lenp)
+ *lenp = solv_chksum_len(chk->type);
+ return chk->result;
+ }
+ switch(chk->type)
+ {
+ case REPOKEY_TYPE_MD5:
+ solv_MD5_Final(chk->result, &chk->c.md5);
+ chk->done = 1;
+ if (lenp)
+ *lenp = 16;
+ return chk->result;
+ case REPOKEY_TYPE_SHA1:
+ solv_SHA1_Final(&chk->c.sha1, chk->result);
+ chk->done = 1;
+ if (lenp)
+ *lenp = 20;
+ return chk->result;
+ case REPOKEY_TYPE_SHA224:
+ solv_SHA224_Final(chk->result, &chk->c.sha224);
+ chk->done = 1;
+ if (lenp)
+ *lenp = 28;
+ return chk->result;
+ case REPOKEY_TYPE_SHA256:
+ solv_SHA256_Final(chk->result, &chk->c.sha256);
+ chk->done = 1;
+ if (lenp)
+ *lenp = 32;
+ return chk->result;
+ case REPOKEY_TYPE_SHA384:
+ solv_SHA384_Final(chk->result, &chk->c.sha384);
+ chk->done = 1;
+ if (lenp)
+ *lenp = 48;
+ return chk->result;
+ case REPOKEY_TYPE_SHA512:
+ solv_SHA512_Final(chk->result, &chk->c.sha512);
+ chk->done = 1;
+ if (lenp)
+ *lenp = 64;
+ return chk->result;
+ default:
+ if (lenp)
+ *lenp = 0;
+ return 0;
+ }
+}
+
+Id
+solv_chksum_get_type(Chksum *chk)
+{
+ return chk->type;
+}
+
+int
+solv_chksum_isfinished(Chksum *chk)
+{
+ return chk->done != 0;
+}
+
+const char *
+solv_chksum_type2str(Id type)
+{
+ switch(type)
+ {
+ case REPOKEY_TYPE_MD5:
+ return "md5";
+ case REPOKEY_TYPE_SHA1:
+ return "sha1";
+ case REPOKEY_TYPE_SHA224:
+ return "sha224";
+ case REPOKEY_TYPE_SHA256:
+ return "sha256";
+ case REPOKEY_TYPE_SHA384:
+ return "sha384";
+ case REPOKEY_TYPE_SHA512:
+ return "sha512";
+ default:
+ return 0;
+ }
+}
+
+Id
+solv_chksum_str2type(const char *str)
+{
+ if (!strcasecmp(str, "md5"))
+ return REPOKEY_TYPE_MD5;
+ if (!strcasecmp(str, "sha") || !strcasecmp(str, "sha1"))
+ return REPOKEY_TYPE_SHA1;
+ if (!strcasecmp(str, "sha224"))
+ return REPOKEY_TYPE_SHA224;
+ if (!strcasecmp(str, "sha256"))
+ return REPOKEY_TYPE_SHA256;
+ if (!strcasecmp(str, "sha384"))
+ return REPOKEY_TYPE_SHA384;
+ if (!strcasecmp(str, "sha512"))
+ return REPOKEY_TYPE_SHA512;
+ return 0;
+}
+
+void *
+solv_chksum_free(Chksum *chk, unsigned char *cp)
+{
+ if (cp)
+ {
+ const unsigned char *res;
+ int l;
+ res = solv_chksum_get(chk, &l);
+ if (l && res)
+ memcpy(cp, res, l);
+ }
+ solv_free(chk);
+ return 0;
+}
+
+int
+solv_chksum_cmp(Chksum *chk, Chksum *chk2)
+{
+ int len;
+ const unsigned char *res1, *res2;
+ if (chk == chk2)
+ return 1;
+ if (!chk || !chk2 || chk->type != chk2->type)
+ return 0;
+ res1 = solv_chksum_get(chk, &len);
+ res2 = solv_chksum_get(chk2, 0);
+ return memcmp(res1, res2, len) == 0 ? 1 : 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2007-2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef LIBSOLV_CHKSUM_H
+#define LIBSOLV_CHKSUM_H
+
+#include "pool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _Chksum;
+typedef struct _Chksum Chksum;
+
+Chksum *solv_chksum_create(Id type);
+Chksum *solv_chksum_create_clone(Chksum *chk);
+Chksum *solv_chksum_create_from_bin(Id type, const unsigned char *buf);
+void solv_chksum_add(Chksum *chk, const void *data, int len);
+Id solv_chksum_get_type(Chksum *chk);
+int solv_chksum_isfinished(Chksum *chk);
+const unsigned char *solv_chksum_get(Chksum *chk, int *lenp);
+void *solv_chksum_free(Chksum *chk, unsigned char *cp);
+const char *solv_chksum_type2str(Id type);
+Id solv_chksum_str2type(const char *str);
+int solv_chksum_len(Id type);
+int solv_chksum_cmp(Chksum *chk, Chksum *chk2);
+
+#ifdef LIBSOLV_INTERNAL
+
+#define case_CHKSUM_TYPES \
+ case REPOKEY_TYPE_MD5: \
+ case REPOKEY_TYPE_SHA1: \
+ case REPOKEY_TYPE_SHA224: \
+ case REPOKEY_TYPE_SHA256: \
+ case REPOKEY_TYPE_SHA384: \
+ case REPOKEY_TYPE_SHA512
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_CHKSUM_H */
--- /dev/null
+/*
+ * Copyright (c) 2014, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * cplxdeps.c
+ *
+ * normalize complex dependencies into CNF/DNF form
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pool.h"
+#include "cplxdeps.h"
+
+#ifdef ENABLE_COMPLEX_DEPS
+
+#undef CPLXDEBUG
+
+int
+pool_is_complex_dep_rd(Pool *pool, Reldep *rd)
+{
+ for (;;)
+ {
+ if (rd->flags == REL_AND || rd->flags == REL_COND) /* those two are the complex ones */
+ return 1;
+ if (rd->flags != REL_OR)
+ return 0;
+ if (ISRELDEP(rd->name) && pool_is_complex_dep_rd(pool, GETRELDEP(pool, rd->name)))
+ return 1;
+ if (!ISRELDEP(rd->evr))
+ return 0;
+ rd = GETRELDEP(pool, rd->evr);
+ }
+}
+
+/* expand simple dependencies into package lists */
+static int
+expand_simpledeps(Pool *pool, Queue *bq, int start, int split)
+{
+ int end = bq->count;
+ int i, x;
+ int newsplit = 0;
+ for (i = start; i < end; i++)
+ {
+ if (i == split)
+ newsplit = bq->count - (end - start);
+ x = bq->elements[i];
+ if (x == pool->nsolvables)
+ {
+ Id *dp = pool->whatprovidesdata + bq->elements[++i];
+ for (; *dp; dp++)
+ queue_push(bq, *dp);
+ }
+ else
+ queue_push(bq, x);
+ }
+ if (i == split)
+ newsplit = bq->count - (end - start);
+ queue_deleten(bq, start, end - start);
+ return newsplit;
+}
+
+#ifdef CPLXDEBUG
+static void
+print_depblocks(Pool *pool, Queue *bq, int start)
+{
+ int i;
+
+ for (i = start; i < bq->count; i++)
+ {
+ if (bq->elements[i] == pool->nsolvables)
+ {
+ Id *dp = pool->whatprovidesdata + bq->elements[++i];
+ printf(" (");
+ while (*dp)
+ printf(" %s", pool_solvid2str(pool, *dp++));
+ printf(" )");
+ }
+ else if (bq->elements[i] > 0)
+ printf(" %s", pool_solvid2str(pool, bq->elements[i]));
+ else if (bq->elements[i] < 0)
+ printf(" -%s", pool_solvid2str(pool, -bq->elements[i]));
+ else
+ printf(" ||");
+ }
+ printf("\n");
+}
+#endif
+
+/* invert all literals in the blocks. note that this also turns DNF into CNF and vice versa */
+static int
+invert_depblocks(Pool *pool, Queue *bq, int start, int r)
+{
+ int i, j, end;
+ if (r == 0 || r == 1)
+ return r ? 0 : 1;
+ expand_simpledeps(pool, bq, start, 0);
+ end = bq->count;
+ for (i = j = start; i < end; i++)
+ {
+ if (bq->elements[i])
+ {
+ bq->elements[i] = -bq->elements[i];
+ continue;
+ }
+ /* end of block reached, reverse */
+ if (i - 1 > j)
+ {
+ int k;
+ for (k = i - 1; j < k; j++, k--)
+ {
+ Id t = bq->elements[j];
+ bq->elements[j] = bq->elements[k];
+ bq->elements[k] = t;
+ }
+ }
+ j = i + 1;
+ }
+ return -1;
+}
+
+/*
+ * returns:
+ * 0: no blocks
+ * 1: matches all
+ * -1: at least one block
+ */
+static int
+normalize_dep(Pool *pool, Id dep, Queue *bq, int flags)
+{
+ int bqcnt = bq->count;
+ int bqcnt2;
+ int todnf = flags & CPLXDEPS_TODNF ? 1 : 0;
+ Id p, dp;
+
+#ifdef CPLXDEBUG
+ printf("normalize_dep %s todnf:%d\n", pool_dep2str(pool, dep), todnf);
+#endif
+ if (pool_is_complex_dep(pool, dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_COND)
+ {
+ int rdflags = rd->flags;
+ Id name = rd->name;
+ Id evr = rd->evr;
+ int r, mode;
+
+ if (rdflags == REL_COND)
+ {
+ /* check for relly complex ELSE case */
+ if (ISRELDEP(evr))
+ {
+ Reldep *rd2 = GETRELDEP(pool, evr);
+ if (rd2->flags == REL_ELSE)
+ {
+ int r2;
+ /* really complex case */
+ if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_AND_1)
+ {
+ /* A OR ~B */
+ rdflags = REL_COND;
+ evr = rd2->name;
+ }
+ else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_AND_2)
+ {
+ /* C OR B */
+ rdflags = REL_OR;
+ name = rd2->evr;
+ evr = rd2->name;
+ }
+ else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_1)
+ {
+ /* A AND B */
+ rdflags = REL_AND;
+ evr = rd2->name;
+ }
+ else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_2)
+ {
+ /* A AND C */
+ rdflags = REL_AND;
+ evr = rd2->evr;
+ }
+ else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_3)
+ {
+ /* C AND ~B */
+ rdflags = REL_ELSE;
+ name = rd2->evr;
+ evr = rd2->name;
+ }
+ else if (!todnf)
+ {
+ /* we want AND: A IF (B ELSE C) -> (A OR ~B) AND (C OR B) */
+ r = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_AND_1);
+ if (r == 0 && (flags & CPLXDEPS_DONTFIX) == 0)
+ return 0;
+ r2 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_AND_2);
+ if (r2 == 0 && (flags & CPLXDEPS_DONTFIX) == 0)
+ {
+ queue_truncate(bq, bqcnt);
+ return 0;
+ }
+ if (r == -1 || r2 == -1)
+ return -1;
+ return r == 1 || r2 == 1 ? 1 : 0;
+ }
+ else
+ {
+ int r2, r3;
+ /* we want OR: A IF (B ELSE C) -> (A AND B) OR (A AND C) OR (~B AND C) */
+ r = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_1);
+ if (r == 1)
+ return 1;
+ r2 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_2);
+ if (r2 == 1)
+ {
+ queue_truncate(bq, bqcnt);
+ return 1;
+ }
+ r3 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_3);
+ if (r3 == 1)
+ {
+ queue_truncate(bq, bqcnt);
+ return 1;
+ }
+ if (r == -1 || r2 == -1 || r3 == -1)
+ return -1;
+ return 0;
+ }
+ }
+ }
+ }
+ mode = rdflags == REL_AND || rdflags == REL_ELSE ? 0 : 1;
+
+ /* get blocks of first argument */
+ r = normalize_dep(pool, name, bq, flags);
+ if (r == 0)
+ {
+ if (rdflags == REL_ELSE)
+ return 0;
+ if (rdflags == REL_AND && (flags & CPLXDEPS_DONTFIX) == 0)
+ return 0;
+ if (rdflags == REL_COND)
+ {
+ r = normalize_dep(pool, evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX);
+ return invert_depblocks(pool, bq, bqcnt, r); /* invert block for COND */
+ }
+ return normalize_dep(pool, evr, bq, flags);
+ }
+ if (r == 1)
+ {
+ if (rdflags == REL_ELSE)
+ {
+ r = normalize_dep(pool, evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX);
+ return invert_depblocks(pool, bq, bqcnt, r); /* invert block for ELSE */
+ }
+ if (rdflags == REL_OR || rdflags == REL_COND)
+ return 1;
+ return normalize_dep(pool, evr, bq, flags);
+ }
+
+ /* get blocks of second argument */
+ bqcnt2 = bq->count;
+ /* COND is OR with NEG on evr block, so we invert the todnf flag in that case */
+ r = normalize_dep(pool, evr, bq, rdflags == REL_COND || rdflags == REL_ELSE ? ((flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX) : flags);
+ if (rdflags == REL_COND || rdflags == REL_ELSE)
+ r = invert_depblocks(pool, bq, bqcnt2, r); /* invert 2nd block */
+ if (r == 0)
+ {
+ if (rdflags == REL_OR)
+ return -1;
+ if (rdflags == REL_AND && (flags & CPLXDEPS_DONTFIX) != 0)
+ return -1;
+ queue_truncate(bq, bqcnt);
+ return 0;
+ }
+ if (r == 1)
+ {
+ if (rdflags == REL_COND || rdflags == REL_OR)
+ {
+ queue_truncate(bq, bqcnt);
+ return 1;
+ }
+ return -1;
+ }
+ if (mode == todnf)
+ {
+ /* simple case: just join em. nothing more to do here. */
+#ifdef CPLXDEBUG
+ printf("SIMPLE JOIN %d %d %d\n", bqcnt, bqcnt2, bq->count);
+#endif
+ return -1;
+ }
+ else
+ {
+ /* complex case: mix em */
+ int i, j, bqcnt3;
+#ifdef CPLXDEBUG
+ printf("COMPLEX JOIN %d %d %d\n", bqcnt, bqcnt2, bq->count);
+#endif
+ bqcnt2 = expand_simpledeps(pool, bq, bqcnt, bqcnt2);
+ bqcnt3 = bq->count;
+ for (i = bqcnt; i < bqcnt2; i++)
+ {
+ for (j = bqcnt2; j < bqcnt3; j++)
+ {
+ int a, b;
+ int bqcnt4 = bq->count;
+ int k = i;
+
+ /* mix i block with j block, both blocks are sorted */
+ while (bq->elements[k] && bq->elements[j])
+ {
+ if (bq->elements[k] < bq->elements[j])
+ queue_push(bq, bq->elements[k++]);
+ else
+ {
+ if (bq->elements[k] == bq->elements[j])
+ k++;
+ queue_push(bq, bq->elements[j++]);
+ }
+ }
+ while (bq->elements[j])
+ queue_push(bq, bq->elements[j++]);
+ while (bq->elements[k])
+ queue_push(bq, bq->elements[k++]);
+
+ /* block is finished, check for A + -A */
+ for (a = bqcnt4, b = bq->count - 1; a < b; )
+ {
+ if (-bq->elements[a] == bq->elements[b])
+ break;
+ if (-bq->elements[a] > bq->elements[b])
+ a++;
+ else
+ b--;
+ }
+ if (a < b)
+ queue_truncate(bq, bqcnt4); /* ignore this block */
+ else
+ queue_push(bq, 0); /* finish block */
+ }
+ /* advance to next block */
+ while (bq->elements[i])
+ i++;
+ }
+ i = -1;
+ if (bqcnt3 == bq->count) /* ignored all blocks? */
+ i = todnf ? 0 : 1;
+ queue_deleten(bq, bqcnt, bqcnt3 - bqcnt);
+ return i;
+ }
+ }
+ }
+
+ /* fallback case: just use package list */
+ dp = pool_whatprovides(pool, dep);
+ if (dp <= 2 || !pool->whatprovidesdata[dp])
+ return dp == 2 ? 1 : 0;
+ if (pool->whatprovidesdata[dp] == SYSTEMSOLVABLE)
+ return 1;
+ bqcnt = bq->count;
+ if ((flags & CPLXDEPS_NAME) != 0)
+ {
+ while ((p = pool->whatprovidesdata[dp++]) != 0)
+ {
+ if (!pool_match_nevr(pool, pool->solvables + p, dep))
+ continue;
+ queue_push(bq, p);
+ if (todnf)
+ queue_push(bq, 0);
+ }
+ }
+ else if (todnf)
+ {
+ while ((p = pool->whatprovidesdata[dp++]) != 0)
+ queue_push2(bq, p, 0);
+ }
+ else
+ queue_push2(bq, pool->nsolvables, dp); /* not yet expanded marker + offset */
+ if (bq->count == bqcnt)
+ return 0; /* no provider */
+ if (!todnf)
+ queue_push(bq, 0); /* finish block */
+ return -1;
+}
+
+int
+pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags)
+{
+ int i, bqcnt = bq->count;
+
+ i = normalize_dep(pool, dep, bq, flags);
+ if ((flags & CPLXDEPS_EXPAND) != 0)
+ {
+ if (i != 0 && i != 1)
+ expand_simpledeps(pool, bq, bqcnt, 0);
+ }
+ if ((flags & CPLXDEPS_INVERT) != 0)
+ i = invert_depblocks(pool, bq, bqcnt, i);
+#ifdef CPLXDEBUG
+ if (i == 0)
+ printf("NONE\n");
+ else if (i == 1)
+ printf("ALL\n");
+ else
+ print_depblocks(pool, bq, bqcnt);
+#endif
+ return i;
+}
+
+void
+pool_add_pos_literals_complex_dep(Pool *pool, Id dep, Queue *q, Map *m, int neg)
+{
+ while (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags != REL_AND && rd->flags != REL_OR && rd->flags != REL_COND)
+ break;
+ pool_add_pos_literals_complex_dep(pool, rd->name, q, m, neg);
+ dep = rd->evr;
+ if (rd->flags == REL_COND)
+ {
+ neg = !neg;
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd2 = GETRELDEP(pool, rd->evr);
+ if (rd2->flags == REL_ELSE)
+ {
+ pool_add_pos_literals_complex_dep(pool, rd2->evr, q, m, !neg);
+ dep = rd2->name;
+ }
+ }
+ }
+ }
+ if (!neg)
+ {
+ Id p, pp;
+ FOR_PROVIDES(p, pp, dep)
+ if (!MAPTST(m, p))
+ queue_push(q, p);
+ }
+}
+
+#endif /* ENABLE_COMPLEX_DEPS */
+
--- /dev/null
+/*
+ * Copyright (c) 2014, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * cplxdeps.h (internal)
+ */
+
+#ifndef LIBSOLV_CPLXDEPS_H
+#define LIBSOLV_CPLXDEPS_H
+
+extern int pool_is_complex_dep_rd(Pool *pool, Reldep *rd);
+
+static inline int
+pool_is_complex_dep(Pool *pool, Id dep)
+{
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags >= 8 && pool_is_complex_dep_rd(pool, rd))
+ return 1;
+ }
+ return 0;
+}
+
+extern int pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags);
+extern void pool_add_pos_literals_complex_dep(Pool *pool, Id dep, Queue *q, Map *m, int neg);
+
+#define CPLXDEPS_TODNF (1 << 0)
+#define CPLXDEPS_EXPAND (1 << 1)
+#define CPLXDEPS_INVERT (1 << 2)
+#define CPLXDEPS_NAME (1 << 3)
+#define CPLXDEPS_DONTFIX (1 << 4)
+
+#define CPLXDEPS_ELSE_AND_1 (1 << 8)
+#define CPLXDEPS_ELSE_AND_2 (1 << 9)
+#define CPLXDEPS_ELSE_OR_1 (1 << 10)
+#define CPLXDEPS_ELSE_OR_2 (1 << 11)
+#define CPLXDEPS_ELSE_OR_3 (1 << 12)
+#define CPLXDEPS_ELSE_MASK (0x1f00)
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * dataiterator.h
+ *
+ */
+
+#ifndef LIBSOLV_DATAITERATOR_H
+#define LIBSOLV_DATAITERATOR_H
+
+#include "pooltypes.h"
+#include "pool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _Repo;
+
+typedef struct _KeyValue {
+ Id id;
+ const char *str;
+ unsigned int num;
+ unsigned int num2;
+
+ int entry; /* array entry, starts with 0 */
+ int eof; /* last entry reached */
+
+ struct _KeyValue *parent;
+} KeyValue;
+
+#define SOLV_KV_NUM64(kv) (((unsigned long long)((kv)->num2)) << 32 | (kv)->num)
+
+/* search matcher flags */
+#define SEARCH_STRINGMASK 15
+#define SEARCH_STRING 1
+#define SEARCH_STRINGSTART 2
+#define SEARCH_STRINGEND 3
+#define SEARCH_SUBSTRING 4
+#define SEARCH_GLOB 5
+#define SEARCH_REGEX 6
+#define SEARCH_ERROR 15
+#define SEARCH_NOCASE (1<<7)
+
+/* iterator control */
+#define SEARCH_NO_STORAGE_SOLVABLE (1<<8)
+#define SEARCH_SUB (1<<9)
+#define SEARCH_ARRAYSENTINEL (1<<10)
+#define SEARCH_DISABLED_REPOS (1<<11)
+#define SEARCH_COMPLETE_FILELIST (1<<12)
+
+/* stringification flags */
+#define SEARCH_SKIP_KIND (1<<16)
+/* By default we stringify just to the basename of a file because
+ the construction of the full filename is costly. Specify this
+ flag if you want to match full filenames */
+#define SEARCH_FILES (1<<17)
+#define SEARCH_CHECKSUMS (1<<18)
+
+/* dataiterator internal */
+#define SEARCH_THISSOLVID (1<<31)
+
+/*
+ * Datamatcher: match a string against a query
+ */
+typedef struct _Datamatcher {
+ int flags; /* see matcher flags above */
+ const char *match; /* the query string */
+ void *matchdata; /* e.g. compiled regexp */
+ int error;
+} Datamatcher;
+
+int datamatcher_init(Datamatcher *ma, const char *match, int flags);
+void datamatcher_free(Datamatcher *ma);
+int datamatcher_match(Datamatcher *ma, const char *str);
+int datamatcher_checkbasename(Datamatcher *ma, const char *str);
+
+
+/*
+ * Dataiterator
+ *
+ * Iterator like interface to 'search' functionality
+ *
+ * Dataiterator is per-pool, additional filters can be applied
+ * to limit the search domain. See dataiterator_init below.
+ *
+ * Use these like:
+ * Dataiterator di;
+ * dataiterator_init(&di, repo->pool, repo, 0, 0, "bla", SEARCH_SUBSTRING);
+ * while (dataiterator_step(&di))
+ * dosomething(di.solvid, di.key, di.kv);
+ * dataiterator_free(&di);
+ */
+typedef struct _Dataiterator
+{
+ int state;
+ int flags;
+
+ Pool *pool;
+ struct _Repo *repo;
+ struct _Repodata *data;
+
+ /* data pointers */
+ unsigned char *dp;
+ unsigned char *ddp;
+ Id *idp;
+ Id *keyp;
+
+ /* the result */
+ struct _Repokey *key;
+ KeyValue kv;
+
+ /* our matcher */
+ Datamatcher matcher;
+
+ /* iterators/filters */
+ Id keyname;
+ Id repodataid;
+ Id solvid;
+ Id repoid;
+
+ Id keynames[3 + 1];
+ int nkeynames;
+ int rootlevel;
+
+ /* recursion data */
+ struct di_parent {
+ KeyValue kv;
+ unsigned char *dp;
+ Id *keyp;
+ } parents[3];
+ int nparents;
+
+ /* vertical data */
+ unsigned char *vert_ddp;
+ Id vert_off;
+ Id vert_len;
+ Id vert_storestate;
+
+ /* strdup data */
+ char *dupstr;
+ int dupstrn;
+
+} Dataiterator;
+
+
+/*
+ * Initialize dataiterator
+ *
+ * di: Pointer to Dataiterator to be initialized
+ * pool: Search domain for the iterator
+ * repo: if non-null, limit search to this repo
+ * solvid: if non-null, limit search to this solvable
+ * keyname: if non-null, limit search to this keyname
+ * match: if non-null, limit search to this match
+ */
+int dataiterator_init(Dataiterator *di, Pool *pool, struct _Repo *repo, Id p, Id keyname, const char *match, int flags);
+void dataiterator_init_clone(Dataiterator *di, Dataiterator *from);
+void dataiterator_set_search(Dataiterator *di, struct _Repo *repo, Id p);
+void dataiterator_set_keyname(Dataiterator *di, Id keyname);
+int dataiterator_set_match(Dataiterator *di, const char *match, int flags);
+
+void dataiterator_prepend_keyname(Dataiterator *di, Id keyname);
+void dataiterator_free(Dataiterator *di);
+int dataiterator_step(Dataiterator *di);
+void dataiterator_setpos(Dataiterator *di);
+void dataiterator_setpos_parent(Dataiterator *di);
+int dataiterator_match(Dataiterator *di, Datamatcher *ma);
+void dataiterator_skip_attribute(Dataiterator *di);
+void dataiterator_skip_solvable(Dataiterator *di);
+void dataiterator_skip_repo(Dataiterator *di);
+void dataiterator_jump_to_solvid(Dataiterator *di, Id solvid);
+void dataiterator_jump_to_repo(Dataiterator *di, struct _Repo *repo);
+void dataiterator_entersub(Dataiterator *di);
+void dataiterator_clonepos(Dataiterator *di, Dataiterator *from);
+void dataiterator_seek(Dataiterator *di, int whence);
+void dataiterator_strdup(Dataiterator *di);
+
+#define DI_SEEK_STAY (1 << 16)
+#define DI_SEEK_CHILD 1
+#define DI_SEEK_PARENT 2
+#define DI_SEEK_REWIND 3
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_DATAITERATOR_H */
--- /dev/null
+/*
+ * Copyright (c) 2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pool.h"
+#include "util.h"
+#include "dirpool.h"
+
+#define DIR_BLOCK 127
+
+/* directories are stored as components,
+ * components are simple ids from the string pool
+ * /usr/bin -> "", "usr", "bin"
+ * /usr/lib -> "", "usr", "lib"
+ * foo/bar -> "foo", "bar"
+ * /usr/games -> "", "usr", "games"
+ *
+ * all directories are stores in the "dirs" array
+ * dirs[id] > 0 : component string pool id
+ * dirs[id] <= 0 : -(parent directory id)
+ *
+ * Directories with the same parent are stored as
+ * multiple blocks. We need multiple blocks because
+ * we cannot insert entries into old blocks, as that
+ * would shift the ids of already used directories.
+ * Each block starts with (-parent_dirid) and contains
+ * component ids of the directory entries.
+ * (The (-parent_dirid) entry is not a valid directory
+ * id, it's just used internally)
+ *
+ * There is also the aux "dirtraverse" array, which
+ * is created on demand to speed things up a bit.
+ * if dirs[id] > 0, dirtravers[id] points to the first
+ * entry in the last block with parent id.
+ * if dirs[id] <= 0, dirtravers[id] points to the entry
+ * in the previous block with the same parent.
+ * (Thus it acts as a linked list that starts at the
+ * parent dirid and chains all the blocks with that
+ * parent.)
+ *
+ * id dirs[id] dirtraverse[id]
+ * 0 0 8 [no parent, block#0]
+ * 1 "" 3
+ * 2 -1 [parent 1, /, block #0]
+ * 3 "usr" 12
+ * 4 -3 [parent 3, /usr, block #0]
+ * 5 "bin"
+ * 6 "lib"
+ * 7 0 1 [no parent, block#1]
+ * 8 "foo" 10
+ * 9 -8 [parent 8, foo, block #0]
+ * 10 "bar"
+ * 11 -3 5 [parent 3, /usr, block #1]
+ * 12 "games"
+ *
+ * to find all children of dirid 3 ("/usr"), follow the
+ * dirtraverse link to 12 -> "games". Then follow the
+ * dirtraverse link of this block to 5 -> "bin", "lib"
+ */
+
+void
+dirpool_init(Dirpool *dp)
+{
+ memset(dp, 0, sizeof(*dp));
+}
+
+void
+dirpool_free(Dirpool *dp)
+{
+ solv_free(dp->dirs);
+ solv_free(dp->dirtraverse);
+}
+
+void
+dirpool_make_dirtraverse(Dirpool *dp)
+{
+ Id parent, i, *dirtraverse;
+ if (!dp->ndirs)
+ return;
+ dp->dirs = solv_extend_resize(dp->dirs, dp->ndirs, sizeof(Id), DIR_BLOCK);
+ dirtraverse = solv_calloc_block(dp->ndirs, sizeof(Id), DIR_BLOCK);
+ for (parent = 0, i = 0; i < dp->ndirs; i++)
+ {
+ if (dp->dirs[i] > 0)
+ continue;
+ parent = -dp->dirs[i];
+ dirtraverse[i] = dirtraverse[parent];
+ dirtraverse[parent] = i + 1;
+ }
+ dp->dirtraverse = dirtraverse;
+}
+
+Id
+dirpool_add_dir(Dirpool *dp, Id parent, Id comp, int create)
+{
+ Id did, d, ds, *dirtraverse;
+
+ if (!dp->ndirs)
+ {
+ if (!create)
+ return 0;
+ dp->ndirs = 2;
+ dp->dirs = solv_extend_resize(dp->dirs, dp->ndirs, sizeof(Id), DIR_BLOCK);
+ dp->dirs[0] = 0;
+ dp->dirs[1] = 1; /* "" */
+ }
+ if (parent == 0 && comp == 1)
+ return 1;
+ if (!dp->dirtraverse)
+ dirpool_make_dirtraverse(dp);
+ /* check all entries with this parent if we
+ * already have this component */
+ dirtraverse = dp->dirtraverse;
+ ds = dirtraverse[parent];
+ while (ds)
+ {
+ /* ds: first component in this block
+ * ds-1: parent link */
+ for (d = ds--; d < dp->ndirs; d++)
+ {
+ if (dp->dirs[d] == comp)
+ return d;
+ if (dp->dirs[d] <= 0) /* reached end of this block */
+ break;
+ }
+ if (ds)
+ ds = dp->dirtraverse[ds];
+ }
+ if (!create)
+ return 0;
+ /* a new one, find last parent */
+ for (did = dp->ndirs - 1; did > 0; did--)
+ if (dp->dirs[did] <= 0)
+ break;
+ if (dp->dirs[did] != -parent)
+ {
+ /* make room for parent entry */
+ dp->dirs = solv_extend(dp->dirs, dp->ndirs, 1, sizeof(Id), DIR_BLOCK);
+ dp->dirtraverse = solv_extend(dp->dirtraverse, dp->ndirs, 1, sizeof(Id), DIR_BLOCK);
+ /* new parent block, link in */
+ dp->dirs[dp->ndirs] = -parent;
+ dp->dirtraverse[dp->ndirs] = dp->dirtraverse[parent];
+ dp->dirtraverse[parent] = ++dp->ndirs;
+ }
+ /* make room for new entry */
+ dp->dirs = solv_extend(dp->dirs, dp->ndirs, 1, sizeof(Id), DIR_BLOCK);
+ dp->dirtraverse = solv_extend(dp->dirtraverse, dp->ndirs, 1, sizeof(Id), DIR_BLOCK);
+ dp->dirs[dp->ndirs] = comp;
+ dp->dirtraverse[dp->ndirs] = 0;
+ return dp->ndirs++;
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+#ifndef LIBSOLV_DIRPOOL_H
+#define LIBSOLV_DIRPOOL_H
+
+
+#include "pooltypes.h"
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _Dirpool {
+ Id *dirs;
+ int ndirs;
+ Id *dirtraverse;
+} Dirpool;
+
+void dirpool_init(Dirpool *dp);
+void dirpool_free(Dirpool *dp);
+
+void dirpool_make_dirtraverse(Dirpool *dp);
+Id dirpool_add_dir(Dirpool *dp, Id parent, Id comp, int create);
+
+/* return the parent directory of child did */
+static inline Id dirpool_parent(Dirpool *dp, Id did)
+{
+ if (!did)
+ return 0;
+ while (dp->dirs[--did] > 0)
+ ;
+ return -dp->dirs[did];
+}
+
+/* return the next child entry of child did */
+static inline Id
+dirpool_sibling(Dirpool *dp, Id did)
+{
+ /* if this block contains another entry, simply return it */
+ if (did + 1 < dp->ndirs && dp->dirs[did + 1] > 0)
+ return did + 1;
+ /* end of block reached, rewind to get to the block's
+ * dirtraverse entry */
+ while (dp->dirs[--did] > 0)
+ ;
+ /* need to special case did == 0 to prevent looping */
+ if (!did)
+ return 0;
+ if (!dp->dirtraverse)
+ dirpool_make_dirtraverse(dp);
+ return dp->dirtraverse[did];
+}
+
+/* return the first child entry of directory did */
+static inline Id
+dirpool_child(Dirpool *dp, Id did)
+{
+ if (!dp->dirtraverse)
+ dirpool_make_dirtraverse(dp);
+ return dp->dirtraverse[did];
+}
+
+static inline void
+dirpool_free_dirtraverse(Dirpool *dp)
+{
+ solv_free(dp->dirtraverse);
+ dp->dirtraverse = 0;
+}
+
+static inline Id
+dirpool_compid(Dirpool *dp, Id did)
+{
+ return dp->dirs[did];
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_DIRPOOL_H */
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * evr.c
+ *
+ * version compare
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include "evr.h"
+#include "pool.h"
+
+
+
+#if defined(DEBIAN) || defined(MULTI_SEMANTICS)
+
+/* debian type version compare */
+int
+solv_vercmp_deb(const char *s1, const char *q1, const char *s2, const char *q2)
+{
+ int r, c1, c2;
+ while (1)
+ {
+ c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
+ c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
+ if ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9'))
+ {
+ while (c1 == '0')
+ c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
+ while (c2 == '0')
+ c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
+ r = 0;
+ while ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9'))
+ {
+ if (!r)
+ r = c1 - c2;
+ c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
+ c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
+ }
+ if (c1 >= '0' && c1 <= '9')
+ return 1;
+ if (c2 >= '0' && c2 <= '9')
+ return -1;
+ if (r)
+ return r < 0 ? -1 : 1;
+ }
+ c1 = c1 == '~' ? -1 : !c1 || (c1 >= '0' && c1 <= '9') || (c1 >= 'A' && c1 <= 'Z') || (c1 >= 'a' && c1 <= 'z') ? c1 : c1 + 256;
+ c2 = c2 == '~' ? -1 : !c2 || (c2 >= '0' && c2 <= '9') || (c2 >= 'A' && c2 <= 'Z') || (c2 >= 'a' && c2 <= 'z') ? c2 : c2 + 256;
+ r = c1 - c2;
+ if (r)
+ return r < 0 ? -1 : 1;
+ if (!c1)
+ return 0;
+ }
+}
+
+#endif
+
+#if !defined(DEBIAN) || defined(MULTI_SEMANTICS)
+
+/* rpm type version compare */
+/* note: the code assumes that *q1 and *q2 are not alphanumeric! */
+
+int
+solv_vercmp_rpm(const char *s1, const char *q1, const char *s2, const char *q2)
+{
+ int r = 0;
+ const char *e1, *e2;
+
+ for (;;)
+ {
+ while (s1 < q1 && !(*s1 >= '0' && *s1 <= '9') &&
+ !(*s1 >= 'a' && *s1 <= 'z') && !(*s1 >= 'A' && *s1 <= 'Z') && *s1 != '~')
+ s1++;
+ while (s2 < q2 && !(*s2 >= '0' && *s2 <= '9') &&
+ !(*s2 >= 'a' && *s2 <= 'z') && !(*s2 >= 'A' && *s2 <= 'Z') && *s2 != '~')
+ s2++;
+ if (s1 < q1 && *s1 == '~')
+ {
+ if (s2 < q2 && *s2 == '~')
+ {
+ s1++;
+ s2++;
+ continue;
+ }
+ return -1;
+ }
+ if (s2 < q2 && *s2 == '~')
+ return 1;
+ if (s1 >= q1 || s2 >= q2)
+ break;
+ 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;
+}
+
+int
+solv_vercmp_rpm_notilde(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;
+}
+
+#endif
+
+#if defined(HAIKU) || defined(MULTI_SEMANTICS)
+
+static int
+solv_cmp_version_part_haiku(const char *s1, const char *q1, const char *s2,
+ const char *q2)
+{
+ while (s1 < q1 && s2 < q2)
+ {
+ int cmp, len1, len2;
+ const char *part1 = s1, *part2 = s2;
+
+ /* compare non-number part */
+ while (s1 < q1 && !isdigit(*s1))
+ s1++;
+ while (s2 < q2 && !isdigit(*s2))
+ s2++;
+
+ if (part1 != s1)
+ {
+ if (part2 == s2)
+ return 1;
+
+ len1 = s1 - part1;
+ len2 = s2 - part2;
+ cmp = strncmp(part1, part2, len1 < len2 ? len1 : len2);
+ if (cmp != 0)
+ return cmp;
+ if (len1 != len2)
+ return len1 - len2;
+ }
+ else if (part2 != s2)
+ return -1;
+
+ /* compare number part */
+ part1 = s1;
+ part2 = s2;
+
+ while (s1 < q1 && isdigit(*s1))
+ s1++;
+ while (s2 < q2 && isdigit(*s2))
+ s2++;
+
+ while (part1 + 1 < s1 && *part1 == '0')
+ part1++;
+ while (part2 + 1 < s2 && *part2 == '0')
+ part2++;
+
+ len1 = s1 - part1;
+ len2 = s2 - part2;
+ if (len1 != len2)
+ return len1 - len2;
+ if (len1 == 0)
+ return 0;
+
+ cmp = strncmp(part1, part2, len1);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
+}
+
+int
+solv_vercmp_haiku(const char *s1, const char *q1, const char *s2, const char *q2)
+{
+ const char *pre1 = s1;
+ const char *pre2 = s2;
+ int cmp;
+
+ /* find pre-release separator */
+ while (pre1 != q1 && *pre1 != '~')
+ pre1++;
+ while (pre2 != q2 && *pre2 != '~')
+ pre2++;
+
+ /* compare main versions */
+ cmp = solv_cmp_version_part_haiku(s1, pre1, s2, pre2);
+ if (cmp != 0)
+ return cmp < 0 ? -1 : 1; /* must return -1, 0, or 1 */
+
+ /* main versions are equal -- compare pre-release (none is greatest) */
+ if (pre1 == q1)
+ return pre2 == q2 ? 0 : 1;
+ if (pre2 == q2)
+ return -1;
+
+ cmp = solv_cmp_version_part_haiku(pre1 + 1, q1, pre2 + 1, q2);
+ return cmp == 0 ? 0 : cmp < 0 ? -1 : 1; /* must return -1, 0, or 1 */
+}
+
+#endif /* HAIKU */
+
+
+/*
+ * the solv_vercmp variant your system uses.
+ */
+int
+solv_vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
+{
+#if defined(DEBIAN)
+ return solv_vercmp_deb(s1, q1, s2, q2);
+#elif defined(ARCHLINUX)
+ return solv_vercmp_rpm_notilde(s1, q1, s2, q2);
+#elif defined(HAIKU)
+ return solv_vercmp_haiku(s1, q1, s2, q2);
+#else
+ return solv_vercmp_rpm(s1, q1, s2, q2);
+#endif
+}
+
+#if defined(MULTI_SEMANTICS)
+# define solv_vercmp (*(pool->disttype == DISTTYPE_DEB ? &solv_vercmp_deb : \
+ pool->disttype == DISTTYPE_HAIKU ? solv_vercmp_haiku : \
+ &solv_ver##cmp_rpm))
+#elif defined(DEBIAN)
+# define solv_vercmp solv_vercmp_deb
+#elif defined(ARCHLINUX)
+# define solv_vercmp solv_vercmp_rpm_notilde
+#elif defined(HAIKU)
+# define solv_vercmp solv_vercmp_haiku
+#else
+# define solv_vercmp solv_vercmp_rpm
+#endif
+
+/* edition (e:v-r) compare */
+int
+pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode)
+{
+ int r;
+ const char *s1, *s2;
+ const char *r1, *r2;
+
+ if (evr1 == evr2)
+ return 0;
+
+#if 0
+ POOL_DEBUG(DEBUG_EVRCMP, "evrcmp %s %s mode=%d\n", evr1, evr2, mode);
+#endif
+ for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
+ ;
+ for (s2 = evr2; *s2 >= '0' && *s2 <= '9'; s2++)
+ ;
+ if (mode == EVRCMP_MATCH && (*evr1 == ':' || *evr2 == ':'))
+ {
+ /* empty epoch, skip epoch check */
+ if (*s1 == ':')
+ evr1 = s1 + 1;
+ if (*s2 == ':')
+ evr2 = s2 + 1;
+ s1 = evr1;
+ s2 = evr2;
+ }
+
+ /* compare the epoch */
+ if (s1 == evr1 || *s1 != ':')
+ s1 = 0;
+ if (s2 == evr2 || *s2 != ':')
+ s2 = 0;
+ if (s1 && s2)
+ {
+ r = solv_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 = s2 + 1;
+ }
+
+ /* same epoch, now split into version/release */
+ for (s1 = evr1, r1 = 0; *s1; s1++)
+ if (*s1 == '-')
+ r1 = s1;
+ for (s2 = evr2, r2 = 0; *s2; s2++)
+ if (*s2 == '-')
+ r2 = s2;
+ r = 0;
+ if (mode != EVRCMP_MATCH || (evr1 != (r1 ? r1 : s1) && evr2 != (r2 ? r2 : s2)))
+ r = solv_vercmp(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2);
+ if (r)
+ return r;
+
+ if (mode == EVRCMP_COMPARE)
+ {
+ if (!r1 && r2)
+ return -1;
+ if (r1 && !r2)
+ return 1;
+ }
+ if (mode == EVRCMP_COMPARE_EVONLY)
+ return 0;
+ if (mode == EVRCMP_MATCH_RELEASE)
+ {
+ /* rpm treats empty releases as missing, i.e "foo = 4-" is the same as "foo = 4" */
+ if (r1 && r1 + 1 == s1)
+ r1 = 0;
+ if (r2 && r2 + 1 == s2)
+ r2 = 0;
+ }
+ if (r1 && r2)
+ {
+ r1++;
+ r2++;
+ if (mode != EVRCMP_MATCH || (s1 != r1 && s2 != r2))
+ {
+ if (pool->havedistepoch)
+ {
+ const char *d1, *d2;
+ for (d1 = r1; d1 < s1; d1++)
+ if (*d1 == ':')
+ break;
+ for (d2 = r2; d2 < s2; d2++)
+ if (*d2 == ':')
+ break;
+ /* XXX: promote just in one direction? */
+ r = solv_vercmp(r1, d1 ? d1 : s1, r2, d2 ? d2 : s2);
+ if (r == 0 && d1 < s1 && d2 < s2)
+ r = solv_vercmp(d1 + 1, s1, d2 + 1, s2);
+ }
+ else
+ r = solv_vercmp(r1, s1, r2, s2);
+ }
+ }
+ else if (mode == EVRCMP_MATCH_RELEASE)
+ {
+ if (!r1 && r2)
+ return -2;
+ if (r1 && !r2)
+ return 2;
+ }
+ return r;
+}
+
+int
+pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode)
+{
+ const char *evr1, *evr2;
+ if (evr1id == evr2id)
+ return 0;
+ evr1 = pool_id2str(pool, evr1id);
+ evr2 = pool_id2str(pool, evr2id);
+ return pool_evrcmp_str(pool, evr1, evr2, mode);
+}
+
+int
+pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release)
+{
+ const char *evr1;
+ const char *s1;
+ const char *r1;
+ int r;
+
+ evr1 = pool_id2str(pool, evrid);
+ for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
+ ;
+ if (s1 != evr1 && *s1 == ':')
+ {
+ if (epoch)
+ {
+ r = solv_vercmp(evr1, s1, epoch, epoch + strlen(epoch));
+ if (r)
+ return r;
+ }
+ evr1 = s1 + 1;
+ }
+ else if (epoch)
+ {
+ while (*epoch == '0')
+ epoch++;
+ if (*epoch)
+ return -1;
+ }
+ for (s1 = evr1, r1 = 0; *s1; s1++)
+ if (*s1 == '-')
+ r1 = s1;
+ if (version)
+ {
+ r = solv_vercmp(evr1, r1 ? r1 : s1, version, version + strlen(version));
+ if (r)
+ return r;
+ }
+ if (release)
+ {
+ if (!r1)
+ return -1;
+ r = solv_vercmp(r1 + 1, s1, release, release + strlen(release));
+ if (r)
+ return r;
+ }
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * evr.h
+ *
+ */
+
+#ifndef LIBSOLV_EVR_H
+#define LIBSOLV_EVR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "pooltypes.h"
+
+#define EVRCMP_COMPARE 0
+#define EVRCMP_MATCH_RELEASE 1
+#define EVRCMP_MATCH 2
+#define EVRCMP_COMPARE_EVONLY 3
+
+extern int solv_vercmp(const char *s1, const char *q1, const char *s2, const char *q2);
+
+extern int pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode);
+extern int pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode);
+extern int pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_EVR_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * hash.h
+ * generic hash functions
+ */
+
+#ifndef LIBSOLV_HASH_H
+#define LIBSOLV_HASH_H
+
+#include "pooltypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* value of a hash */
+typedef unsigned int Hashval;
+
+/* 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;
+}
+
+static inline Hashval
+strnhash(const char *str, unsigned len)
+{
+ Hashval r = 0;
+ unsigned int c;
+ while (len-- && (c = *(const unsigned char *)str++) != 0)
+ r += (r << 3) + c;
+ return r;
+}
+
+static inline Hashval
+strhash_cont(const char *str, Hashval r)
+{
+ 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) > 2 * num + 3
+ *
+ * used for Hashtable 'modulo' operation
+ */
+static inline Hashval
+mkmask(unsigned int num)
+{
+ num = num * 2 + 3;
+ while (num & (num - 1))
+ num &= num - 1;
+ return num * 2 - 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_HASH_H */
--- /dev/null
+/*
+ * Copyright (c) 2007-2014, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * knownid.h
+ *
+ */
+
+/*
+ * Warning: you're free to append new entries, but insert/delete breaks
+ * the ABI!
+ */
+
+#ifndef LIBSOLV_KNOWNID_H
+#define LIBSOLV_KNOWNID_H
+
+#undef KNOWNID
+#ifdef KNOWNID_INITIALIZE
+# define KNOWNID(a, b) b
+static const char *initpool_data[] = {
+#else
+# define KNOWNID(a, b) a
+enum solv_knownid {
+#endif
+
+KNOWNID(ID_NULL, "<NULL>"),
+KNOWNID(ID_EMPTY, ""),
+
+/* The following Ids are stored in the solvable and must
+ * come in one block */
+KNOWNID(SOLVABLE_NAME, "solvable:name"),
+KNOWNID(SOLVABLE_ARCH, "solvable:arch"),
+KNOWNID(SOLVABLE_EVR, "solvable:evr"),
+KNOWNID(SOLVABLE_VENDOR, "solvable:vendor"),
+KNOWNID(SOLVABLE_PROVIDES, "solvable:provides"),
+KNOWNID(SOLVABLE_OBSOLETES, "solvable:obsoletes"),
+KNOWNID(SOLVABLE_CONFLICTS, "solvable:conflicts"),
+KNOWNID(SOLVABLE_REQUIRES, "solvable:requires"),
+KNOWNID(SOLVABLE_RECOMMENDS, "solvable:recommends"),
+KNOWNID(SOLVABLE_SUGGESTS, "solvable:suggests"),
+KNOWNID(SOLVABLE_SUPPLEMENTS, "solvable:supplements"),
+KNOWNID(SOLVABLE_ENHANCES, "solvable:enhances"),
+KNOWNID(RPM_RPMDBID, "rpm:dbid"),
+
+/* normal requires before this, prereqs after this */
+KNOWNID(SOLVABLE_PREREQMARKER, "solvable:prereqmarker"),
+/* normal provides before this, generated file provides after this */
+KNOWNID(SOLVABLE_FILEMARKER, "solvable:filemarker"),
+
+KNOWNID(NAMESPACE_INSTALLED, "namespace:installed"),
+KNOWNID(NAMESPACE_MODALIAS, "namespace:modalias"),
+KNOWNID(NAMESPACE_SPLITPROVIDES, "namespace:splitprovides"),
+KNOWNID(NAMESPACE_LANGUAGE, "namespace:language"),
+KNOWNID(NAMESPACE_FILESYSTEM, "namespace:filesystem"),
+KNOWNID(NAMESPACE_OTHERPROVIDERS, "namespace:otherproviders"),
+
+KNOWNID(SYSTEM_SYSTEM, "system:system"),
+
+/* special solvable architectures */
+KNOWNID(ARCH_SRC, "src"),
+KNOWNID(ARCH_NOSRC, "nosrc"),
+KNOWNID(ARCH_NOARCH, "noarch"),
+KNOWNID(ARCH_ALL, "all"),
+KNOWNID(ARCH_ANY, "any"),
+
+/* the meta tags used in solv file storage */
+KNOWNID(REPOSITORY_SOLVABLES, "repository:solvables"),
+KNOWNID(REPOSITORY_DELTAINFO, "repository:deltainfo"),
+
+/* sub-repository information, they will get loaded on demand */
+KNOWNID(REPOSITORY_EXTERNAL, "repository:external"),
+KNOWNID(REPOSITORY_KEYS, "repository:keys"),
+KNOWNID(REPOSITORY_LOCATION, "repository:location"),
+
+/* file provides already added to our solvables */
+KNOWNID(REPOSITORY_ADDEDFILEPROVIDES, "repository:addedfileprovides"),
+/* inode of the rpm database for rpm --rebuilddb detection */
+KNOWNID(REPOSITORY_RPMDBCOOKIE, "repository:rpmdbcookie"),
+
+/* the known data types */
+KNOWNID(REPOKEY_TYPE_VOID, "repokey:type:void"),
+KNOWNID(REPOKEY_TYPE_CONSTANT, "repokey:type:constant"),
+KNOWNID(REPOKEY_TYPE_CONSTANTID, "repokey:type:constantid"),
+KNOWNID(REPOKEY_TYPE_ID, "repokey:type:id"),
+KNOWNID(REPOKEY_TYPE_NUM, "repokey:type:num"),
+KNOWNID(REPOKEY_TYPE_U32, "repokey:type:num32"),
+KNOWNID(REPOKEY_TYPE_DIR, "repokey:type:dir"),
+KNOWNID(REPOKEY_TYPE_STR, "repokey:type:str"),
+KNOWNID(REPOKEY_TYPE_BINARY, "repokey:type:binary"),
+KNOWNID(REPOKEY_TYPE_IDARRAY, "repokey:type:idarray"),
+KNOWNID(REPOKEY_TYPE_REL_IDARRAY, "repokey:type:relidarray"),
+KNOWNID(REPOKEY_TYPE_DIRSTRARRAY, "repokey:type:dirstrarray"),
+KNOWNID(REPOKEY_TYPE_DIRNUMNUMARRAY, "repokey:type:dirnumnumarray"),
+KNOWNID(REPOKEY_TYPE_MD5, "repokey:type:md5"),
+KNOWNID(REPOKEY_TYPE_SHA1, "repokey:type:sha1"),
+KNOWNID(REPOKEY_TYPE_SHA224, "repokey:type:sha224"),
+KNOWNID(REPOKEY_TYPE_SHA256, "repokey:type:sha256"),
+KNOWNID(REPOKEY_TYPE_SHA384, "repokey:type:sha384"),
+KNOWNID(REPOKEY_TYPE_SHA512, "repokey:type:sha512"),
+KNOWNID(REPOKEY_TYPE_FIXARRAY, "repokey:type:fixarray"),
+KNOWNID(REPOKEY_TYPE_FLEXARRAY, "repokey:type:flexarray"),
+KNOWNID(REPOKEY_TYPE_DELETED, "repokey:type:deleted"), /* internal only */
+
+KNOWNID(SOLVABLE_SUMMARY, "solvable:summary"),
+KNOWNID(SOLVABLE_DESCRIPTION, "solvable:description"),
+KNOWNID(SOLVABLE_DISTRIBUTION, "solvable:distribution"),
+KNOWNID(SOLVABLE_AUTHORS, "solvable:authors"),
+KNOWNID(SOLVABLE_PACKAGER, "solvable:packager"),
+KNOWNID(SOLVABLE_GROUP, "solvable:group"),
+KNOWNID(SOLVABLE_URL, "solvable:url"),
+KNOWNID(SOLVABLE_KEYWORDS, "solvable:keywords"),
+KNOWNID(SOLVABLE_LICENSE, "solvable:license"),
+KNOWNID(SOLVABLE_BUILDTIME, "solvable:buildtime"),
+KNOWNID(SOLVABLE_BUILDHOST, "solvable:buildhost"),
+KNOWNID(SOLVABLE_EULA, "solvable:eula"),
+KNOWNID(SOLVABLE_CPEID, "solvable:cpeid"),
+KNOWNID(SOLVABLE_MESSAGEINS, "solvable:messageins"),
+KNOWNID(SOLVABLE_MESSAGEDEL, "solvable:messagedel"),
+KNOWNID(SOLVABLE_INSTALLSIZE, "solvable:installsize"),
+KNOWNID(SOLVABLE_DISKUSAGE, "solvable:diskusage"),
+KNOWNID(SOLVABLE_FILELIST, "solvable:filelist"),
+KNOWNID(SOLVABLE_INSTALLTIME, "solvable:installtime"),
+KNOWNID(SOLVABLE_MEDIADIR, "solvable:mediadir"),
+KNOWNID(SOLVABLE_MEDIAFILE, "solvable:mediafile"),
+KNOWNID(SOLVABLE_MEDIANR, "solvable:medianr"),
+KNOWNID(SOLVABLE_MEDIABASE, "solvable:mediabase"), /* <location xml:base=... > */
+KNOWNID(SOLVABLE_DOWNLOADSIZE, "solvable:downloadsize"),
+KNOWNID(SOLVABLE_SOURCEARCH, "solvable:sourcearch"),
+KNOWNID(SOLVABLE_SOURCENAME, "solvable:sourcename"),
+KNOWNID(SOLVABLE_SOURCEEVR, "solvable:sourceevr"),
+KNOWNID(SOLVABLE_ISVISIBLE, "solvable:isvisible"),
+KNOWNID(SOLVABLE_TRIGGERS, "solvable:triggers"),
+KNOWNID(SOLVABLE_CHECKSUM, "solvable:checksum"),
+KNOWNID(SOLVABLE_PKGID, "solvable:pkgid"), /* pkgid: md5sum over header + payload */
+KNOWNID(SOLVABLE_HDRID, "solvable:hdrid"), /* hdrid: sha1sum over header only */
+KNOWNID(SOLVABLE_LEADSIGID, "solvable:leadsigid"), /* leadsigid: md5sum over lead + sigheader */
+
+KNOWNID(SOLVABLE_PATCHCATEGORY, "solvable:patchcategory"),
+KNOWNID(SOLVABLE_HEADEREND, "solvable:headerend"),
+KNOWNID(SOLVABLE_CHANGELOG, "solvable:changelog"),
+KNOWNID(SOLVABLE_CHANGELOG_AUTHOR, "solvable:changelog:author"),
+KNOWNID(SOLVABLE_CHANGELOG_TIME, "solvable:changelog:time"),
+KNOWNID(SOLVABLE_CHANGELOG_TEXT, "solvable:changelog:text"),
+
+/* stuff for solvables of type pattern */
+KNOWNID(SOLVABLE_CATEGORY, "solvable:category"),
+KNOWNID(SOLVABLE_INCLUDES, "solvable:includes"),
+KNOWNID(SOLVABLE_EXTENDS, "solvable:extends"),
+KNOWNID(SOLVABLE_ICON, "solvable:icon"),
+KNOWNID(SOLVABLE_ORDER, "solvable:order"),
+
+/* extra definitions for updates (i.e. patch: solvables) */
+KNOWNID(UPDATE_REBOOT, "update:reboot"), /* reboot suggested (kernel update) */
+KNOWNID(UPDATE_RESTART, "update:restart"), /* restart suggested (update stack update) */
+KNOWNID(UPDATE_RELOGIN, "update:relogin"), /* relogin suggested */
+
+KNOWNID(UPDATE_MESSAGE, "update:message"), /* informative message */
+KNOWNID(UPDATE_SEVERITY, "update:severity"), /* "Important", ...*/
+KNOWNID(UPDATE_RIGHTS, "update:rights"), /* copyright */
+
+/* 'content' of patch, usually list of packages */
+KNOWNID(UPDATE_COLLECTION, "update:collection"), /* "name evr arch" */
+KNOWNID(UPDATE_COLLECTION_NAME, "update:collection:name"), /* name */
+KNOWNID(UPDATE_COLLECTION_EVR, "update:collection:evr"), /* epoch:version-release */
+KNOWNID(UPDATE_COLLECTION_ARCH, "update:collection:arch"), /* architecture */
+KNOWNID(UPDATE_COLLECTION_FILENAME, "update:collection:filename"), /* filename (of rpm) */
+KNOWNID(UPDATE_COLLECTION_FLAGS, "update:collection:flags"), /* reboot(1)/restart(2) suggested if this rpm gets updated */
+
+KNOWNID(UPDATE_REFERENCE, "update:reference"), /* external references for the update */
+KNOWNID(UPDATE_REFERENCE_TYPE, "update:reference:type"), /* type, e.g. 'bugzilla' or 'cve' */
+KNOWNID(UPDATE_REFERENCE_HREF, "update:reference:href"), /* href, e.g. 'http://bugzilla...' */
+KNOWNID(UPDATE_REFERENCE_ID, "update:reference:id"), /* id, e.g. bug number */
+KNOWNID(UPDATE_REFERENCE_TITLE, "update:reference:title"), /* title, e.g. "the bla forz scribs on fuggle" */
+
+/* extra definitions for products */
+KNOWNID(PRODUCT_REFERENCEFILE, "product:referencefile"), /* installed product only */
+KNOWNID(PRODUCT_SHORTLABEL, "product:shortlabel"), /* not in repomd? */
+KNOWNID(PRODUCT_DISTPRODUCT, "product:distproduct"), /* obsolete */
+KNOWNID(PRODUCT_DISTVERSION, "product:distversion"), /* obsolete */
+KNOWNID(PRODUCT_TYPE, "product:type"), /* e.g. 'base' */
+KNOWNID(PRODUCT_URL, "product:url"),
+KNOWNID(PRODUCT_URL_TYPE, "product:url:type"),
+KNOWNID(PRODUCT_FLAGS, "product:flags"), /* e.g. 'update', 'no_you' */
+KNOWNID(PRODUCT_PRODUCTLINE, "product:productline"), /* installed product only */
+KNOWNID(PRODUCT_REGISTER_TARGET, "product:regtarget"), /* installed and available product */
+KNOWNID(PRODUCT_REGISTER_RELEASE, "product:regrelease"), /* installed product only */
+KNOWNID(PRODUCT_UPDATES_REPOID, "product:updates:repoid"),
+KNOWNID(PRODUCT_UPDATES, "product:updates"),
+KNOWNID(PRODUCT_ENDOFLIFE, "product:endoflife"),
+
+/* argh, should rename to repository and unify with REPOMD */
+KNOWNID(SUSETAGS_DATADIR, "susetags:datadir"),
+KNOWNID(SUSETAGS_DESCRDIR, "susetags:descrdir"),
+KNOWNID(SUSETAGS_DEFAULTVENDOR, "susetags:defaultvendor"),
+KNOWNID(SUSETAGS_FILE, "susetags:file"),
+KNOWNID(SUSETAGS_FILE_NAME, "susetags:file:name"),
+KNOWNID(SUSETAGS_FILE_TYPE, "susetags:file:type"),
+KNOWNID(SUSETAGS_FILE_CHECKSUM, "susetags:file:checksum"),
+KNOWNID(SUSETAGS_SHARE_NAME, "susetags:share:name"),
+KNOWNID(SUSETAGS_SHARE_EVR, "susetags:share:evr"),
+KNOWNID(SUSETAGS_SHARE_ARCH, "susetags:share:arch"),
+
+/* timestamp then the repository was generated */
+KNOWNID(REPOSITORY_TIMESTAMP, "repository:timestamp"),
+/* hint when the metadata could be outdated w/respect to generated timestamp */
+KNOWNID(REPOSITORY_EXPIRE, "repository:expire"),
+/* which things does this repo provides updates for, if it does (array) */
+KNOWNID(REPOSITORY_UPDATES, "repository:updates"), /* obsolete? */
+/* which products this repository is supposed to be for (array) */
+KNOWNID(REPOSITORY_DISTROS, "repository:distros"),
+KNOWNID(REPOSITORY_PRODUCT_LABEL, "repository:product:label"),
+KNOWNID(REPOSITORY_PRODUCT_CPEID, "repository:product:cpeid"),
+KNOWNID(REPOSITORY_REPOID, "repository:repoid"), /* obsolete? */
+/* keyword (tags) for this repository */
+KNOWNID(REPOSITORY_KEYWORDS, "repository:keywords"),
+/* revision of the repository. arbitrary string */
+KNOWNID(REPOSITORY_REVISION, "repository:revision"),
+KNOWNID(REPOSITORY_TOOLVERSION, "repository:toolversion"),
+
+KNOWNID(DELTA_PACKAGE_NAME, "delta:pkgname"),
+KNOWNID(DELTA_PACKAGE_EVR, "delta:pkgevr"),
+KNOWNID(DELTA_PACKAGE_ARCH, "delta:pkgarch"),
+KNOWNID(DELTA_LOCATION_DIR, "delta:locdir"),
+KNOWNID(DELTA_LOCATION_NAME, "delta:locname"),
+KNOWNID(DELTA_LOCATION_EVR, "delta:locevr"),
+KNOWNID(DELTA_LOCATION_SUFFIX, "delta:locsuffix"),
+KNOWNID(DELTA_DOWNLOADSIZE, "delta:downloadsize"),
+KNOWNID(DELTA_CHECKSUM, "delta:checksum"),
+KNOWNID(DELTA_BASE_EVR, "delta:baseevr"),
+KNOWNID(DELTA_SEQ_NAME, "delta:seqname"),
+KNOWNID(DELTA_SEQ_EVR, "delta:seqevr"),
+KNOWNID(DELTA_SEQ_NUM, "delta:seqnum"),
+KNOWNID(DELTA_LOCATION_BASE, "delta:locbase"), /* <location xml:base=... > */
+
+KNOWNID(REPOSITORY_REPOMD, "repository:repomd"),
+KNOWNID(REPOSITORY_REPOMD_TYPE, "repository:repomd:type"),
+KNOWNID(REPOSITORY_REPOMD_LOCATION, "repository:repomd:location"),
+KNOWNID(REPOSITORY_REPOMD_TIMESTAMP, "repository:repomd:timestamp"),
+KNOWNID(REPOSITORY_REPOMD_CHECKSUM, "repository:repomd:checksum"),
+KNOWNID(REPOSITORY_REPOMD_OPENCHECKSUM, "repository:repomd:openchecksum"),
+KNOWNID(REPOSITORY_REPOMD_SIZE, "repository:repomd:size"),
+
+KNOWNID(PUBKEY_KEYID, "pubkey:keyid"),
+KNOWNID(PUBKEY_FINGERPRINT, "pubkey:fingerprint"),
+KNOWNID(PUBKEY_EXPIRES, "pubkey:expires"),
+KNOWNID(PUBKEY_SIGNATURES, "pubkey:signatures"),
+KNOWNID(PUBKEY_DATA, "pubkey:data"),
+KNOWNID(PUBKEY_SUBKEYOF, "pubkey:subkeyof"),
+
+KNOWNID(SIGNATURE_ISSUER, "signature:issuer"),
+KNOWNID(SIGNATURE_TIME, "signature:time"),
+KNOWNID(SIGNATURE_EXPIRES, "signature:expires"),
+KNOWNID(SIGNATURE_DATA, "signature:data"),
+
+KNOWNID(PRODUCT_REGISTER_FLAVOR, "product:regflavor"), /* installed and available product */
+
+KNOWNID(SOLVABLE_INSTALLSTATUS, "solvable:installstatus"), /* debian install status */
+
+KNOWNID(ID_NUM_INTERNAL, 0)
+
+#ifdef KNOWNID_INITIALIZE
+};
+#else
+};
+#endif
+
+#undef KNOWNID
+
+#endif
+
+
--- /dev/null
+SOLV_1.0 {
+ global:
+ dataiterator_clonepos;
+ dataiterator_entersub;
+ dataiterator_free;
+ dataiterator_init;
+ dataiterator_init_clone;
+ dataiterator_jump_to_repo;
+ dataiterator_jump_to_solvid;
+ dataiterator_match;
+ dataiterator_prepend_keyname;
+ dataiterator_seek;
+ dataiterator_set_keyname;
+ dataiterator_set_match;
+ dataiterator_set_search;
+ dataiterator_setpos;
+ dataiterator_setpos_parent;
+ dataiterator_skip_attribute;
+ dataiterator_skip_repo;
+ dataiterator_skip_solvable;
+ dataiterator_step;
+ dataiterator_strdup;
+ datamatcher_free;
+ datamatcher_init;
+ datamatcher_match;
+ dirpool_add_dir;
+ dirpool_free;
+ dirpool_init;
+ dirpool_make_dirtraverse;
+ map_and;
+ map_subtract;
+ map_free;
+ map_grow;
+ map_init;
+ map_init_clone;
+ map_or;
+ policy_filter_unwanted;
+ policy_findupdatepackages;
+ policy_illegal2str;
+ policy_illegal_archchange;
+ policy_illegal_vendorchange;
+ policy_is_illegal;
+ pool_add_fileconflicts_deps;
+ pool_add_userinstalled_jobs;
+ pool_addfileprovides;
+ pool_addfileprovides_queue;
+ pool_addrelproviders;
+ pool_addvendorclass;
+ pool_alloctmpspace;
+ pool_arch2color_slow;
+ pool_bin2hex;
+ pool_calc_duchanges;
+ pool_calc_installsizechange;
+ pool_clear_pos;
+ pool_create;
+ pool_create_state_maps;
+ pool_createwhatprovides;
+ pool_debug;
+ pool_dep2str;
+ pool_error;
+ pool_errstr;
+ pool_evrcmp;
+ pool_evrcmp_str;
+ pool_evrmatch;
+ pool_flush_namespaceproviders;
+ pool_free;
+ pool_freeallrepos;
+ pool_freeidhashes;
+ pool_freetmpspace;
+ pool_freewhatprovides;
+ pool_get_flag;
+ pool_get_rootdir;
+ pool_id2evr;
+ pool_id2langid;
+ pool_id2rel;
+ pool_id2str;
+ pool_ids2whatprovides;
+ pool_intersect_evrs;
+ pool_isemptyupdatejob;
+ pool_job2solvables;
+ pool_job2str;
+ pool_lookup_bin_checksum;
+ pool_lookup_checksum;
+ pool_lookup_deltalocation;
+ pool_lookup_id;
+ pool_lookup_idarray;
+ pool_lookup_num;
+ pool_lookup_str;
+ pool_lookup_void;
+ pool_match_dep;
+ pool_match_nevr_rel;
+ pool_prepend_rootdir;
+ pool_prepend_rootdir_tmp;
+ pool_queuetowhatprovides;
+ pool_rel2id;
+ pool_search;
+ pool_selection2str;
+ pool_set_custom_vendorcheck;
+ pool_set_flag;
+ pool_set_installed;
+ pool_set_languages;
+ pool_set_rootdir;
+ pool_setarch;
+ pool_setarchpolicy;
+ pool_setdebugcallback;
+ pool_setdebuglevel;
+ pool_setdebugmask;
+ pool_setdisttype;
+ pool_setloadcallback;
+ pool_setnamespacecallback;
+ pool_setvendorclasses;
+ pool_shrink_rels;
+ pool_shrink_strings;
+ pool_solvable2str;
+ pool_str2id;
+ pool_strn2id;
+ pool_tmpappend;
+ pool_tmpjoin;
+ pool_trivial_installable;
+ pool_trivial_installable_multiversionmap;
+ pool_vendor2mask;
+ pool_whatmatchesdep;
+ queue_alloc_one;
+ queue_alloc_one_head;
+ queue_delete;
+ queue_delete2;
+ queue_deleten;
+ queue_free;
+ queue_init;
+ queue_init_buffer;
+ queue_init_clone;
+ queue_insert;
+ queue_insert2;
+ queue_insertn;
+ queue_prealloc;
+ repo_add_deparray;
+ repo_add_idarray;
+ repo_add_poolstr_array;
+ repo_add_repodata;
+ repo_add_solv;
+ repo_add_solvable;
+ repo_add_solvable_block;
+ repo_add_solvable_block_before;
+ repo_addid;
+ repo_addid_dep;
+ repo_create;
+ repo_disable_paging;
+ repo_empty;
+ repo_fix_conflicts;
+ repo_fix_supplements;
+ repo_free;
+ repo_free_solvable;
+ repo_free_solvable_block;
+ repo_id2repodata;
+ repo_internalize;
+ repo_last_repodata;
+ repo_lookup_bin_checksum;
+ repo_lookup_binary;
+ repo_lookup_checksum;
+ repo_lookup_deparray;
+ repo_lookup_id;
+ repo_lookup_idarray;
+ repo_lookup_num;
+ repo_lookup_str;
+ repo_lookup_type;
+ repo_lookup_void;
+ repo_matchvalue;
+ repo_reserve_ids;
+ repo_search;
+ repo_set_deparray;
+ repo_set_id;
+ repo_set_idarray;
+ repo_set_num;
+ repo_set_poolstr;
+ repo_set_str;
+ repo_sidedata_create;
+ repo_unset;
+ repo_write;
+ repo_write_filtered;
+ repo_write_stdkeyfilter;
+ repodata_add_dirnumnum;
+ repodata_add_dirstr;
+ repodata_add_fixarray;
+ repodata_add_flexarray;
+ repodata_add_idarray;
+ repodata_add_poolstr_array;
+ repodata_chk2str;
+ repodata_create_stubs;
+ repodata_dir2str;
+ repodata_disable_paging;
+ repodata_empty;
+ repodata_extend;
+ repodata_extend_block;
+ repodata_free;
+ repodata_free_dircache;
+ repodata_free_schemahash;
+ repodata_freedata;
+ repodata_globalize_id;
+ repodata_initdata;
+ repodata_internalize;
+ repodata_key2id;
+ repodata_localize_id;
+ repodata_lookup_bin_checksum;
+ repodata_lookup_binary;
+ repodata_lookup_dirstrarray_uninternalized;
+ repodata_lookup_id;
+ repodata_lookup_id_uninternalized;
+ repodata_lookup_idarray;
+ repodata_lookup_num;
+ repodata_lookup_str;
+ repodata_lookup_type;
+ repodata_lookup_void;
+ repodata_memused;
+ repodata_merge_attrs;
+ repodata_merge_some_attrs;
+ repodata_new_handle;
+ repodata_schema2id;
+ repodata_search;
+ repodata_set_binary;
+ repodata_set_bin_checksum;
+ repodata_set_checksum;
+ repodata_set_constant;
+ repodata_set_constantid;
+ repodata_set_deltalocation;
+ repodata_set_id;
+ repodata_set_idarray;
+ repodata_set_location;
+ repodata_set_num;
+ repodata_set_poolstr;
+ repodata_set_sourcepkg;
+ repodata_set_str;
+ repodata_set_void;
+ repodata_setpos_kv;
+ repodata_shrink;
+ repodata_str2dir;
+ repodata_stringify;
+ repodata_swap_attrs;
+ repodata_translate_id;
+ repodata_unset;
+ repodata_unset_uninternalized;
+ repodata_write;
+ repodata_write_filtered;
+ repopagestore_compress_page;
+ selection_add;
+ selection_filter;
+ selection_make;
+ selection_make_matchdeps;
+ selection_solvables;
+ solv_bin2hex;
+ solv_calloc;
+ solv_chksum_add;
+ solv_chksum_cmp;
+ solv_chksum_create;
+ solv_chksum_create_clone;
+ solv_chksum_create_from_bin;
+ solv_chksum_free;
+ solv_chksum_get;
+ solv_chksum_get_type;
+ solv_chksum_isfinished;
+ solv_chksum_len;
+ solv_chksum_str2type;
+ solv_chksum_type2str;
+ solv_depmarker;
+ solv_dupappend;
+ solv_dupjoin;
+ solv_extend_realloc;
+ solv_free;
+ solv_hex2bin;
+ solv_latin1toutf8;
+ solv_malloc;
+ solv_malloc2;
+ solv_oom;
+ solv_realloc;
+ solv_realloc2;
+ solv_replacebadutf8;
+ solv_sort;
+ solv_strdup;
+ solv_timems;
+ solv_validutf8;
+ solv_vercmp;
+ solv_vercmp_deb;
+ solv_vercmp_haiku;
+ solv_vercmp_rpm;
+ solv_vercmp_rpm_notilde;
+ solv_version;
+ solv_version_major;
+ solv_version_minor;
+ solv_version_patch;
+ solvable_add_deparray;
+ solvable_add_idarray;
+ solvable_add_poolstr_array;
+ solvable_get_location;
+ solvable_identical;
+ solvable_lookup_bin_checksum;
+ solvable_lookup_bool;
+ solvable_lookup_checksum;
+ solvable_lookup_deparray;
+ solvable_lookup_id;
+ solvable_lookup_idarray;
+ solvable_lookup_location;
+ solvable_lookup_num;
+ solvable_lookup_sizek;
+ solvable_lookup_sourcepkg;
+ solvable_lookup_str;
+ solvable_lookup_str_lang;
+ solvable_lookup_str_poollang;
+ solvable_lookup_type;
+ solvable_lookup_void;
+ solvable_selfprovidedep;
+ solvable_set_deparray;
+ solvable_set_id;
+ solvable_set_idarray;
+ solvable_set_num;
+ solvable_set_poolstr;
+ solvable_set_str;
+ solvable_trivial_installable_map;
+ solvable_trivial_installable_queue;
+ solvable_trivial_installable_repo;
+ solvable_unset;
+ solver_allruleinfos;
+ solver_alternative2str;
+ solver_alternatives_count;
+ solver_calc_duchanges;
+ solver_calc_installsizechange;
+ solver_calculate_multiversionmap;
+ solver_calculate_noobsmap;
+ solver_create;
+ solver_create_state_maps;
+ solver_create_transaction;
+ solver_describe_decision;
+ solver_describe_weakdep_decision;
+ solver_disableproblem;
+ solver_enableproblem;
+ solver_findallproblemrules;
+ solver_findproblemrule;
+ solver_free;
+ solver_freedupmaps;
+ solver_get_alternative;
+ solver_get_decisionblock;
+ solver_get_decisionlevel;
+ solver_get_decisionqueue;
+ solver_get_flag;
+ solver_get_lastdecisionblocklevel;
+ solver_get_orphaned;
+ solver_get_recommendations;
+ solver_get_unneeded;
+ solver_get_userinstalled;
+ solver_next_problem;
+ solver_next_solution;
+ solver_next_solutionelement;
+ solver_prepare_solutions;
+ solver_printallsolutions;
+ solver_printcompleteprobleminfo;
+ solver_printdecisionq;
+ solver_printdecisions;
+ solver_printproblem;
+ solver_printprobleminfo;
+ solver_printproblemruleinfo;
+ solver_printrule;
+ solver_printruleclass;
+ solver_printruleelement;
+ solver_printsolution;
+ solver_printtrivial;
+ solver_printwatches;
+ solver_problem2str;
+ solver_problem_count;
+ solver_problemruleinfo2str;
+ solver_rule2job;
+ solver_rule2jobidx;
+ solver_rule2pkgrule;
+ solver_rule2rules;
+ solver_rule2solvable;
+ solver_ruleclass;
+ solver_ruleinfo;
+ solver_ruleliterals;
+ solver_rulecmp;
+ solver_select2str;
+ solver_set_flag;
+ solver_solution_count;
+ solver_solutionelement2str;
+ solver_solutionelement_count;
+ solver_solutionelement_internalid;
+ solver_solutionelement_extrajobflags;
+ solver_solve;
+ solver_take_solution;
+ solver_take_solutionelement;
+ solver_trivial_installable;
+ solver_unifyrules;
+ stringpool_clone;
+ stringpool_free;
+ stringpool_freehash;
+ stringpool_init;
+ stringpool_init_empty;
+ stringpool_shrink;
+ stringpool_str2id;
+ stringpool_strn2id;
+ transaction_add_obsoleted;
+ transaction_all_obs_pkgs;
+ transaction_calc_duchanges;
+ transaction_calc_installsizechange;
+ transaction_check_order;
+ transaction_classify;
+ transaction_classify_pkgs;
+ transaction_create;
+ transaction_create_clone;
+ transaction_create_decisionq;
+ transaction_free;
+ transaction_free_orderdata;
+ transaction_installedresult;
+ transaction_obs_pkg;
+ transaction_order;
+ transaction_order_add_choices;
+ transaction_order_get_cycle;
+ transaction_order_get_cycleids;
+ transaction_print;
+ transaction_type;
+ local:
+ *;
+};
--- /dev/null
+/*
+ * Copyright (c) 2013, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * linkedpkg.c
+ *
+ * Linked packages are "pseudo" packages that are bound to real packages but
+ * contain different information (name/summary/description). They are normally
+ * somehow generated from the real packages, either when the repositories are
+ * created or automatically from the packages by looking at the provides.
+ *
+ * We currently support:
+ *
+ * application:
+ * created from AppStream appdata xml in the repository (which is generated
+ * from files in /usr/share/appdata)
+ *
+ * product:
+ * created from product data in the repository (which is generated from files
+ * in /etc/products.d). In the future we may switch to using product()
+ * provides of packages.
+ *
+ * pattern:
+ * created from pattern() provides of packages.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "evr.h"
+#include "linkedpkg.h"
+
+#ifdef ENABLE_LINKED_PKGS
+
+void
+find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
+{
+ Id req = 0;
+ Id prv = 0;
+ Id p, pp;
+ Id pkgname = 0, appdataid = 0;
+
+ /* find appdata requires */
+ if (s->requires)
+ {
+ Id *reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0) /* go through all requires */
+ {
+ if (ISRELDEP(req))
+ continue;
+ if (!strncmp("appdata(", pool_id2str(pool, req), 8))
+ appdataid = req;
+ else
+ pkgname = req;
+ }
+ }
+ req = appdataid ? appdataid : pkgname;
+ if (!req)
+ return;
+ /* find application-appdata provides */
+ if (s->provides)
+ {
+ Id *prvp = s->repo->idarraydata + s->provides;
+ const char *reqs = pool_id2str(pool, req);
+ const char *prvs;
+ while ((prv = *prvp++) != 0) /* go through all provides */
+ {
+ if (ISRELDEP(prv))
+ continue;
+ prvs = pool_id2str(pool, prv);
+ if (strncmp("application-appdata(", prvs, 20))
+ continue;
+ if (appdataid)
+ {
+ if (!strcmp(prvs + 12, reqs))
+ break;
+ }
+ else
+ {
+ int reqsl = strlen(reqs);
+ if (!strncmp(prvs + 20, reqs, reqsl) && !strcmp(prvs + 20 + reqsl, ")"))
+ break;
+ }
+ }
+ }
+ if (!prv)
+ return; /* huh, no provides found? */
+ /* now link em */
+ FOR_PROVIDES(p, pp, req)
+ if (pool->solvables[p].repo == s->repo)
+ if (!pkgname || pool->solvables[p].name == pkgname)
+ queue_push(qr, p);
+ if (!qr->count && pkgname && appdataid)
+ {
+ /* huh, no matching package? try without pkgname filter */
+ FOR_PROVIDES(p, pp, req)
+ if (pool->solvables[p].repo == s->repo)
+ queue_push(qr, p);
+ }
+ if (qp)
+ {
+ FOR_PROVIDES(p, pp, prv)
+ if (pool->solvables[p].repo == s->repo)
+ queue_push(qp, p);
+ }
+ if (reqidp)
+ *reqidp = req;
+ if (prvidp)
+ *prvidp = prv;
+}
+
+void
+find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
+{
+ Id p, pp, namerelid;
+ char *str;
+ unsigned int sbt = 0;
+
+ /* search for project requires */
+ namerelid = 0;
+ if (s->requires)
+ {
+ Id req, *reqp = s->repo->idarraydata + s->requires;
+ const char *nn = pool_id2str(pool, s->name);
+ int nnl = strlen(nn);
+ while ((req = *reqp++) != 0) /* go through all requires */
+ if (ISRELDEP(req))
+ {
+ const char *rn;
+ Reldep *rd = GETRELDEP(pool, req);
+ if (rd->flags != REL_EQ || rd->evr != s->evr)
+ continue;
+ rn = pool_id2str(pool, rd->name);
+ if (!strncmp(rn, "product(", 8) && !strncmp(rn + 8, nn + 8, nnl - 8) && !strcmp( rn + nnl, ")"))
+ {
+ namerelid = req;
+ break;
+ }
+ }
+ }
+ if (!namerelid)
+ {
+ /* too bad. construct from scratch */
+ str = pool_tmpjoin(pool, pool_id2str(pool, s->name), ")", 0);
+ str[7] = '(';
+ namerelid = pool_rel2id(pool, pool_str2id(pool, str, 1), s->evr, REL_EQ, 1);
+ }
+ FOR_PROVIDES(p, pp, namerelid)
+ {
+ Solvable *ps = pool->solvables + p;
+ if (ps->repo != s->repo || ps->arch != s->arch)
+ continue;
+ queue_push(qr, p);
+ }
+ if (qr->count > 1)
+ {
+ /* multiple providers. try buildtime filter */
+ sbt = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
+ if (sbt)
+ {
+ unsigned int bt;
+ int i, j;
+ int filterqp = 1;
+ for (i = j = 0; i < qr->count; i++)
+ {
+ bt = solvable_lookup_num(pool->solvables + qr->elements[i], SOLVABLE_BUILDTIME, 0);
+ if (!bt)
+ filterqp = 0; /* can't filter */
+ if (!bt || bt == sbt)
+ qr->elements[j++] = qr->elements[i];
+ }
+ if (j)
+ qr->count = j;
+ if (!j || !filterqp)
+ sbt = 0; /* filter failed */
+ }
+ }
+ if (!qr->count && s->repo == pool->installed)
+ {
+ /* oh no! Look up reference file */
+ Dataiterator di;
+ const char *refbasename = solvable_lookup_str(s, PRODUCT_REFERENCEFILE);
+ dataiterator_init(&di, pool, s->repo, 0, SOLVABLE_FILELIST, refbasename, SEARCH_STRING);
+ while (dataiterator_step(&di))
+ queue_push(qr, di.solvid);
+ dataiterator_free(&di);
+ if (qp)
+ {
+ dataiterator_init(&di, pool, s->repo, 0, PRODUCT_REFERENCEFILE, refbasename, SEARCH_STRING);
+ while (dataiterator_step(&di))
+ queue_push(qp, di.solvid);
+ dataiterator_free(&di);
+ }
+ }
+ else if (qp)
+ {
+ /* find qp */
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ Solvable *ps = pool->solvables + p;
+ if (s->name != ps->name || ps->repo != s->repo || ps->arch != s->arch || s->evr != ps->evr)
+ continue;
+ if (sbt && solvable_lookup_num(ps, SOLVABLE_BUILDTIME, 0) != sbt)
+ continue;
+ queue_push(qp, p);
+ }
+ }
+ if (reqidp)
+ *reqidp = namerelid;
+ if (prvidp)
+ *prvidp = solvable_selfprovidedep(s);
+}
+
+void
+find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
+{
+ Id p, pp, *pr, apevr = 0, aprel = 0;
+
+ /* check if autopattern */
+ if (!s->provides)
+ return;
+ for (pr = s->repo->idarraydata + s->provides; (p = *pr++) != 0; )
+ if (ISRELDEP(p))
+ {
+ Reldep *rd = GETRELDEP(pool, p);
+ if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
+ {
+ aprel = p;
+ apevr = rd->evr;
+ break;
+ }
+ }
+ if (!apevr)
+ return;
+ FOR_PROVIDES(p, pp, apevr)
+ {
+ Solvable *s2 = pool->solvables + p;
+ if (s2->repo == s->repo && s2->name == apevr && s2->evr == s->evr && s2->vendor == s->vendor)
+ queue_push(qr, p);
+ }
+ if (qp)
+ {
+ FOR_PROVIDES(p, pp, aprel)
+ {
+ Solvable *s2 = pool->solvables + p;
+ if (s2->repo == s->repo && s2->evr == s->evr && s2->vendor == s->vendor)
+ queue_push(qp, p);
+ }
+ }
+ if (reqidp)
+ *reqidp = apevr;
+ if (prvidp)
+ *prvidp = aprel;
+}
+
+/* the following two functions are used in solvable_lookup_str_base to do
+ * translated lookups on the product/pattern packages
+ */
+Id
+find_autopattern_name(Pool *pool, Solvable *s)
+{
+ Id prv, *prvp;
+ if (!s->provides)
+ return 0;
+ for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
+ if (ISRELDEP(prv))
+ {
+ Reldep *rd = GETRELDEP(pool, prv);
+ if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
+ return strncmp(pool_id2str(pool, rd->evr), "pattern:", 8) != 0 ? rd->evr : 0;
+ }
+ return 0;
+}
+
+Id
+find_autoproduct_name(Pool *pool, Solvable *s)
+{
+ Id prv, *prvp;
+ if (!s->provides)
+ return 0;
+ for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
+ if (ISRELDEP(prv))
+ {
+ Reldep *rd = GETRELDEP(pool, prv);
+ if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autoproduct()"))
+ return strncmp(pool_id2str(pool, rd->evr), "product:", 8) != 0 ? rd->evr : 0;
+ }
+ return 0;
+}
+
+void
+find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
+{
+ const char *name = pool_id2str(pool, s->name);
+ if (name[0] == 'a' && !strncmp("application:", name, 12))
+ find_application_link(pool, s, reqidp, qr, prvidp, qp);
+ else if (name[0] == 'p' && !strncmp("pattern:", name, 7))
+ find_pattern_link(pool, s, reqidp, qr, prvidp, qp);
+ else if (name[0] == 'p' && !strncmp("product:", name, 8))
+ find_product_link(pool, s, reqidp, qr, prvidp, qp);
+}
+
+static int
+name_min_max(Pool *pool, Solvable *s, Id *namep, Id *minp, Id *maxp)
+{
+ Queue q;
+ Id qbuf[4];
+ Id name, min, max;
+ int i;
+
+ queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
+ find_package_link(pool, s, 0, &q, 0, 0);
+ if (!q.count)
+ {
+ queue_free(&q);
+ return 0;
+ }
+ s = pool->solvables + q.elements[0];
+ name = s->name;
+ min = max = s->evr;
+ for (i = 1; i < q.count; i++)
+ {
+ s = pool->solvables + q.elements[i];
+ if (s->name != name)
+ {
+ queue_free(&q);
+ return 0;
+ }
+ if (s->evr == min || s->evr == max)
+ continue;
+ if (pool_evrcmp(pool, min, s->evr, EVRCMP_COMPARE) >= 0)
+ min = s->evr;
+ else if (min == max || pool_evrcmp(pool, max, s->evr, EVRCMP_COMPARE) <= 0)
+ max = s->evr;
+ }
+ queue_free(&q);
+ *namep = name;
+ *minp = min;
+ *maxp = max;
+ return 1;
+}
+
+int
+pool_link_evrcmp(Pool *pool, Solvable *s1, Solvable *s2)
+{
+ Id name1, evrmin1, evrmax1;
+ Id name2, evrmin2, evrmax2;
+
+ if (s1->name != s2->name)
+ return 0; /* can't compare */
+ if (!name_min_max(pool, s1, &name1, &evrmin1, &evrmax1))
+ return 0;
+ if (!name_min_max(pool, s2, &name2, &evrmin2, &evrmax2))
+ return 0;
+ /* compare linked names */
+ if (name1 != name2)
+ return 0;
+ if (evrmin1 == evrmin2 && evrmax1 == evrmax2)
+ return 0;
+ /* now compare evr intervals */
+ if (evrmin1 == evrmax1 && evrmin2 == evrmax2)
+ return pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE);
+ if (evrmin1 != evrmax2 && pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE) > 0)
+ return 1;
+ if (evrmax1 != evrmin2 && pool_evrcmp(pool, evrmax1, evrmin2, EVRCMP_COMPARE) < 0)
+ return -1;
+ return 0;
+}
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2013, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * linkedpkg.h (internal)
+ */
+
+#ifndef LIBSOLV_LINKEDPKG_H
+#define LIBSOLV_LINKEDPKG_H
+
+static inline int
+has_package_link(Pool *pool, Solvable *s)
+{
+ const char *name = pool_id2str(pool, s->name);
+ if (name[0] == 'a' && !strncmp("application:", name, 12))
+ return 1;
+ if (name[0] == 'p' && !strncmp("pattern:", name, 7))
+ return 1;
+ if (name[0] == 'p' && !strncmp("product:", name, 8))
+ return 1;
+ return 0;
+}
+
+extern void find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
+extern void find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
+extern void find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
+
+extern Id find_autopattern_name(Pool *pool, Solvable *s);
+extern Id find_autoproduct_name(Pool *pool, Solvable *s);
+
+/* generic */
+extern void find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
+extern int pool_link_evrcmp(Pool *pool, Solvable *s1, Solvable *s2);
+
+#endif
--- /dev/null
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security,
+ * Inc. MD5 Message-Digest Algorithm.
+ *
+ * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
+ * the public domain.
+ *
+ * This differs from Colin Plumb's older public domain implementation in
+ * that no 32-bit integer data type is required, there's no compile-time
+ * endianness configuration, and the function prototypes match OpenSSL's.
+ * The primary goals are portability and ease of use.
+ *
+ * This implementation is meant to be fast, but not as fast as possible.
+ * Some known optimizations are not included to reduce source code size
+ * and avoid compile-time configuration.
+ */
+
+#include <string.h>
+#include "md5.h"
+
+
+/*
+ * The basic MD5 functions.
+ *
+ * F is optimized compared to its RFC 1321 definition just like in Colin
+ * Plumb's implementation.
+ */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures which tolerate unaligned
+ * memory accesses is just an optimization. Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__vax__)
+#define SET(n) \
+ (*(MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+#else
+#define SET(n) \
+ (ctx->block[(n)] = \
+ (MD5_u32plus)ptr[(n) * 4] | \
+ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+ (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters. There're no alignment requirements.
+ */
+static void *body(MD5_CTX *ctx, void *data, unsigned long size)
+{
+ unsigned char *ptr;
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ ptr = data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+/* Round 1 */
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+void solv_MD5_Init(MD5_CTX *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+void solv_MD5_Update(MD5_CTX *ctx, void *data, unsigned long size)
+{
+ MD5_u32plus saved_lo;
+ unsigned long used, free;
+
+ saved_lo = ctx->lo;
+ if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+ ctx->hi++;
+ ctx->hi += size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if (used) {
+ free = 64 - used;
+
+ if (size < free) {
+ memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ memcpy(&ctx->buffer[used], data, free);
+ data = (unsigned char *)data + free;
+ size -= free;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = body(ctx, data, size & ~(unsigned long)0x3f);
+ size &= 0x3f;
+ }
+
+ memcpy(ctx->buffer, data, size);
+}
+
+void solv_MD5_Final(unsigned char *result, MD5_CTX *ctx)
+{
+ unsigned long used, free;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ free = 64 - used;
+
+ if (free < 8) {
+ memset(&ctx->buffer[used], 0, free);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ free = 64;
+ }
+
+ memset(&ctx->buffer[used], 0, free - 8);
+
+ ctx->lo <<= 3;
+ ctx->buffer[56] = ctx->lo;
+ ctx->buffer[57] = ctx->lo >> 8;
+ ctx->buffer[58] = ctx->lo >> 16;
+ ctx->buffer[59] = ctx->lo >> 24;
+ ctx->buffer[60] = ctx->hi;
+ ctx->buffer[61] = ctx->hi >> 8;
+ ctx->buffer[62] = ctx->hi >> 16;
+ ctx->buffer[63] = ctx->hi >> 24;
+
+ body(ctx, ctx->buffer, 64);
+
+ result[0] = ctx->a;
+ result[1] = ctx->a >> 8;
+ result[2] = ctx->a >> 16;
+ result[3] = ctx->a >> 24;
+ result[4] = ctx->b;
+ result[5] = ctx->b >> 8;
+ result[6] = ctx->b >> 16;
+ result[7] = ctx->b >> 24;
+ result[8] = ctx->c;
+ result[9] = ctx->c >> 8;
+ result[10] = ctx->c >> 16;
+ result[11] = ctx->c >> 24;
+ result[12] = ctx->d;
+ result[13] = ctx->d >> 8;
+ result[14] = ctx->d >> 16;
+ result[15] = ctx->d >> 24;
+
+ memset(ctx, 0, sizeof(*ctx));
+}
--- /dev/null
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security,
+ * Inc. MD5 Message-Digest Algorithm.
+ *
+ * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
+ * the public domain. See md5.c for more information.
+ */
+
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef unsigned long MD5_u32plus;
+
+typedef struct {
+ MD5_u32plus lo, hi;
+ MD5_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+} MD5_CTX;
+
+extern void solv_MD5_Init(MD5_CTX *ctx);
+extern void solv_MD5_Update(MD5_CTX *ctx, void *data, unsigned long size);
+extern void solv_MD5_Final(unsigned char *result, MD5_CTX *ctx);
--- /dev/null
+/*
+ * Copyright (c) 2007-2015, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * order.c
+ *
+ * Transaction ordering
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "transaction.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+
+struct _TransactionElement {
+ Id p; /* solvable id */
+ Id edges; /* pointer into edges data */
+ Id mark;
+};
+
+struct _TransactionOrderdata {
+ struct _TransactionElement *tes;
+ int ntes;
+ Id *invedgedata;
+ int ninvedgedata;
+ Queue *cycles;
+};
+
+#define TYPE_BROKEN (1<<0)
+#define TYPE_CON (1<<1)
+
+#define TYPE_REQ_P (1<<2)
+#define TYPE_PREREQ_P (1<<3)
+
+#define TYPE_REQ (1<<4)
+#define TYPE_PREREQ (1<<5)
+
+#define TYPE_CYCLETAIL (1<<16)
+#define TYPE_CYCLEHEAD (1<<17)
+
+#define EDGEDATA_BLOCK 127
+
+void
+transaction_clone_orderdata(Transaction *trans, Transaction *srctrans)
+{
+ struct _TransactionOrderdata *od = srctrans->orderdata;
+ if (!od)
+ return;
+ trans->orderdata = solv_calloc(1, sizeof(*trans->orderdata));
+ trans->orderdata->tes = solv_memdup2(od->tes, od->ntes, sizeof(*od->tes));
+ trans->orderdata->ntes = od->ntes;
+ trans->orderdata->invedgedata = solv_memdup2(od->invedgedata, od->ninvedgedata, sizeof(Id));
+ trans->orderdata->ninvedgedata = od->ninvedgedata;
+ if (od->cycles)
+ {
+ trans->orderdata->cycles = solv_calloc(1, sizeof(Queue));
+ queue_init_clone(trans->orderdata->cycles, od->cycles);
+ }
+}
+
+void
+transaction_free_orderdata(Transaction *trans)
+{
+ if (trans->orderdata)
+ {
+ struct _TransactionOrderdata *od = trans->orderdata;
+ od->tes = solv_free(od->tes);
+ od->invedgedata = solv_free(od->invedgedata);
+ if (od->cycles)
+ {
+ queue_free(od->cycles);
+ od->cycles = solv_free(od->cycles);
+ }
+ trans->orderdata = solv_free(trans->orderdata);
+ }
+}
+
+struct orderdata {
+ Transaction *trans;
+ struct _TransactionElement *tes;
+ int ntes;
+ Id *edgedata;
+ int nedgedata;
+ Id *invedgedata;
+
+ Queue cycles;
+ Queue cyclesdata;
+ int ncycles;
+};
+
+static int
+addteedge(struct orderdata *od, int from, int to, int type)
+{
+ int i;
+ struct _TransactionElement *te;
+
+ if (from == to)
+ return 0;
+
+ /* printf("edge %d(%s) -> %d(%s) type %x\n", from, pool_solvid2str(pool, od->tes[from].p), to, pool_solvid2str(pool, od->tes[to].p), type); */
+
+ te = od->tes + from;
+ for (i = te->edges; od->edgedata[i]; i += 2)
+ if (od->edgedata[i] == to)
+ break;
+ /* test of brokenness */
+ if (type == TYPE_BROKEN)
+ return od->edgedata[i] && (od->edgedata[i + 1] & TYPE_BROKEN) != 0 ? 1 : 0;
+ if (od->edgedata[i])
+ {
+ od->edgedata[i + 1] |= type;
+ return 0;
+ }
+ if (i + 1 == od->nedgedata)
+ {
+ /* printf("tail add %d\n", i - te->edges); */
+ if (!i)
+ te->edges = ++i;
+ od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3, sizeof(Id), EDGEDATA_BLOCK);
+ }
+ else
+ {
+ /* printf("extend %d\n", i - te->edges); */
+ od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3 + (i - te->edges), sizeof(Id), EDGEDATA_BLOCK);
+ if (i > te->edges)
+ memcpy(od->edgedata + od->nedgedata, od->edgedata + te->edges, sizeof(Id) * (i - te->edges));
+ i = od->nedgedata + (i - te->edges);
+ te->edges = od->nedgedata;
+ }
+ od->edgedata[i] = to;
+ od->edgedata[i + 1] = type;
+ od->edgedata[i + 2] = 0; /* end marker */
+ od->nedgedata = i + 3;
+ return 0;
+}
+
+static int
+addedge(struct orderdata *od, Id from, Id to, int type)
+{
+ Transaction *trans = od->trans;
+ Pool *pool = trans->pool;
+ Solvable *s;
+ struct _TransactionElement *te;
+ int i;
+
+ /* printf("addedge %d %d type %d\n", from, to, type); */
+ s = pool->solvables + from;
+ if (s->repo == pool->installed && trans->transaction_installed[from - pool->installed->start])
+ {
+ /* obsolete, map to install */
+ if (trans->transaction_installed[from - pool->installed->start] > 0)
+ from = trans->transaction_installed[from - pool->installed->start];
+ else
+ {
+ int ret = 0;
+ Queue ti;
+ Id tibuf[5];
+
+ queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
+ transaction_all_obs_pkgs(trans, from, &ti);
+ for (i = 0; i < ti.count; i++)
+ ret |= addedge(od, ti.elements[i], to, type);
+ queue_free(&ti);
+ return ret;
+ }
+ }
+ s = pool->solvables + to;
+ if (s->repo == pool->installed && trans->transaction_installed[to - pool->installed->start])
+ {
+ /* obsolete, map to install */
+ if (trans->transaction_installed[to - pool->installed->start] > 0)
+ to = trans->transaction_installed[to - pool->installed->start];
+ else
+ {
+ int ret = 0;
+ Queue ti;
+ Id tibuf[5];
+
+ queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
+ transaction_all_obs_pkgs(trans, to, &ti);
+ for (i = 0; i < ti.count; i++)
+ ret |= addedge(od, from, ti.elements[i], type);
+ queue_free(&ti);
+ return ret;
+ }
+ }
+
+ /* map from/to to te numbers */
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ if (te->p == to)
+ break;
+ if (i == od->ntes)
+ return 0;
+ to = i;
+
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ if (te->p == from)
+ break;
+ if (i == od->ntes)
+ return 0;
+
+ return addteedge(od, i, to, type);
+}
+
+static inline int
+havescripts(Pool *pool, Id solvid)
+{
+ Solvable *s = pool->solvables + solvid;
+ const char *dep;
+ if (s->requires)
+ {
+ Id req, *reqp;
+ int inpre = 0;
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ {
+ inpre = 1;
+ continue;
+ }
+ if (!inpre)
+ continue;
+ dep = pool_id2str(pool, req);
+ if (*dep == '/' && strcmp(dep, "/sbin/ldconfig") != 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+addsolvableedges(struct orderdata *od, Solvable *s)
+{
+ Transaction *trans = od->trans;
+ Pool *pool = trans->pool;
+ Id req, *reqp, con, *conp;
+ Id p, p2, pp2;
+ int i, j, pre, numins;
+ Repo *installed = pool->installed;
+ Solvable *s2;
+ Queue reqq;
+ int provbyinst;
+
+#if 0
+ printf("addsolvableedges %s\n", pool_solvable2str(pool, s));
+#endif
+ p = s - pool->solvables;
+ queue_init(&reqq);
+ if (s->requires)
+ {
+ reqp = s->repo->idarraydata + s->requires;
+ pre = TYPE_REQ;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ {
+ pre = TYPE_PREREQ;
+ continue;
+ }
+#if 0
+ if (pre != TYPE_PREREQ && installed && s->repo == installed)
+ {
+ /* ignore normal requires if we're getting obsoleted */
+ if (trans->transaction_installed[p - pool->installed->start])
+ continue;
+ }
+#endif
+ queue_empty(&reqq);
+ numins = 0; /* number of packages to be installed providing it */
+ provbyinst = 0; /* provided by kept package */
+ FOR_PROVIDES(p2, pp2, req)
+ {
+ s2 = pool->solvables + p2;
+ if (p2 == p)
+ {
+ reqq.count = 0; /* self provides */
+ break;
+ }
+ if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
+ {
+ provbyinst = 1;
+#if 0
+ printf("IGNORE inst provides %s by %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s2));
+ reqq.count = 0; /* provided by package that stays installed */
+ break;
+#else
+ continue;
+#endif
+ }
+ if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
+ continue; /* package stays uninstalled */
+
+ if (s->repo == installed)
+ {
+ /* s gets uninstalled */
+ queue_pushunique(&reqq, p2);
+ if (s2->repo != installed)
+ numins++;
+ }
+ else
+ {
+ if (s2->repo == installed)
+ continue; /* s2 gets uninstalled */
+ queue_pushunique(&reqq, p2);
+ }
+ }
+ if (provbyinst)
+ {
+ /* prune to harmless ->inst edges */
+ for (i = j = 0; i < reqq.count; i++)
+ if (pool->solvables[reqq.elements[i]].repo != installed)
+ reqq.elements[j++] = reqq.elements[i];
+ reqq.count = j;
+ }
+
+ if (numins && reqq.count)
+ {
+ if (s->repo == installed)
+ {
+ for (i = 0; i < reqq.count; i++)
+ {
+ if (pool->solvables[reqq.elements[i]].repo == installed)
+ {
+ for (j = 0; j < reqq.count; j++)
+ {
+ if (pool->solvables[reqq.elements[j]].repo != installed)
+ {
+ if (trans->transaction_installed[reqq.elements[i] - pool->installed->start] == reqq.elements[j])
+ continue; /* no self edge */
+#if 0
+ printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, reqq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, reqq.elements[j]));
+#endif
+ addedge(od, reqq.elements[i], reqq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+ }
+ }
+ }
+ }
+ }
+ /* no mixed types, remove all deps on uninstalls */
+ for (i = j = 0; i < reqq.count; i++)
+ if (pool->solvables[reqq.elements[i]].repo != installed)
+ reqq.elements[j++] = reqq.elements[i];
+ reqq.count = j;
+ }
+ if (!reqq.count)
+ continue;
+ for (i = 0; i < reqq.count; i++)
+ {
+ p2 = reqq.elements[i];
+ if (pool->solvables[p2].repo != installed)
+ {
+ /* all elements of reqq are installs, thus have different TEs */
+ if (pool->solvables[p].repo != installed)
+ {
+#if 0
+ printf("add inst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+ addedge(od, p, p2, pre);
+ }
+ else
+ {
+#if 0
+ printf("add uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+ addedge(od, p, p2, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+ }
+ }
+ else
+ {
+ if (s->repo != installed)
+ continue; /* no inst->uninst edges, please! */
+
+ /* uninst -> uninst edge. Those make trouble. Only add if we must */
+ if (trans->transaction_installed[p - installed->start] && !havescripts(pool, p))
+ {
+ /* p is obsoleted by another package and has no scripts */
+ /* we assume that the obsoletor is good enough to replace p */
+ continue;
+ }
+#if 0
+ printf("add uninst->uninst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+ addedge(od, p2, p, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+ }
+ }
+ }
+ }
+ if (s->conflicts)
+ {
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ FOR_PROVIDES(p2, pp2, con)
+ {
+ if (p2 == p)
+ continue;
+ s2 = pool->solvables + p2;
+ if (!s2->repo)
+ continue;
+ if (s->repo == installed)
+ {
+ if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
+ {
+ /* deinstall p before installing p2 */
+#if 0
+ printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, con), pool_solvid2str(pool, p));
+#endif
+ addedge(od, p2, p, TYPE_CON);
+ }
+ }
+ else
+ {
+ if (s2->repo == installed && MAPTST(&trans->transactsmap, p2))
+ {
+ /* deinstall p2 before installing p */
+#if 0
+ printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, con), pool_solvid2str(pool, p2));
+#endif
+ addedge(od, p, p2, TYPE_CON);
+ }
+ }
+
+ }
+ }
+ }
+ if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &reqq) && reqq.count)
+ {
+ /* we're getting deinstalled/updated. Try to do this before our
+ * triggers are hit */
+ for (i = 0; i < reqq.count; i++)
+ {
+ Id tri = reqq.elements[i];
+ FOR_PROVIDES(p2, pp2, tri)
+ {
+ if (p2 == p)
+ continue;
+ s2 = pool->solvables + p2;
+ if (!s2->repo)
+ continue;
+ if (s2->name == s->name)
+ continue; /* obsoleted anyway */
+ if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
+ {
+ /* deinstall/update p before installing p2 */
+#if 0
+ printf("add trigger uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, tri), pool_solvid2str(pool, p));
+#endif
+ addedge(od, p2, p, TYPE_CON);
+ }
+ }
+ }
+ }
+ queue_free(&reqq);
+}
+
+
+/* break an edge in a cycle */
+static void
+breakcycle(struct orderdata *od, Id *cycle)
+{
+ Pool *pool = od->trans->pool;
+ Id ddegmin, ddegmax, ddeg;
+ int k, l;
+ struct _TransactionElement *te;
+
+ l = 0;
+ ddegmin = ddegmax = 0;
+ for (k = 0; cycle[k + 1]; k += 2)
+ {
+ ddeg = od->edgedata[cycle[k + 1] + 1];
+ if (ddeg > ddegmax)
+ ddegmax = ddeg;
+ if (!k || ddeg < ddegmin)
+ {
+ l = k;
+ ddegmin = ddeg;
+ continue;
+ }
+ if (ddeg == ddegmin)
+ {
+ if (havescripts(pool, od->tes[cycle[l]].p) && !havescripts(pool, od->tes[cycle[k]].p))
+ {
+ /* prefer k, as l comes from a package with contains scriptlets */
+ l = k;
+ continue;
+ }
+ /* same edge value, check for prereq */
+ }
+ }
+
+ /* record brkoen cycle starting with the tail */
+ queue_push(&od->cycles, od->cyclesdata.count); /* offset into data */
+ queue_push(&od->cycles, k / 2); /* cycle elements */
+ queue_push(&od->cycles, od->edgedata[cycle[l + 1] + 1]); /* broken edge */
+ queue_push(&od->cycles, (ddegmax << 16) | ddegmin); /* max/min values */
+ od->ncycles++;
+ for (k = l;;)
+ {
+ k += 2;
+ if (!cycle[k + 1])
+ k = 0;
+ queue_push(&od->cyclesdata, cycle[k]);
+ if (k == l)
+ break;
+ }
+ queue_push(&od->cyclesdata, 0); /* mark end */
+
+ /* break that edge */
+ od->edgedata[cycle[l + 1] + 1] |= TYPE_BROKEN;
+
+#if 1
+ if (ddegmin < TYPE_REQ)
+ return;
+#endif
+
+ /* cycle recorded, print it */
+ if (ddegmin >= TYPE_REQ && (ddegmax & TYPE_PREREQ) != 0)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "CRITICAL ");
+ POOL_DEBUG(SOLV_DEBUG_STATS, "cycle: --> ");
+ for (k = 0; cycle[k + 1]; k += 2)
+ {
+ te = od->tes + cycle[k];
+ if ((od->edgedata[cycle[k + 1] + 1] & TYPE_BROKEN) != 0)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "%s ##%x##> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
+ else
+ POOL_DEBUG(SOLV_DEBUG_STATS, "%s --%x--> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "\n");
+}
+
+static inline void
+dump_tes(struct orderdata *od)
+{
+ Pool *pool = od->trans->pool;
+ int i, j;
+ Queue obsq;
+ struct _TransactionElement *te, *te2;
+
+ queue_init(&obsq);
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ {
+ Solvable *s = pool->solvables + te->p;
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "TE %4d: %c%s\n", i, s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+ if (s->repo != pool->installed)
+ {
+ queue_empty(&obsq);
+ transaction_all_obs_pkgs(od->trans, te->p, &obsq);
+ for (j = 0; j < obsq.count; j++)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " -%s\n", pool_solvid2str(pool, obsq.elements[j]));
+ }
+ for (j = te->edges; od->edgedata[j]; j += 2)
+ {
+ te2 = od->tes + od->edgedata[j];
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) == 0)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " --%x--> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
+ else
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " ##%x##> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
+ }
+ }
+}
+
+static void
+reachable(struct orderdata *od, Id i)
+{
+ struct _TransactionElement *te = od->tes + i;
+ int j, k;
+
+ if (te->mark != 0)
+ return;
+ te->mark = 1;
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (!od->tes[k].mark)
+ reachable(od, k);
+ if (od->tes[k].mark == 2)
+ {
+ te->mark = 2;
+ return;
+ }
+ }
+ te->mark = -1;
+}
+
+static void
+addcycleedges(struct orderdata *od, Id *cycle, Queue *todo)
+{
+#if 0
+ Transaction *trans = od->trans;
+ Pool *pool = trans->pool;
+#endif
+ struct _TransactionElement *te;
+ int i, j, k, tail;
+ int head;
+
+#if 0
+ printf("addcycleedges\n");
+ for (i = 0; (j = cycle[i]) != 0; i++)
+ printf("cycle %s\n", pool_solvid2str(pool, od->tes[j].p));
+#endif
+
+ /* first add all the tail cycle edges */
+
+ /* see what we can reach from the cycle */
+ queue_empty(todo);
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ te->mark = 0;
+ for (i = 0; (j = cycle[i]) != 0; i++)
+ {
+ od->tes[j].mark = -1;
+ queue_push(todo, j);
+ }
+ while (todo->count)
+ {
+ i = queue_pop(todo);
+ te = od->tes + i;
+ if (te->mark > 0)
+ continue;
+ te->mark = te->mark < 0 ? 2 : 1;
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (od->tes[k].mark > 0)
+ continue; /* no need to visit again */
+ queue_push(todo, k);
+ }
+ }
+ /* now all cycle TEs are marked with 2, all TEs reachable
+ * from the cycle are marked with 1 */
+ tail = cycle[0];
+ od->tes[tail].mark = 1; /* no need to add edges */
+
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ {
+ if (te->mark)
+ continue; /* reachable from cycle */
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (od->tes[k].mark != 2)
+ continue;
+ /* We found an edge to the cycle. Add an extra edge to the tail */
+ /* the TE was not reachable, so we're not creating a new cycle! */
+#if 0
+ printf("adding TO TAIL cycle edge %d->%d %s->%s!\n", i, tail, pool_solvid2str(pool, od->tes[i].p), pool_solvid2str(pool, od->tes[tail].p));
+#endif
+ j -= te->edges; /* in case we move */
+ addteedge(od, i, tail, TYPE_CYCLETAIL);
+ j += te->edges;
+ break; /* one edge is enough */
+ }
+ }
+
+ /* now add all head cycle edges */
+
+ /* reset marks */
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ te->mark = 0;
+ head = 0;
+ for (i = 0; (j = cycle[i]) != 0; i++)
+ {
+ head = j;
+ od->tes[j].mark = 2;
+ }
+ /* first the head to save some time */
+ te = od->tes + head;
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (!od->tes[k].mark)
+ reachable(od, k);
+ if (od->tes[k].mark == -1)
+ od->tes[k].mark = -2; /* no need for another edge */
+ }
+ for (i = 0; cycle[i] != 0; i++)
+ {
+ if (cycle[i] == head)
+ break;
+ te = od->tes + cycle[i];
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ /* see if we can reach a cycle TE from k */
+ if (!od->tes[k].mark)
+ reachable(od, k);
+ if (od->tes[k].mark == -1)
+ {
+#if 0
+ printf("adding FROM HEAD cycle edge %d->%d %s->%s [%s]!\n", head, k, pool_solvid2str(pool, od->tes[head].p), pool_solvid2str(pool, od->tes[k].p), pool_solvid2str(pool, od->tes[cycle[i]].p));
+#endif
+ addteedge(od, head, k, TYPE_CYCLEHEAD);
+ od->tes[k].mark = -2; /* no need to add that one again */
+ }
+ }
+ }
+}
+
+void
+transaction_order(Transaction *trans, int flags)
+{
+ Pool *pool = trans->pool;
+ Queue *tr = &trans->steps;
+ Repo *installed = pool->installed;
+ Id p;
+ Solvable *s;
+ int i, j, k, numte, numedge;
+ struct orderdata od;
+ struct _TransactionElement *te;
+ Queue todo, obsq, samerepoq, uninstq;
+ int cycstart, cycel;
+ Id *cycle;
+ int oldcount;
+ int start, now;
+ Repo *lastrepo;
+ int lastmedia;
+ Id *temedianr;
+
+ start = now = solv_timems(0);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "ordering transaction\n");
+ /* free old data if present */
+ if (trans->orderdata)
+ {
+ struct _TransactionOrderdata *od = trans->orderdata;
+ od->tes = solv_free(od->tes);
+ od->invedgedata = solv_free(od->invedgedata);
+ trans->orderdata = solv_free(trans->orderdata);
+ }
+
+ /* create a transaction element for every active component */
+ numte = 0;
+ for (i = 0; i < tr->count; i++)
+ {
+ p = tr->elements[i];
+ s = pool->solvables + p;
+ if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
+ continue;
+ numte++;
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "transaction elements: %d\n", numte);
+ if (!numte)
+ return; /* nothing to do... */
+
+ numte++; /* leave first one zero */
+ memset(&od, 0, sizeof(od));
+ od.trans = trans;
+ od.ntes = numte;
+ od.tes = solv_calloc(numte, sizeof(*od.tes));
+ od.edgedata = solv_extend(0, 0, 1, sizeof(Id), EDGEDATA_BLOCK);
+ od.edgedata[0] = 0;
+ od.nedgedata = 1;
+ queue_init(&od.cycles);
+
+ /* initialize TEs */
+ for (i = 0, te = od.tes + 1; i < tr->count; i++)
+ {
+ p = tr->elements[i];
+ s = pool->solvables + p;
+ if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
+ continue;
+ te->p = p;
+ te++;
+ }
+
+ /* create dependency graph */
+ for (i = 0; i < tr->count; i++)
+ addsolvableedges(&od, pool->solvables + tr->elements[i]);
+
+ /* count edges */
+ numedge = 0;
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ for (j = te->edges; od.edgedata[j]; j += 2)
+ numedge++;
+ POOL_DEBUG(SOLV_DEBUG_STATS, "edges: %d, edge space: %d\n", numedge, od.nedgedata / 2);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "edge creation took %d ms\n", solv_timems(now));
+
+#if 0
+ dump_tes(&od);
+#endif
+
+ now = solv_timems(0);
+ /* kill all cycles */
+ queue_init(&todo);
+ for (i = numte - 1; i > 0; i--)
+ queue_push(&todo, i);
+
+ while (todo.count)
+ {
+ i = queue_pop(&todo);
+ /* printf("- look at TE %d\n", i); */
+ if (i < 0)
+ {
+ i = -i;
+ od.tes[i].mark = 2; /* done with that one */
+ continue;
+ }
+ te = od.tes + i;
+ if (te->mark == 2)
+ continue; /* already finished before */
+ if (te->mark == 0)
+ {
+ int edgestovisit = 0;
+ /* new node, visit edges */
+ for (j = te->edges; (k = od.edgedata[j]) != 0; j += 2)
+ {
+ if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (od.tes[k].mark == 2)
+ continue; /* no need to visit again */
+ if (!edgestovisit++)
+ queue_push(&todo, -i); /* end of edges marker */
+ queue_push(&todo, k);
+ }
+ if (!edgestovisit)
+ te->mark = 2; /* no edges, done with that one */
+ else
+ te->mark = 1; /* under investigation */
+ continue;
+ }
+ /* oh no, we found a cycle */
+ /* find start of cycle node (<0) */
+ for (j = todo.count - 1; j >= 0; j--)
+ if (todo.elements[j] == -i)
+ break;
+ assert(j >= 0);
+ cycstart = j;
+ /* build te/edge chain */
+ k = cycstart;
+ for (j = k; j < todo.count; j++)
+ if (todo.elements[j] < 0)
+ todo.elements[k++] = -todo.elements[j];
+ cycel = k - cycstart;
+ assert(cycel > 1);
+ /* make room for edges, two extra element for cycle loop + terminating 0 */
+ while (todo.count < cycstart + 2 * cycel + 2)
+ queue_push(&todo, 0);
+ cycle = todo.elements + cycstart;
+ cycle[cycel] = i; /* close the loop */
+ cycle[2 * cycel + 1] = 0; /* terminator */
+ for (k = cycel; k > 0; k--)
+ {
+ cycle[k * 2] = cycle[k];
+ te = od.tes + cycle[k - 1];
+ assert(te->mark == 1);
+ te->mark = 0; /* reset investigation marker */
+ /* printf("searching for edge from %d to %d\n", cycle[k - 1], cycle[k]); */
+ for (j = te->edges; od.edgedata[j]; j += 2)
+ if (od.edgedata[j] == cycle[k])
+ break;
+ assert(od.edgedata[j]);
+ cycle[k * 2 - 1] = j;
+ }
+ /* now cycle looks like this: */
+ /* te1 edge te2 edge te3 ... teN edge te1 0 */
+ breakcycle(&od, cycle);
+ /* restart with start of cycle */
+ todo.count = cycstart + 1;
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "cycles broken: %d\n", od.ncycles);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "cycle breaking took %d ms\n", solv_timems(now));
+
+ now = solv_timems(0);
+ /* now go through all broken cycles and create cycle edges to help
+ the ordering */
+ for (i = od.cycles.count - 4; i >= 0; i -= 4)
+ {
+ if (od.cycles.elements[i + 2] >= TYPE_REQ)
+ addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
+ }
+ for (i = od.cycles.count - 4; i >= 0; i -= 4)
+ {
+ if (od.cycles.elements[i + 2] < TYPE_REQ)
+ addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "cycle edge creation took %d ms\n", solv_timems(now));
+
+#if 0
+ dump_tes(&od);
+#endif
+ /* all edges are finally set up and there are no cycles, now the easy part.
+ * Create an ordered transaction */
+ now = solv_timems(0);
+ /* first invert all edges */
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ te->mark = 1; /* term 0 */
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ {
+ for (j = te->edges; od.edgedata[j]; j += 2)
+ {
+ if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ od.tes[od.edgedata[j]].mark++;
+ }
+ }
+ j = 1;
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ {
+ te->mark += j;
+ j = te->mark;
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "invedge space: %d\n", j + 1);
+ od.invedgedata = solv_calloc(j + 1, sizeof(Id));
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ {
+ for (j = te->edges; od.edgedata[j]; j += 2)
+ {
+ if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ od.invedgedata[--od.tes[od.edgedata[j]].mark] = i;
+ }
+ }
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ te->edges = te->mark; /* edges now points into invedgedata */
+ od.edgedata = solv_free(od.edgedata);
+ od.nedgedata = j + 1;
+
+ /* now the final ordering */
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ te->mark = 0;
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ for (j = te->edges; od.invedgedata[j]; j++)
+ od.tes[od.invedgedata[j]].mark++;
+
+ queue_init(&samerepoq);
+ queue_init(&uninstq);
+ queue_empty(&todo);
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ if (te->mark == 0)
+ {
+ if (installed && pool->solvables[te->p].repo == installed)
+ queue_push(&uninstq, i);
+ else
+ queue_push(&todo, i);
+ }
+ assert(todo.count > 0 || uninstq.count > 0);
+ oldcount = tr->count;
+ queue_empty(tr);
+
+ queue_init(&obsq);
+
+ lastrepo = 0;
+ lastmedia = 0;
+ temedianr = solv_calloc(numte, sizeof(Id));
+ for (i = 1; i < numte; i++)
+ {
+ Solvable *s = pool->solvables + od.tes[i].p;
+ if (installed && s->repo == installed)
+ j = 1;
+ else
+ j = solvable_lookup_num(s, SOLVABLE_MEDIANR, 1);
+ temedianr[i] = j;
+ }
+ for (;;)
+ {
+ /* select an TE i */
+ if (uninstq.count)
+ i = queue_shift(&uninstq);
+ else if (samerepoq.count)
+ i = queue_shift(&samerepoq);
+ else if (todo.count)
+ {
+ /* find next repo/media */
+ for (j = 0; j < todo.count; j++)
+ {
+ if (!j || temedianr[todo.elements[j]] < lastmedia)
+ {
+ i = j;
+ lastmedia = temedianr[todo.elements[j]];
+ }
+ }
+ lastrepo = pool->solvables[od.tes[todo.elements[i]].p].repo;
+
+ /* move all matching TEs to samerepoq */
+ for (i = j = 0; j < todo.count; j++)
+ {
+ int k = todo.elements[j];
+ if (temedianr[k] == lastmedia && pool->solvables[od.tes[k].p].repo == lastrepo)
+ queue_push(&samerepoq, k);
+ else
+ todo.elements[i++] = k;
+ }
+ todo.count = i;
+
+ assert(samerepoq.count);
+ i = queue_shift(&samerepoq);
+ }
+ else
+ break;
+
+ te = od.tes + i;
+ queue_push(tr, te->p);
+#if 0
+printf("do %s [%d]\n", pool_solvid2str(pool, te->p), temedianr[i]);
+#endif
+ s = pool->solvables + te->p;
+ for (j = te->edges; od.invedgedata[j]; j++)
+ {
+ struct _TransactionElement *te2 = od.tes + od.invedgedata[j];
+ assert(te2->mark > 0);
+ if (--te2->mark == 0)
+ {
+ Solvable *s = pool->solvables + te2->p;
+#if 0
+printf("free %s [%d]\n", pool_solvid2str(pool, te2->p), temedianr[od.invedgedata[j]]);
+#endif
+ if (installed && s->repo == installed)
+ queue_push(&uninstq, od.invedgedata[j]);
+ else if (s->repo == lastrepo && temedianr[od.invedgedata[j]] == lastmedia)
+ queue_push(&samerepoq, od.invedgedata[j]);
+ else
+ queue_push(&todo, od.invedgedata[j]);
+ }
+ }
+ }
+ solv_free(temedianr);
+ queue_free(&todo);
+ queue_free(&samerepoq);
+ queue_free(&uninstq);
+ queue_free(&obsq);
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ assert(te->mark == 0);
+
+ /* add back obsoleted packages */
+ transaction_add_obsoleted(trans);
+ assert(tr->count == oldcount);
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "creating new transaction took %d ms\n", solv_timems(now));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "transaction ordering took %d ms\n", solv_timems(start));
+
+ if ((flags & (SOLVER_TRANSACTION_KEEP_ORDERDATA | SOLVER_TRANSACTION_KEEP_ORDERCYCLES)) != 0)
+ {
+ struct _TransactionOrderdata *tod;
+ trans->orderdata = tod = solv_calloc(1, sizeof(*trans->orderdata));
+ if ((flags & SOLVER_TRANSACTION_KEEP_ORDERCYCLES) != 0)
+ {
+ Queue *cycles = tod->cycles = solv_calloc(1, sizeof(Queue));
+ queue_init_clone(cycles, &od.cyclesdata);
+ /* map from tes to packages */
+ for (i = 0; i < cycles->count; i++)
+ if (cycles->elements[i])
+ cycles->elements[i] = od.tes[cycles->elements[i]].p;
+ queue_insertn(cycles, cycles->count, od.cycles.count, od.cycles.elements);
+ queue_push(cycles, od.cycles.count / 4);
+ }
+ if ((flags & SOLVER_TRANSACTION_KEEP_ORDERDATA) != 0)
+ {
+ tod->tes = od.tes;
+ tod->ntes = numte;
+ tod->invedgedata = od.invedgedata;
+ tod->ninvedgedata = od.nedgedata;
+ od.tes = 0;
+ od.invedgedata = 0;
+ }
+ }
+ solv_free(od.tes);
+ solv_free(od.invedgedata);
+ queue_free(&od.cycles);
+ queue_free(&od.cyclesdata);
+}
+
+
+int
+transaction_order_add_choices(Transaction *trans, Id chosen, Queue *choices)
+{
+ int i, j;
+ struct _TransactionOrderdata *od = trans->orderdata;
+ struct _TransactionElement *te;
+
+ if (!od)
+ return choices->count;
+ if (!chosen)
+ {
+ /* initialization step */
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ te->mark = 0;
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ {
+ for (j = te->edges; od->invedgedata[j]; j++)
+ od->tes[od->invedgedata[j]].mark++;
+ }
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ if (!te->mark)
+ queue_push(choices, te->p);
+ return choices->count;
+ }
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ if (te->p == chosen)
+ break;
+ if (i == od->ntes)
+ return choices->count;
+ if (te->mark > 0)
+ {
+ /* hey! out-of-order installation! */
+ te->mark = -1;
+ }
+ for (j = te->edges; od->invedgedata[j]; j++)
+ {
+ te = od->tes + od->invedgedata[j];
+ assert(te->mark > 0 || te->mark == -1);
+ if (te->mark > 0 && --te->mark == 0)
+ queue_push(choices, te->p);
+ }
+ return choices->count;
+}
+
+void
+transaction_add_obsoleted(Transaction *trans)
+{
+ Pool *pool = trans->pool;
+ Repo *installed = pool->installed;
+ Id p;
+ Solvable *s;
+ int i, j, k, max;
+ Map done;
+ Queue obsq, *steps;
+
+ if (!installed || !trans->steps.count)
+ return;
+ /* calculate upper bound */
+ max = 0;
+ FOR_REPO_SOLVABLES(installed, p, s)
+ if (MAPTST(&trans->transactsmap, p))
+ max++;
+ if (!max)
+ return;
+ /* make room */
+ steps = &trans->steps;
+ queue_insertn(steps, 0, max, 0);
+
+ /* now add em */
+ map_init(&done, installed->end - installed->start);
+ queue_init(&obsq);
+ for (j = 0, i = max; i < steps->count; i++)
+ {
+ p = trans->steps.elements[i];
+ if (pool->solvables[p].repo == installed)
+ {
+ if (!trans->transaction_installed[p - pool->installed->start])
+ trans->steps.elements[j++] = p;
+ continue;
+ }
+ trans->steps.elements[j++] = p;
+ queue_empty(&obsq);
+ transaction_all_obs_pkgs(trans, p, &obsq);
+ for (k = 0; k < obsq.count; k++)
+ {
+ p = obsq.elements[k];
+ assert(p >= installed->start && p < installed->end);
+ if (!MAPTST(&trans->transactsmap, p)) /* just in case */
+ continue;
+ if (MAPTST(&done, p - installed->start))
+ continue;
+ MAPSET(&done, p - installed->start);
+ trans->steps.elements[j++] = p;
+ }
+ }
+
+ /* free unneeded space */
+ queue_truncate(steps, j);
+ map_free(&done);
+ queue_free(&obsq);
+}
+
+static void
+transaction_check_pkg(Transaction *trans, Id tepkg, Id pkg, Map *ins, Map *seen, int onlyprereq, Id noconfpkg, int depth)
+{
+ Pool *pool = trans->pool;
+ Id p, pp;
+ Solvable *s;
+ int good;
+
+ if (MAPTST(seen, pkg))
+ return;
+ MAPSET(seen, pkg);
+ s = pool->solvables + pkg;
+#if 0
+ printf("- %*s%c%s\n", depth * 2, "", s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+#endif
+ if (s->requires)
+ {
+ Id req, *reqp;
+ int inpre = 0;
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ {
+ inpre = 1;
+ continue;
+ }
+ if (onlyprereq && !inpre)
+ continue;
+ if (!strncmp(pool_id2str(pool, req), "rpmlib(", 7))
+ continue;
+ good = 0;
+ /* first check kept packages, then freshly installed, then not yet uninstalled */
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (!MAPTST(ins, p))
+ continue;
+ if (MAPTST(&trans->transactsmap, p))
+ continue;
+ good++;
+ transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+ }
+ if (!good)
+ {
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (!MAPTST(ins, p))
+ continue;
+ if (pool->solvables[p].repo == pool->installed)
+ continue;
+ good++;
+ transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+ }
+ }
+ if (!good)
+ {
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (!MAPTST(ins, p))
+ continue;
+ good++;
+ transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+ }
+ }
+ if (!good)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %c%s: nothing provides %s needed by %c%s\n", pool->solvables[tepkg].repo == pool->installed ? '-' : '+', pool_solvid2str(pool, tepkg), pool_dep2str(pool, req), s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+ }
+ }
+ }
+}
+
+void
+transaction_check_order(Transaction *trans)
+{
+ Pool *pool = trans->pool;
+ Solvable *s;
+ Id p, lastins;
+ Map ins, seen;
+ int i;
+
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\nchecking transaction order...\n");
+ map_init(&ins, pool->nsolvables);
+ map_init(&seen, pool->nsolvables);
+ if (pool->installed)
+ FOR_REPO_SOLVABLES(pool->installed, p, s)
+ MAPSET(&ins, p);
+ lastins = 0;
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ p = trans->steps.elements[i];
+ s = pool->solvables + p;
+ if (s->repo != pool->installed)
+ lastins = p;
+ if (s->repo != pool->installed)
+ MAPSET(&ins, p);
+ if (havescripts(pool, p))
+ {
+ MAPZERO(&seen);
+ transaction_check_pkg(trans, p, p, &ins, &seen, 1, lastins, 0);
+ }
+ if (s->repo == pool->installed)
+ MAPCLR(&ins, p);
+ }
+ map_free(&seen);
+ map_free(&ins);
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "transaction order check done.\n");
+}
+
+void
+transaction_order_get_cycleids(Transaction *trans, Queue *q, int minseverity)
+{
+ struct _TransactionOrderdata *od = trans->orderdata;
+ Queue *cq;
+ int i, cid, ncycles;
+
+ queue_empty(q);
+ if (!od || !od->cycles || !od->cycles->count)
+ return;
+ cq = od->cycles;
+ ncycles = cq->elements[cq->count - 1];
+ i = cq->count - 1 - ncycles * 4;
+ for (cid = 1; cid <= ncycles; cid++, i += 4)
+ {
+ if (minseverity)
+ {
+ int cmin = cq->elements[i + 3] & 0xffff;
+ int cmax = (cq->elements[i + 3] >> 16) & 0xffff;
+ if (minseverity >= SOLVER_ORDERCYCLE_NORMAL && cmin < TYPE_REQ)
+ continue;
+ if (minseverity >= SOLVER_ORDERCYCLE_CRITICAL && (cmax & TYPE_PREREQ) == 0)
+ continue;
+ }
+ queue_push(q, cid);
+ }
+}
+
+int
+transaction_order_get_cycle(Transaction *trans, Id cid, Queue *q)
+{
+ struct _TransactionOrderdata *od = trans->orderdata;
+ Queue *cq;
+ int cmin, cmax, severity;
+ int ncycles;
+
+ queue_empty(q);
+ if (!od || !od->cycles || !od->cycles->count)
+ return SOLVER_ORDERCYCLE_HARMLESS;
+ cq = od->cycles;
+ ncycles = cq->elements[cq->count - 1];
+ if (cid < 1 || cid > ncycles)
+ return SOLVER_ORDERCYCLE_HARMLESS;
+ cid = cq->count - 1 - 4 * (ncycles - cid + 1);
+ cmin = cq->elements[cid + 3] & 0xffff;
+ cmax = (cq->elements[cid + 3] >> 16) & 0xffff;
+ if (cmin < TYPE_REQ)
+ severity = SOLVER_ORDERCYCLE_HARMLESS;
+ else if ((cmax & TYPE_PREREQ) == 0)
+ severity = SOLVER_ORDERCYCLE_NORMAL;
+ else
+ severity = SOLVER_ORDERCYCLE_CRITICAL;
+ if (q)
+ queue_insertn(q, 0, cq->elements[cid + 1], cq->elements + cq->elements[cid]);
+ return severity;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * Generic policy interface for SAT solver
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "solver.h"
+#include "solver_private.h"
+#include "evr.h"
+#include "policy.h"
+#include "poolvendor.h"
+#include "poolarch.h"
+#include "linkedpkg.h"
+#include "cplxdeps.h"
+
+
+
+/*-----------------------------------------------------------------*/
+
+/*
+ * prep for prune_best_version
+ * sort by name
+ */
+
+static int
+prune_to_best_version_sortcmp(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = dp;
+ int r;
+ Id a = *(Id *)ap;
+ Id b = *(Id *)bp;
+ Solvable *sa, *sb;
+
+ sa = pool->solvables + a;
+ sb = pool->solvables + b;
+ r = sa->name - sb->name;
+ if (r)
+ {
+ const char *na, *nb;
+ /* different names. We use real strcmp here so that the result
+ * is not depending on some random solvable order */
+ na = pool_id2str(pool, sa->name);
+ nb = pool_id2str(pool, sb->name);
+ return strcmp(na, nb);
+ }
+ if (sa->arch != sb->arch)
+ {
+ int aa, ab;
+ aa = (sa->arch <= pool->lastarch) ? pool->id2arch[sa->arch] : 0;
+ ab = (sb->arch <= pool->lastarch) ? pool->id2arch[sb->arch] : 0;
+ if (aa != ab && aa > 1 && ab > 1)
+ return aa - ab; /* lowest score first */
+ }
+
+ /* the same name, bring installed solvables to the front */
+ if (pool->installed)
+ {
+ if (sa->repo == pool->installed)
+ {
+ if (sb->repo != pool->installed)
+ return -1;
+ }
+ else if (sb->repo == pool->installed)
+ return 1;
+ }
+ /* sort by repository sub-prio (installed repo handled above) */
+ r = (sb->repo ? sb->repo->subpriority : 0) - (sa->repo ? sa->repo->subpriority : 0);
+ if (r)
+ return r;
+ /* no idea about the order, sort by id */
+ return a - b;
+}
+
+
+/*
+ * prune to repository with highest priority.
+ * does not prune installed solvables.
+ */
+
+static void
+prune_to_highest_prio(Pool *pool, Queue *plist)
+{
+ int i, j;
+ Solvable *s;
+ int bestprio = 0, bestprioset = 0;
+
+ /* prune to highest priority */
+ for (i = 0; i < plist->count; i++) /* find highest prio in queue */
+ {
+ s = pool->solvables + plist->elements[i];
+ if (pool->installed && s->repo == pool->installed)
+ continue;
+ if (!bestprioset || s->repo->priority > bestprio)
+ {
+ bestprio = s->repo->priority;
+ bestprioset = 1;
+ }
+ }
+ if (!bestprioset)
+ return;
+ for (i = j = 0; i < plist->count; i++) /* remove all with lower prio */
+ {
+ s = pool->solvables + plist->elements[i];
+ if (s->repo->priority == bestprio || (pool->installed && s->repo == pool->installed))
+ plist->elements[j++] = plist->elements[i];
+ }
+ plist->count = j;
+}
+
+
+/* installed packages involed in a dup operation can only be kept
+ * if they are identical to a non-installed one */
+static void
+solver_prune_installed_dup_packages(Solver *solv, Queue *plist)
+{
+ Pool *pool = solv->pool;
+ int i, j, bestprio = 0;
+
+ /* find bestprio (again) */
+ for (i = 0; i < plist->count; i++)
+ {
+ Solvable *s = pool->solvables + plist->elements[i];
+ if (s->repo != pool->installed)
+ {
+ bestprio = s->repo->priority;
+ break;
+ }
+ }
+ if (i == plist->count)
+ return; /* only installed packages, could not find prio */
+ for (i = j = 0; i < plist->count; i++)
+ {
+ Id p = plist->elements[i];
+ Solvable *s = pool->solvables + p;
+ if (s->repo != pool->installed && s->repo->priority < bestprio)
+ continue;
+ if (s->repo == pool->installed && (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))))
+ {
+ Id p2, pp2;
+ int keepit = 0;
+ FOR_PROVIDES(p2, pp2, s->name)
+ {
+ Solvable *s2 = pool->solvables + p2;
+ if (s2->repo == pool->installed || s2->evr != s->evr || s2->repo->priority < bestprio)
+ continue;
+ if (!solvable_identical(s, s2))
+ continue;
+ keepit = 1;
+ if (s2->repo->priority > bestprio)
+ {
+ /* new max prio! */
+ bestprio = s2->repo->priority;
+ j = 0;
+ }
+ }
+ if (!keepit)
+ continue; /* no identical package found, ignore installed package */
+ }
+ plist->elements[j++] = p;
+ }
+ if (j)
+ plist->count = j;
+}
+
+/*
+ * like prune_to_highest_prio, but calls solver prune_installed_dup_packages
+ * when there are dup packages
+ */
+static inline void
+solver_prune_to_highest_prio(Solver *solv, Queue *plist)
+{
+ prune_to_highest_prio(solv->pool, plist);
+ if (plist->count > 1 && solv->pool->installed && (solv->dupmap_all || solv->dupinvolvedmap.size))
+ solver_prune_installed_dup_packages(solv, plist);
+}
+
+
+static void
+solver_prune_to_highest_prio_per_name(Solver *solv, Queue *plist)
+{
+ Pool *pool = solv->pool;
+ Queue pq;
+ int i, j, k;
+ Id name;
+
+ queue_init(&pq);
+ solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
+ queue_push(&pq, plist->elements[0]);
+ name = pool->solvables[pq.elements[0]].name;
+ for (i = 1, j = 0; i < plist->count; i++)
+ {
+ if (pool->solvables[plist->elements[i]].name != name)
+ {
+ if (pq.count > 2)
+ solver_prune_to_highest_prio(solv, &pq);
+ for (k = 0; k < pq.count; k++)
+ plist->elements[j++] = pq.elements[k];
+ queue_empty(&pq);
+ queue_push(&pq, plist->elements[i]);
+ name = pool->solvables[pq.elements[0]].name;
+ }
+ }
+ if (pq.count > 2)
+ solver_prune_to_highest_prio(solv, &pq);
+ for (k = 0; k < pq.count; k++)
+ plist->elements[j++] = pq.elements[k];
+ queue_free(&pq);
+ plist->count = j;
+}
+
+
+#ifdef ENABLE_COMPLEX_DEPS
+
+/* simple fixed-size hash for package ids */
+#define CPLXDEPHASH_EMPTY(elements) (memset(elements, 0, sizeof(Id) * 256))
+#define CPLXDEPHASH_SET(elements, p) (elements[(p) & 255] |= (1 << ((p) >> 8 & 31)))
+#define CPLXDEPHASH_TST(elements, p) (elements[(p) & 255] && (elements[(p) & 255] & (1 << ((p) >> 8 & 31))))
+
+static void
+check_complex_dep(Solver *solv, Id dep, Map *m, Queue **cqp)
+{
+ Pool *pool = solv->pool;
+ Queue q;
+ queue_init(&q);
+ Id p;
+ int i, qcnt;
+
+#if 0
+ printf("check_complex_dep %s\n", pool_dep2str(pool, dep));
+#endif
+ i = pool_normalize_complex_dep(pool, dep, &q, CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ {
+ queue_free(&q);
+ return;
+ }
+ qcnt = q.count;
+ for (i = 0; i < qcnt; i++)
+ {
+ /* we rely on the fact that blocks are ordered here.
+ * if we reach a positive element, we know that we
+ * saw all negative ones */
+ for (; (p = q.elements[i]) < 0; i++)
+ {
+ if (solv->decisionmap[-p] < 0)
+ break;
+ if (solv->decisionmap[-p] == 0)
+ queue_push(&q, -p); /* undecided negative literal */
+ }
+ if (p <= 0)
+ {
+#if 0
+ printf("complex dep block cannot be true or no pos literals\n");
+#endif
+ while (q.elements[i])
+ i++;
+ if (qcnt != q.count)
+ queue_truncate(&q, qcnt);
+ continue;
+ }
+ if (qcnt == q.count)
+ {
+ /* all negative literals installed, add positive literals to map */
+ for (; (p = q.elements[i]) != 0; i++)
+ MAPSET(m, p);
+ }
+ else
+ {
+ /* at least one undecided negative literal, postpone */
+ int j, k;
+ Queue *cq;
+#if 0
+ printf("add new complex dep block\n");
+ for (j = qcnt; j < q.count; j++)
+ printf(" - %s\n", pool_solvid2str(pool, q.elements[j]));
+#endif
+ while (q.elements[i])
+ i++;
+ if (!(cq = *cqp))
+ {
+ cq = solv_calloc(1, sizeof(Queue));
+ queue_init(cq);
+ queue_insertn(cq, 0, 256, 0); /* allocate hash area */
+ *cqp = cq;
+ }
+ for (j = qcnt; j < q.count; j++)
+ {
+ p = q.elements[j];
+ /* check if we already have this (dep, p) entry */
+ for (k = 256; k < cq->count; k += 2)
+ if (cq->elements[k + 1] == dep && cq->elements[k] == p)
+ break;
+ if (k == cq->count)
+ {
+ /* a new one. add to cq and hash */
+ queue_push2(cq, p, dep);
+ CPLXDEPHASH_SET(cq->elements, p);
+ }
+ }
+ queue_truncate(&q, qcnt);
+ }
+ }
+ queue_free(&q);
+}
+
+static void
+recheck_complex_deps(Solver *solv, Id p, Map *m, Queue **cqp)
+{
+ Queue *cq = *cqp;
+ Id pp;
+ int i;
+#if 0
+ printf("recheck_complex_deps for package %s\n", pool_solvid2str(solv->pool, p));
+#endif
+ /* make sure that we don't have a false hit */
+ for (i = 256; i < cq->count; i += 2)
+ if (cq->elements[i] == p)
+ break;
+ if (i == cq->count)
+ return; /* false alert */
+ if (solv->decisionmap[p] <= 0)
+ return; /* just in case... */
+
+ /* rebuild the hash, call check_complex_dep for our package */
+ CPLXDEPHASH_EMPTY(cq->elements);
+ for (i = 256; i < cq->count; i += 2)
+ if ((pp = cq->elements[i]) == p)
+ {
+ Id dep = cq->elements[i + 1];
+ queue_deleten(cq, i, 2);
+ i -= 2;
+ check_complex_dep(solv, dep, m, &cq);
+ }
+ else
+ CPLXDEPHASH_SET(cq->elements, pp);
+}
+
+#endif
+
+
+void
+policy_update_recommendsmap(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Solvable *s;
+ Id p, pp, rec, *recp, sug, *sugp;
+
+ if (solv->recommends_index < 0)
+ {
+ MAPZERO(&solv->recommendsmap);
+ MAPZERO(&solv->suggestsmap);
+#ifdef ENABLE_COMPLEX_DEPS
+ if (solv->recommendscplxq)
+ {
+ queue_free(solv->recommendscplxq);
+ solv->recommendscplxq = solv_free(solv->recommendscplxq);
+ }
+ if (solv->suggestscplxq)
+ {
+ queue_free(solv->suggestscplxq);
+ solv->suggestscplxq = solv_free(solv->suggestscplxq);
+ }
+#endif
+ solv->recommends_index = 0;
+ }
+ while (solv->recommends_index < solv->decisionq.count)
+ {
+ p = solv->decisionq.elements[solv->recommends_index++];
+ if (p < 0)
+ continue;
+ s = pool->solvables + p;
+#ifdef ENABLE_COMPLEX_DEPS
+ /* re-check postponed complex blocks */
+ if (solv->recommendscplxq && CPLXDEPHASH_TST(solv->recommendscplxq->elements, p))
+ recheck_complex_deps(solv, p, &solv->recommendsmap, &solv->recommendscplxq);
+ if (solv->suggestscplxq && CPLXDEPHASH_TST(solv->suggestscplxq->elements, p))
+ recheck_complex_deps(solv, p, &solv->suggestsmap, &solv->suggestscplxq);
+#endif
+ if (s->recommends)
+ {
+ recp = s->repo->idarraydata + s->recommends;
+ while ((rec = *recp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, rec))
+ {
+ check_complex_dep(solv, rec, &solv->recommendsmap, &solv->recommendscplxq);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, rec)
+ MAPSET(&solv->recommendsmap, p);
+ }
+ }
+ if (s->suggests)
+ {
+ sugp = s->repo->idarraydata + s->suggests;
+ while ((sug = *sugp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, sug))
+ {
+ check_complex_dep(solv, sug, &solv->suggestsmap, &solv->suggestscplxq);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, sug)
+ MAPSET(&solv->suggestsmap, p);
+ }
+ }
+ }
+}
+
+/* bring suggested/enhanced packages to front
+ * installed packages count as suggested */
+static void
+prefer_suggested(Solver *solv, Queue *plist)
+{
+ Pool *pool = solv->pool;
+ int i, count;
+
+ /* update our recommendsmap/suggestsmap */
+ if (solv->recommends_index < solv->decisionq.count)
+ policy_update_recommendsmap(solv);
+
+ for (i = 0, count = plist->count; i < count; i++)
+ {
+ Id p = plist->elements[i];
+ Solvable *s = pool->solvables + p;
+ if ((pool->installed && s->repo == pool->installed) ||
+ MAPTST(&solv->suggestsmap, p) ||
+ solver_is_enhancing(solv, s))
+ continue; /* good package */
+ /* bring to back */
+ if (i < plist->count - 1)
+ {
+ memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id));
+ plist->elements[plist->count - 1] = p;
+ }
+ i--;
+ count--;
+ }
+}
+
+/*
+ * prune to recommended/suggested packages.
+ * does not prune installed packages (they are also somewhat recommended).
+ */
+static void
+prune_to_recommended(Solver *solv, Queue *plist)
+{
+ Pool *pool = solv->pool;
+ int i, j, k, ninst;
+ Solvable *s;
+ Id p;
+
+ ninst = 0;
+ if (pool->installed)
+ {
+ for (i = 0; i < plist->count; i++)
+ {
+ p = plist->elements[i];
+ s = pool->solvables + p;
+ if (pool->installed && s->repo == pool->installed)
+ ninst++;
+ }
+ }
+ if (plist->count - ninst < 2)
+ return;
+
+ /* update our recommendsmap/suggestsmap */
+ if (solv->recommends_index < solv->decisionq.count)
+ policy_update_recommendsmap(solv);
+
+ /* prune to recommended/supplemented */
+ ninst = 0;
+ for (i = j = 0; i < plist->count; i++)
+ {
+ p = plist->elements[i];
+ s = pool->solvables + p;
+ if (pool->installed && s->repo == pool->installed)
+ {
+ ninst++;
+ if (j)
+ plist->elements[j++] = p;
+ continue;
+ }
+ if (!MAPTST(&solv->recommendsmap, p))
+ if (!solver_is_supplementing(solv, s))
+ continue;
+ if (!j && ninst)
+ {
+ for (k = 0; j < ninst; k++)
+ {
+ s = pool->solvables + plist->elements[k];
+ if (pool->installed && s->repo == pool->installed)
+ plist->elements[j++] = plist->elements[k];
+ }
+ }
+ plist->elements[j++] = p;
+ }
+ if (j)
+ plist->count = j;
+
+#if 0
+ /* anything left to prune? */
+ if (plist->count - ninst < 2)
+ return;
+
+ /* prune to suggested/enhanced */
+ ninst = 0;
+ for (i = j = 0; i < plist->count; i++)
+ {
+ p = plist->elements[i];
+ s = pool->solvables + p;
+ if (pool->installed && s->repo == pool->installed)
+ {
+ ninst++;
+ if (j)
+ plist->elements[j++] = p;
+ continue;
+ }
+ if (!MAPTST(&solv->suggestsmap, p))
+ if (!solver_is_enhancing(solv, s))
+ continue;
+ if (!j && ninst)
+ {
+ for (k = 0; j < ninst; k++)
+ {
+ s = pool->solvables + plist->elements[k];
+ if (pool->installed && s->repo == pool->installed)
+ plist->elements[j++] = plist->elements[k];
+ }
+ }
+ plist->elements[j++] = p;
+ }
+ if (j)
+ plist->count = j;
+#endif
+}
+
+static void
+prune_to_best_arch(const Pool *pool, Queue *plist)
+{
+ Id a, bestscore;
+ Solvable *s;
+ int i, j;
+
+ if (!pool->id2arch || plist->count < 2)
+ return;
+ bestscore = 0;
+ for (i = 0; i < plist->count; i++)
+ {
+ s = pool->solvables + plist->elements[i];
+ a = s->arch;
+ a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+ if (a && a != 1 && (!bestscore || a < bestscore))
+ bestscore = a;
+ }
+ if (!bestscore)
+ return;
+ 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 ^ bestscore) & 0xffff0000) != 0)
+ continue;
+ plist->elements[j++] = plist->elements[i];
+ }
+ if (j)
+ plist->count = j;
+}
+
+
+struct trj_data {
+ Pool *pool;
+ Queue *plist;
+ Id *stack;
+ Id nstack;
+ Id *low;
+ Id firstidx;
+ Id idx;
+};
+
+/* This is Tarjan's SCC algorithm, slightly modified */
+static void
+trj_visit(struct trj_data *trj, Id node)
+{
+ Id *low = trj->low;
+ Pool *pool = trj->pool;
+ Queue *plist = trj->plist;
+ Id myidx, stackstart;
+ Solvable *s;
+ int i;
+ Id p, pp, obs, *obsp;
+
+ low[node] = myidx = trj->idx++;
+ trj->stack[(stackstart = trj->nstack++)] = node;
+
+ s = pool->solvables + plist->elements[node];
+ if (s->obsoletes)
+ {
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(p, pp, obs)
+ {
+ Solvable *ps = pool->solvables + p;
+ if (ps->name == s->name)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ /* hmm, expensive. should use hash if plist is big */
+ for (i = 0; i < plist->count; i++)
+ {
+ if (node != i && plist->elements[i] == p)
+ {
+ Id l = low[i];
+ if (!l)
+ {
+ if (!ps->obsoletes)
+ {
+ /* don't bother */
+ trj->idx++;
+ low[i] = -1;
+ continue;
+ }
+ trj_visit(trj, i);
+ l = low[i];
+ }
+ if (l < 0)
+ continue;
+ if (l < trj->firstidx)
+ {
+ int k;
+ /* this means we have reached an old SCC found earlier.
+ * delete it as we obsolete it */
+ for (k = l; ; k++)
+ {
+ if (low[trj->stack[k]] == l)
+ low[trj->stack[k]] = -1;
+ else
+ break;
+ }
+ }
+ else if (l < low[node])
+ low[node] = l;
+ }
+ }
+ }
+ }
+ }
+ if (low[node] == myidx) /* found a SCC? */
+ {
+ /* we're only interested in SCCs that contain the first node,
+ * as all others are "obsoleted" */
+ if (myidx != trj->firstidx)
+ myidx = -1;
+ for (i = stackstart; i < trj->nstack; i++)
+ low[trj->stack[i]] = myidx;
+ trj->nstack = stackstart; /* empty stack */
+ }
+}
+
+/*
+ * remove entries from plist that are obsoleted by other entries
+ * with different name.
+ */
+static void
+prune_obsoleted(Pool *pool, Queue *plist)
+{
+ Id data_buf[2 * 16], *data;
+ struct trj_data trj;
+ int i, j;
+ Solvable *s;
+
+ if (plist->count <= 16)
+ {
+ memset(data_buf, 0, sizeof(data_buf));
+ data = data_buf;
+ }
+ else
+ data = solv_calloc(plist->count, 2 * sizeof(Id));
+ trj.pool = pool;
+ trj.plist = plist;
+ trj.low = data;
+ trj.idx = 1;
+ trj.stack = data + plist->count - 1; /* -1 so we can index with idx (which starts with 1) */
+ for (i = 0; i < plist->count; i++)
+ {
+ if (trj.low[i])
+ continue;
+ s = pool->solvables + plist->elements[i];
+ if (s->obsoletes)
+ {
+ trj.firstidx = trj.nstack = trj.idx;
+ trj_visit(&trj, i);
+ }
+ else
+ {
+ Id myidx = trj.idx++;
+ trj.low[i] = myidx;
+ trj.stack[myidx] = i;
+ }
+ }
+ for (i = j = 0; i < plist->count; i++)
+ if (trj.low[i] >= 0)
+ plist->elements[j++] = plist->elements[i];
+ plist->count = j;
+ if (data != data_buf)
+ solv_free(data);
+}
+
+/* this is prune_obsoleted special-cased for two elements */
+static void
+prune_obsoleted_2(Pool *pool, Queue *plist)
+{
+ int i;
+ Solvable *s;
+ Id p, pp, obs, *obsp;
+ Id other;
+ int obmap = 0;
+
+ for (i = 0; i < 2; i++)
+ {
+ s = pool->solvables + plist->elements[i];
+ other = plist->elements[1 - i];
+ if (s->obsoletes)
+ {
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(p, pp, obs)
+ {
+ Solvable *ps;
+ if (p != other)
+ continue;
+ ps = pool->solvables + p;
+ if (ps->name == s->name)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ obmap |= 1 << i;
+ break;
+ }
+ if (p)
+ break;
+ }
+ }
+ }
+ if (obmap == 0 || obmap == 3)
+ return;
+ if (obmap == 2)
+ plist->elements[0] = plist->elements[1];
+ plist->count = 1;
+}
+
+/*
+ * bring those elements to the front of the queue that
+ * have a installed solvable with the same name
+ */
+static void
+move_installed_to_front(Pool *pool, Queue *plist)
+{
+ int i, j;
+ Solvable *s;
+ Id p, pp;
+
+ for (i = j = 0; i < plist->count; i++)
+ {
+ s = pool->solvables + plist->elements[i];
+ if (s->repo != pool->installed)
+ {
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ Solvable *ps = pool->solvables + p;
+ if (s->name == ps->name && ps->repo == pool->installed)
+ {
+ s = ps;
+ break;
+ }
+ }
+ }
+ if (s->repo == pool->installed)
+ {
+ if (i != j)
+ {
+ p = plist->elements[i];
+ if (i - j == 1)
+ plist->elements[i] = plist->elements[j];
+ else
+ memmove(plist->elements + j + 1, plist->elements + j, (i - j) * sizeof(Id));
+ plist->elements[j] = p;
+ }
+ else if (j + 2 == plist->count)
+ break; /* no need to check last element if all prev ones are installed */
+ j++;
+ }
+ }
+}
+
+/*
+ * prune_to_best_version
+ *
+ * sort list of packages (given through plist) by name and evr
+ * return result through plist
+ */
+void
+prune_to_best_version(Pool *pool, Queue *plist)
+{
+ int i, j, r;
+ Solvable *s, *best;
+
+ if (plist->count < 2) /* no need to prune for a single entry */
+ return;
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
+
+ /* sort by name first, prefer installed */
+ solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
+
+ /* now find best 'per name' */
+ best = 0;
+ for (i = j = 0; i < plist->count; i++)
+ {
+ s = pool->solvables + plist->elements[i];
+
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "- %s[%s]\n",
+ pool_solvable2str(pool, s),
+ (pool->installed && s->repo == pool->installed) ? "installed" : "not installed");
+
+ if (!best) /* if no best yet, the current is best */
+ {
+ best = s;
+ continue;
+ }
+
+ /* name switch: finish group, re-init */
+ if (best->name != s->name) /* new name */
+ {
+ plist->elements[j++] = best - pool->solvables; /* move old best to front */
+ best = s; /* take current as new best */
+ continue;
+ }
+ r = best->evr != s->evr ? pool_evrcmp(pool, best->evr, s->evr, EVRCMP_COMPARE) : 0;
+#ifdef ENABLE_LINKED_PKGS
+ if (r == 0 && has_package_link(pool, s))
+ r = pool_link_evrcmp(pool, best, s);
+#endif
+ if (r < 0)
+ best = s;
+ }
+ plist->elements[j++] = best - pool->solvables; /* finish last group */
+ plist->count = j;
+
+ /* we reduced the list to one package per name, now look at
+ * package obsoletes */
+ if (plist->count > 1)
+ {
+ if (plist->count == 2)
+ prune_obsoleted_2(pool, plist);
+ else
+ prune_obsoleted(pool, plist);
+ }
+ if (plist->count > 1 && pool->installed)
+ move_installed_to_front(pool, plist);
+}
+
+
+static int
+sort_by_name_evr_sortcmp(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = dp;
+ Id a, *aa = (Id *)ap;
+ Id b, *bb = (Id *)bp;
+ Id r = aa[1] - bb[1];
+ if (r)
+ return r < 0 ? -1 : 1;
+ if (aa[2] == bb[2])
+ return 0;
+ a = aa[2] < 0 ? -aa[2] : aa[2];
+ b = bb[2] < 0 ? -bb[2] : bb[2];
+ if (pool->disttype != DISTTYPE_DEB && a != b)
+ {
+ /* treat release-less versions different */
+ const char *as = pool_id2str(pool, a);
+ const char *bs = pool_id2str(pool, b);
+ if (strchr(as, '-'))
+ {
+ if (!strchr(bs, '-'))
+ return -2;
+ }
+ else
+ {
+ if (strchr(bs, '-'))
+ return 2;
+ }
+ }
+ r = pool_evrcmp(pool, b, a, EVRCMP_COMPARE);
+ if (!r && (aa[2] < 0 || bb[2] < 0))
+ {
+ if (bb[2] >= 0)
+ return 1;
+ if (aa[2] >= 0)
+ return -1;
+ }
+ if (r)
+ return r < 0 ? -1 : 1;
+ return 0;
+}
+
+/* common end of sort_by_srcversion and sort_by_common_dep */
+static void
+sort_by_name_evr_array(Pool *pool, Queue *plist, int count, int ent)
+{
+ Id lastname;
+ int i, j, bad, havebad;
+ Id *pp, *elements = plist->elements;
+
+ if (ent < 2)
+ {
+ queue_truncate(plist, count);
+ return;
+ }
+ solv_sort(elements + count * 2, ent, sizeof(Id) * 3, sort_by_name_evr_sortcmp, pool);
+ lastname = 0;
+ bad = havebad = 0;
+ for (i = 0, pp = elements + count * 2; i < ent; i++, pp += 3)
+ {
+ if (lastname && pp[1] == lastname)
+ {
+ if (pp[0] != pp[-3] && sort_by_name_evr_sortcmp(pp - 3, pp, pool) == -1)
+ {
+#if 0
+ printf("%s - %s: bad %s %s - %s\n", pool_solvid2str(pool, elements[pp[-3]]), pool_solvid2str(pool, elements[pp[0]]), pool_dep2str(pool, lastname), pool_id2str(pool, pp[-1] < 0 ? -pp[-1] : pp[-1]), pool_id2str(pool, pp[2] < 0 ? -pp[2] : pp[2]));
+#endif
+ bad++;
+ havebad = 1;
+ }
+ }
+ else
+ {
+ bad = 0;
+ lastname = pp[1];
+ }
+ elements[count + pp[0]] += bad;
+ }
+
+#if 0
+for (i = 0; i < count; i++)
+ printf("%s badness %d\n", pool_solvid2str(pool, elements[i]), elements[count + i]);
+#endif
+
+ if (havebad)
+ {
+ /* simple stable insertion sort */
+ if (pool->installed)
+ for (i = 0; i < count; i++)
+ if (pool->solvables[elements[i]].repo == pool->installed)
+ elements[i + count] = 0;
+ for (i = 1; i < count; i++)
+ for (j = i, pp = elements + count + j; j > 0; j--, pp--)
+ if (pp[-1] > pp[0])
+ {
+ Id *pp2 = pp - count;
+ Id p = pp[-1];
+ pp[-1] = pp[0];
+ pp[0] = p;
+ p = pp2[-1];
+ pp2[-1] = pp2[0];
+ pp2[0] = p;
+ }
+ else
+ break;
+ }
+ queue_truncate(plist, count);
+}
+
+#if 0
+static void
+sort_by_srcversion(Pool *pool, Queue *plist)
+{
+ int i, count = plist->count, ent = 0;
+ queue_insertn(plist, count, count, 0);
+ for (i = 0; i < count; i++)
+ {
+ Id name, evr, p = plist->elements[i];
+ Solvable *s = pool->solvables + p;
+ if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
+ name = s->name;
+ else
+ name = solvable_lookup_id(s, SOLVABLE_SOURCENAME);
+ if (solvable_lookup_void(s, SOLVABLE_SOURCEEVR))
+ evr = s->evr;
+ else
+ evr = solvable_lookup_id(s, SOLVABLE_SOURCEEVR);
+ if (!name || !evr || ISRELDEP(evr))
+ continue;
+ queue_push(plist, i);
+ queue_push2(plist, name, evr);
+ ent++;
+ }
+ sort_by_name_evr_array(pool, plist, count, ent);
+}
+#endif
+
+static void
+sort_by_common_dep(Pool *pool, Queue *plist)
+{
+ int i, count = plist->count, ent = 0;
+ Id id, *dp;
+ queue_insertn(plist, count, count, 0);
+ for (i = 0; i < count; i++)
+ {
+ Id p = plist->elements[i];
+ Solvable *s = pool->solvables + p;
+ if (!s->provides)
+ continue;
+ for (dp = s->repo->idarraydata + s->provides; (id = *dp++) != 0; )
+ {
+ Reldep *rd;
+ if (!ISRELDEP(id))
+ continue;
+ rd = GETRELDEP(pool, id);
+ if ((rd->flags == REL_EQ || rd->flags == (REL_EQ | REL_LT) || rd->flags == REL_LT) && !ISRELDEP(rd->evr))
+ {
+ if (rd->flags == REL_EQ)
+ {
+ /* ignore hashes */
+ const char *s = pool_id2str(pool, rd->evr);
+ if (strlen(s) >= 4)
+ {
+ while ((*s >= 'a' && *s <= 'f') || (*s >= '0' && *s <= '9'))
+ s++;
+ if (!*s)
+ continue;
+ }
+ }
+ queue_push(plist, i);
+ queue_push2(plist, rd->name, rd->flags == REL_LT ? -rd->evr : rd->evr);
+ ent++;
+ }
+ }
+ }
+ sort_by_name_evr_array(pool, plist, count, ent);
+}
+
+/* check if we have an update candidate */
+static void
+dislike_old_versions(Pool *pool, Queue *plist)
+{
+ int i, count;
+
+ for (i = 0, count = plist->count; i < count; i++)
+ {
+ Id p = plist->elements[i];
+ Solvable *s = pool->solvables + p;
+ Repo *repo = s->repo;
+ Id q, qq;
+ int bad = 0;
+
+ if (!repo || repo == pool->installed)
+ continue;
+ FOR_PROVIDES(q, qq, s->name)
+ {
+ Solvable *qs = pool->solvables + q;
+ if (q == p)
+ continue;
+ if (s->name != qs->name || s->arch != qs->arch)
+ continue;
+ if (repo->priority != qs->repo->priority)
+ {
+ if (repo->priority > qs->repo->priority)
+ continue;
+ bad = 1;
+ break;
+ }
+ if (pool_evrcmp(pool, qs->evr, s->evr, EVRCMP_COMPARE) > 0)
+ {
+ bad = 1;
+ break;
+ }
+ }
+ if (!bad)
+ continue;
+ /* bring to back */
+ if (i < plist->count - 1)
+ {
+ memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id));
+ plist->elements[plist->count - 1] = p;
+ }
+ i--;
+ count--;
+ }
+}
+
+/*
+ * POLICY_MODE_CHOOSE: default, do all pruning steps
+ * POLICY_MODE_RECOMMEND: leave out prune_to_recommended
+ * POLICY_MODE_SUGGEST: leave out prune_to_recommended, do prio pruning just per name
+ */
+void
+policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
+{
+ Pool *pool = solv->pool;
+ if (plist->count > 1)
+ {
+ if (mode != POLICY_MODE_SUGGEST)
+ solver_prune_to_highest_prio(solv, plist);
+ else
+ solver_prune_to_highest_prio_per_name(solv, plist);
+ }
+ if (plist->count > 1)
+ prune_to_best_arch(pool, plist);
+ if (plist->count > 1)
+ prune_to_best_version(pool, plist);
+ if (plist->count > 1 && (mode == POLICY_MODE_CHOOSE || mode == POLICY_MODE_CHOOSE_NOREORDER))
+ {
+ prune_to_recommended(solv, plist);
+ if (plist->count > 1 && mode != POLICY_MODE_CHOOSE_NOREORDER)
+ {
+ /* do some fancy reordering */
+#if 0
+ sort_by_srcversion(pool, plist);
+#endif
+ dislike_old_versions(pool, plist);
+ sort_by_common_dep(pool, plist);
+ prefer_suggested(solv, plist);
+ }
+ }
+}
+
+
+/* check if there is an illegal architecture change if
+ * installed solvable s1 is replaced by s2 */
+int
+policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
+{
+ Pool *pool = solv->pool;
+ Id a1 = s1->arch, a2 = s2->arch;
+
+ /* we allow changes to/from noarch */
+ if (a1 == a2 || a1 == pool->noarchid || a2 == pool->noarchid)
+ 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;
+}
+
+/* check if there is an illegal vendor change if
+ * installed solvable s1 is replaced by s2 */
+int
+policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
+{
+ Pool *pool = solv->pool;
+ Id v1, v2;
+ Id vendormask1, vendormask2;
+
+ if (pool->custom_vendorcheck)
+ return pool->custom_vendorcheck(pool, s1, s2);
+
+ /* treat a missing vendor as empty string */
+ v1 = s1->vendor ? s1->vendor : ID_EMPTY;
+ v2 = s2->vendor ? s2->vendor : ID_EMPTY;
+ if (v1 == v2)
+ return 0;
+ vendormask1 = pool_vendor2mask(pool, v1);
+ if (!vendormask1)
+ return 1; /* can't match */
+ vendormask2 = pool_vendor2mask(pool, v2);
+ if ((vendormask1 & vendormask2) != 0)
+ return 0;
+ return 1; /* no class matches */
+}
+
+/* check if it is illegal to replace installed
+ * package "is" with package "s" (which must obsolete "is")
+ */
+int
+policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore)
+{
+ Pool *pool = solv->pool;
+ int ret = 0;
+ int duppkg = solv->dupmap_all ? 1 : 0;
+ if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !(duppkg ? solv->dup_allowdowngrade : solv->allowdowngrade))
+ {
+ if (is->name == s->name && pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0)
+ ret |= POLICY_ILLEGAL_DOWNGRADE;
+ }
+ if (!(ignore & POLICY_ILLEGAL_ARCHCHANGE) && !(duppkg ? solv->dup_allowarchchange : solv->allowarchchange))
+ {
+ if (is->arch != s->arch && policy_illegal_archchange(solv, is, s))
+ ret |= POLICY_ILLEGAL_ARCHCHANGE;
+ }
+ if (!(ignore & POLICY_ILLEGAL_VENDORCHANGE) && !(duppkg ? solv->dup_allowvendorchange : solv->allowvendorchange))
+ {
+ if (is->vendor != s->vendor && policy_illegal_vendorchange(solv, is, s))
+ ret |= POLICY_ILLEGAL_VENDORCHANGE;
+ }
+ if (!(ignore & POLICY_ILLEGAL_NAMECHANGE) && !(duppkg ? solv->dup_allownamechange : solv->allownamechange))
+ {
+ if (is->name != s->name)
+ ret |= POLICY_ILLEGAL_NAMECHANGE;
+ }
+ return ret;
+}
+
+/*-------------------------------------------------------------------
+ *
+ * create reverse obsoletes map for installed solvables
+ *
+ * For each installed solvable find which packages with *different* names
+ * obsolete the solvable.
+ * This index is used in policy_findupdatepackages() below.
+ */
+void
+policy_create_obsolete_index(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Solvable *s;
+ Repo *installed = solv->installed;
+ Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data;
+ int i, n, cnt;
+
+ solv->obsoletes = solv_free(solv->obsoletes);
+ solv->obsoletes_data = solv_free(solv->obsoletes_data);
+ if (!installed || installed->start == installed->end)
+ return;
+ cnt = installed->end - installed->start;
+ solv->obsoletes = obsoletes = solv_calloc(cnt, sizeof(Id));
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ s = pool->solvables + i;
+ if (!s->obsoletes)
+ continue;
+ if (!pool_installable(pool, s))
+ continue;
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(p, pp, obs)
+ {
+ Solvable *ps = pool->solvables + p;;
+ if (ps->repo != installed)
+ continue;
+ if (ps->name == s->name)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ obsoletes[p - installed->start]++;
+ }
+ }
+ }
+ n = 0;
+ for (i = 0; i < cnt; i++)
+ if (obsoletes[i])
+ {
+ n += obsoletes[i] + 1;
+ obsoletes[i] = n;
+ }
+ solv->obsoletes_data = obsoletes_data = solv_calloc(n + 1, sizeof(Id));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1);
+ for (i = pool->nsolvables - 1; i > 0; i--)
+ {
+ s = pool->solvables + i;
+ if (!s->obsoletes)
+ continue;
+ if (!pool_installable(pool, s))
+ continue;
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(p, pp, obs)
+ {
+ Solvable *ps = pool->solvables + p;;
+ if (ps->repo != installed)
+ continue;
+ if (ps->name == s->name)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ if (obsoletes_data[obsoletes[p - installed->start]] != i)
+ obsoletes_data[--obsoletes[p - installed->start]] = i;
+ }
+ }
+ }
+}
+
+
+/*
+ * find update candidates
+ *
+ * s: installed solvable to be updated
+ * qs: [out] queue to hold Ids of candidates
+ * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
+ * 2 = dup mode
+ *
+ */
+void
+policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
+{
+ /* installed packages get a special upgrade allowed rule */
+ Pool *pool = solv->pool;
+ Id p, pp, n, p2, pp2;
+ Id obs, *obsp;
+ Solvable *ps;
+ int haveprovobs = 0;
+ int allowdowngrade = allow_all ? 1 : solv->allowdowngrade;
+ int allownamechange = allow_all ? 1 : solv->allownamechange;
+ int allowarchchange = allow_all ? 1 : solv->allowarchchange;
+ int allowvendorchange = allow_all ? 1 : solv->allowvendorchange;
+ if (allow_all == 2)
+ {
+ allowdowngrade = solv->dup_allowdowngrade;
+ allownamechange = solv->dup_allownamechange;
+ allowarchchange = solv->dup_allowarchchange;
+ allowvendorchange = solv->dup_allowvendorchange;
+ }
+
+ queue_empty(qs);
+
+ n = s - pool->solvables;
+
+ /*
+ * look for updates for s
+ */
+ FOR_PROVIDES(p, pp, s->name) /* every provider of s' name */
+ {
+ if (p == n) /* skip itself */
+ continue;
+
+ ps = pool->solvables + p;
+ if (s->name == ps->name) /* name match */
+ {
+ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ if (!allowdowngrade && pool_evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0)
+ continue;
+ }
+ else if (!allownamechange)
+ continue;
+ else if ((!solv->noupdateprovide || solv->needupdateprovide) && ps->obsoletes) /* provides/obsoletes combination ? */
+ {
+ /* check if package ps obsoletes installed package s */
+ /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
+ * use it to limit our update candidates */
+ if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, ps))
+ continue;
+ obsp = ps->repo->idarraydata + ps->obsoletes;
+ while ((obs = *obsp++) != 0) /* for all obsoletes */
+ {
+ FOR_PROVIDES(p2, pp2, obs) /* and all matching providers of the obsoletes */
+ {
+ Solvable *ps2 = pool->solvables + p2;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs))
+ continue;
+ 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
+ */
+ haveprovobs = 1;
+ }
+ else
+ continue;
+ if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
+ continue;
+ if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
+ continue;
+ queue_push(qs, p);
+ }
+ if (!allownamechange)
+ return;
+ /* if we have found some valid candidates and noupdateprovide is not set, we're
+ done. otherwise we fallback to all obsoletes */
+ if (solv->needupdateprovide || (!solv->noupdateprovide && haveprovobs))
+ return;
+ if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
+ {
+ Id *opp;
+ for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
+ {
+ ps = pool->solvables + p;
+ if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
+ continue;
+ if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
+ continue;
+ /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
+ * use it to limit our update candidates */
+ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ queue_push(qs, p);
+ }
+ }
+}
+
+const char *
+policy_illegal2str(Solver *solv, int illegal, Solvable *s, Solvable *rs)
+{
+ Pool *pool = solv->pool;
+ const char *str;
+ if (illegal == POLICY_ILLEGAL_DOWNGRADE)
+ {
+ str = pool_tmpjoin(pool, "downgrade of ", pool_solvable2str(pool, s), 0);
+ return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
+ }
+ if (illegal == POLICY_ILLEGAL_NAMECHANGE)
+ {
+ str = pool_tmpjoin(pool, "name change of ", pool_solvable2str(pool, s), 0);
+ return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
+ }
+ if (illegal == POLICY_ILLEGAL_ARCHCHANGE)
+ {
+ str = pool_tmpjoin(pool, "architecture change of ", pool_solvable2str(pool, s), 0);
+ return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
+ }
+ if (illegal == POLICY_ILLEGAL_VENDORCHANGE)
+ {
+ str = pool_tmpjoin(pool, "vendor change from '", pool_id2str(pool, s->vendor), "' (");
+ if (rs->vendor)
+ {
+ str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to '");
+ str = pool_tmpappend(pool, str, pool_id2str(pool, rs->vendor), "' (");
+ }
+ else
+ str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to no vendor (");
+ return pool_tmpappend(pool, str, pool_solvable2str(pool, rs), ")");
+ }
+ return "unknown illegal change";
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * Generic policy interface for SAT solver
+ * The policy* function can be "overloaded" by defining a callback in the solver struct.
+ */
+
+#include "solver.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define POLICY_MODE_CHOOSE 0
+#define POLICY_MODE_RECOMMEND 1
+#define POLICY_MODE_SUGGEST 2
+#define POLICY_MODE_CHOOSE_NOREORDER 3 /* internal, do not use */
+
+
+#define POLICY_ILLEGAL_DOWNGRADE 1
+#define POLICY_ILLEGAL_ARCHCHANGE 2
+#define POLICY_ILLEGAL_VENDORCHANGE 4
+#define POLICY_ILLEGAL_NAMECHANGE 8
+
+extern void policy_filter_unwanted(Solver *solv, Queue *plist, int mode);
+extern int policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2);
+extern int policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2);
+extern int policy_is_illegal(Solver *solv, Solvable *s1, Solvable *s2, int ignore);
+extern void policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allowall);
+extern const char *policy_illegal2str(Solver *solv, int illegal, Solvable *s, Solvable *rs);
+extern void policy_update_recommendsmap(Solver *solv);
+
+extern void policy_create_obsolete_index(Solver *solv);
+
+/* internal, do not use */
+extern void prune_to_best_version(Pool *pool, Queue *plist);
+
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * pool.c
+ *
+ * The pool contains information about solvables
+ * stored optimized for memory consumption and fast retrieval.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pool.h"
+#include "poolvendor.h"
+#include "repo.h"
+#include "poolid.h"
+#include "poolid_private.h"
+#include "poolarch.h"
+#include "util.h"
+#include "bitmap.h"
+#include "evr.h"
+
+#define SOLVABLE_BLOCK 255
+
+#undef LIBSOLV_KNOWNID_H
+#define KNOWNID_INITIALIZE
+#include "knownid.h"
+#undef KNOWNID_INITIALIZE
+
+/* create pool */
+Pool *
+pool_create(void)
+{
+ Pool *pool;
+ Solvable *s;
+
+ pool = (Pool *)solv_calloc(1, sizeof(*pool));
+
+ stringpool_init (&pool->ss, initpool_data);
+
+ /* alloc space for RelDep 0 */
+ pool->rels = solv_extend_resize(0, 1, sizeof(Reldep), REL_BLOCK);
+ pool->nrels = 1;
+ memset(pool->rels, 0, sizeof(Reldep));
+
+ /* alloc space for Solvable 0 and system solvable */
+ pool->solvables = solv_extend_resize(0, 2, sizeof(Solvable), SOLVABLE_BLOCK);
+ pool->nsolvables = 2;
+ memset(pool->solvables, 0, 2 * sizeof(Solvable));
+
+ queue_init(&pool->vendormap);
+ queue_init(&pool->pooljobs);
+ queue_init(&pool->lazywhatprovidesq);
+
+#if defined(DEBIAN)
+ pool->disttype = DISTTYPE_DEB;
+ pool->noarchid = ARCH_ALL;
+#elif defined(ARCHLINUX)
+ pool->disttype = DISTTYPE_ARCH;
+ pool->noarchid = ARCH_ANY;
+#elif defined(HAIKU)
+ pool->disttype = DISTTYPE_HAIKU;
+ pool->noarchid = ARCH_ANY;
+ pool->obsoleteusesprovides = 1;
+#else
+ pool->disttype = DISTTYPE_RPM;
+ pool->noarchid = ARCH_NOARCH;
+#endif
+
+ /* initialize the system solvable */
+ s = pool->solvables + SYSTEMSOLVABLE;
+ s->name = SYSTEM_SYSTEM;
+ s->arch = pool->noarchid;
+ s->evr = ID_EMPTY;
+
+ pool->debugmask = SOLV_DEBUG_RESULT; /* FIXME */
+#if defined(FEDORA) || defined(MAGEIA)
+ pool->implicitobsoleteusescolors = 1;
+#endif
+#ifdef RPM5
+ pool->noobsoletesmultiversion = 1;
+ pool->forbidselfconflicts = 1;
+ pool->obsoleteusesprovides = 1;
+ pool->implicitobsoleteusesprovides = 1;
+ pool->havedistepoch = 1;
+#endif
+ return pool;
+}
+
+
+/* free all the resources of our pool */
+void
+pool_free(Pool *pool)
+{
+ int i;
+
+ pool_freewhatprovides(pool);
+ pool_freeidhashes(pool);
+ pool_freeallrepos(pool, 1);
+ solv_free(pool->id2arch);
+ solv_free(pool->id2color);
+ solv_free(pool->solvables);
+ stringpool_free(&pool->ss);
+ solv_free(pool->rels);
+ pool_setvendorclasses(pool, 0);
+ queue_free(&pool->vendormap);
+ queue_free(&pool->pooljobs);
+ queue_free(&pool->lazywhatprovidesq);
+ for (i = 0; i < POOL_TMPSPACEBUF; i++)
+ solv_free(pool->tmpspace.buf[i]);
+ for (i = 0; i < pool->nlanguages; i++)
+ free((char *)pool->languages[i]);
+ solv_free((void *)pool->languages);
+ solv_free(pool->languagecache);
+ solv_free(pool->errstr);
+ solv_free(pool->rootdir);
+ solv_free(pool);
+}
+
+void
+pool_freeallrepos(Pool *pool, int reuseids)
+{
+ int i;
+
+ pool_freewhatprovides(pool);
+ for (i = 1; i < pool->nrepos; i++)
+ if (pool->repos[i])
+ repo_freedata(pool->repos[i]);
+ pool->repos = solv_free(pool->repos);
+ pool->nrepos = 0;
+ pool->urepos = 0;
+ /* the first two solvables don't belong to a repo */
+ pool_free_solvable_block(pool, 2, pool->nsolvables - 2, reuseids);
+}
+
+int
+pool_setdisttype(Pool *pool, int disttype)
+{
+#ifdef MULTI_SEMANTICS
+ int olddisttype = pool->disttype;
+ switch(disttype)
+ {
+ case DISTTYPE_RPM:
+ pool->noarchid = ARCH_NOARCH;
+ break;
+ case DISTTYPE_DEB:
+ pool->noarchid = ARCH_ALL;
+ break;
+ case DISTTYPE_ARCH:
+ case DISTTYPE_HAIKU:
+ pool->noarchid = ARCH_ANY;
+ break;
+ default:
+ return -1;
+ }
+ pool->disttype = disttype;
+ pool->solvables[SYSTEMSOLVABLE].arch = pool->noarchid;
+ return olddisttype;
+#else
+ return pool->disttype == disttype ? disttype : -1;
+#endif
+}
+
+int
+pool_get_flag(Pool *pool, int flag)
+{
+ switch (flag)
+ {
+ case POOL_FLAG_PROMOTEEPOCH:
+ return pool->promoteepoch;
+ case POOL_FLAG_FORBIDSELFCONFLICTS:
+ return pool->forbidselfconflicts;
+ case POOL_FLAG_OBSOLETEUSESPROVIDES:
+ return pool->obsoleteusesprovides;
+ case POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES:
+ return pool->implicitobsoleteusesprovides;
+ case POOL_FLAG_OBSOLETEUSESCOLORS:
+ return pool->obsoleteusescolors;
+ case POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS:
+ return pool->implicitobsoleteusescolors;
+ case POOL_FLAG_NOINSTALLEDOBSOLETES:
+ return pool->noinstalledobsoletes;
+ case POOL_FLAG_HAVEDISTEPOCH:
+ return pool->havedistepoch;
+ case POOL_FLAG_NOOBSOLETESMULTIVERSION:
+ return pool->noobsoletesmultiversion;
+ case POOL_FLAG_ADDFILEPROVIDESFILTERED:
+ return pool->addfileprovidesfiltered;
+ case POOL_FLAG_NOWHATPROVIDESAUX:
+ return pool->nowhatprovidesaux;
+ default:
+ break;
+ }
+ return -1;
+}
+
+int
+pool_set_flag(Pool *pool, int flag, int value)
+{
+ int old = pool_get_flag(pool, flag);
+ switch (flag)
+ {
+ case POOL_FLAG_PROMOTEEPOCH:
+ pool->promoteepoch = value;
+ break;
+ case POOL_FLAG_FORBIDSELFCONFLICTS:
+ pool->forbidselfconflicts = value;
+ break;
+ case POOL_FLAG_OBSOLETEUSESPROVIDES:
+ pool->obsoleteusesprovides = value;
+ break;
+ case POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES:
+ pool->implicitobsoleteusesprovides = value;
+ break;
+ case POOL_FLAG_OBSOLETEUSESCOLORS:
+ pool->obsoleteusescolors = value;
+ break;
+ case POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS:
+ pool->implicitobsoleteusescolors = value;
+ break;
+ case POOL_FLAG_NOINSTALLEDOBSOLETES:
+ pool->noinstalledobsoletes = value;
+ break;
+ case POOL_FLAG_HAVEDISTEPOCH:
+ pool->havedistepoch = value;
+ break;
+ case POOL_FLAG_NOOBSOLETESMULTIVERSION:
+ pool->noobsoletesmultiversion = value;
+ break;
+ case POOL_FLAG_ADDFILEPROVIDESFILTERED:
+ pool->addfileprovidesfiltered = value;
+ break;
+ case POOL_FLAG_NOWHATPROVIDESAUX:
+ pool->nowhatprovidesaux = value;
+ break;
+ default:
+ break;
+ }
+ return old;
+}
+
+
+Id
+pool_add_solvable(Pool *pool)
+{
+ pool->solvables = solv_extend(pool->solvables, pool->nsolvables, 1, sizeof(Solvable), SOLVABLE_BLOCK);
+ memset(pool->solvables + pool->nsolvables, 0, sizeof(Solvable));
+ return pool->nsolvables++;
+}
+
+Id
+pool_add_solvable_block(Pool *pool, int count)
+{
+ Id nsolvables = pool->nsolvables;
+ if (!count)
+ return nsolvables;
+ pool->solvables = solv_extend(pool->solvables, pool->nsolvables, count, sizeof(Solvable), SOLVABLE_BLOCK);
+ memset(pool->solvables + nsolvables, 0, sizeof(Solvable) * count);
+ pool->nsolvables += count;
+ return nsolvables;
+}
+
+void
+pool_free_solvable_block(Pool *pool, Id start, int count, int reuseids)
+{
+ if (!count)
+ return;
+ if (reuseids && start + count == pool->nsolvables)
+ {
+ /* might want to shrink solvable array */
+ pool->nsolvables = start;
+ return;
+ }
+ memset(pool->solvables + start, 0, sizeof(Solvable) * count);
+}
+
+
+void
+pool_set_installed(Pool *pool, Repo *installed)
+{
+ if (pool->installed == installed)
+ return;
+ pool->installed = installed;
+ pool_freewhatprovides(pool);
+}
+
+static int
+pool_shrink_whatprovides_sortcmp(const void *ap, const void *bp, void *dp)
+{
+ int r;
+ Pool *pool = dp;
+ Id oa, ob, *da, *db;
+ oa = pool->whatprovides[*(Id *)ap];
+ ob = pool->whatprovides[*(Id *)bp];
+ if (oa == ob)
+ return *(Id *)ap - *(Id *)bp;
+ da = pool->whatprovidesdata + oa;
+ db = pool->whatprovidesdata + ob;
+ while (*db)
+ if ((r = (*da++ - *db++)) != 0)
+ return r;
+ if (*da)
+ return *da;
+ return *(Id *)ap - *(Id *)bp;
+}
+
+/*
+ * pool_shrink_whatprovides - unify whatprovides data
+ *
+ * whatprovides_rel must be empty for this to work!
+ *
+ */
+static void
+pool_shrink_whatprovides(Pool *pool)
+{
+ Id i, n, id;
+ Id *sorted;
+ Id lastid, *last, *dp, *lp;
+ Offset o;
+ int r;
+
+ if (pool->ss.nstrings < 3)
+ return;
+ sorted = solv_malloc2(pool->ss.nstrings, sizeof(Id));
+ for (i = id = 0; id < pool->ss.nstrings; id++)
+ if (pool->whatprovides[id] >= 4)
+ sorted[i++] = id;
+ n = i;
+ solv_sort(sorted, n, sizeof(Id), pool_shrink_whatprovides_sortcmp, pool);
+ last = 0;
+ lastid = 0;
+ for (i = 0; i < n; i++)
+ {
+ id = sorted[i];
+ o = pool->whatprovides[id];
+ dp = pool->whatprovidesdata + o;
+ if (last)
+ {
+ lp = last;
+ while (*dp)
+ if (*dp++ != *lp++)
+ {
+ last = 0;
+ break;
+ }
+ if (last && *lp)
+ last = 0;
+ if (last)
+ {
+ pool->whatprovides[id] = -lastid;
+ continue;
+ }
+ }
+ last = pool->whatprovidesdata + o;
+ lastid = id;
+ }
+ solv_free(sorted);
+ dp = pool->whatprovidesdata + 4;
+ for (id = 1; id < pool->ss.nstrings; id++)
+ {
+ o = pool->whatprovides[id];
+ if (!o)
+ continue;
+ if ((Id)o < 0)
+ {
+ i = -(Id)o;
+ if (i >= id)
+ abort();
+ pool->whatprovides[id] = pool->whatprovides[i];
+ continue;
+ }
+ if (o < 4)
+ continue;
+ lp = pool->whatprovidesdata + o;
+ if (lp < dp)
+ abort();
+ pool->whatprovides[id] = dp - pool->whatprovidesdata;
+ while ((*dp++ = *lp++) != 0)
+ ;
+ }
+ o = dp - pool->whatprovidesdata;
+ POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesdata from %d to %d\n", pool->whatprovidesdataoff, o);
+ if (pool->whatprovidesdataoff == o)
+ return;
+ r = pool->whatprovidesdataoff - o;
+ pool->whatprovidesdataoff = o;
+ pool->whatprovidesdata = solv_realloc(pool->whatprovidesdata, (o + pool->whatprovidesdataleft) * sizeof(Id));
+ if (r > pool->whatprovidesdataleft)
+ r = pool->whatprovidesdataleft;
+ memset(pool->whatprovidesdata + o, 0, r * sizeof(Id));
+}
+
+/* this gets rid of all the zeros in the aux */
+static void
+pool_shrink_whatprovidesaux(Pool *pool)
+{
+ int num = pool->whatprovidesauxoff;
+ Id id;
+ Offset newoff;
+ Id *op, *wp = pool->whatprovidesauxdata + 1;
+ int i;
+
+ for (i = 0; i < num; i++)
+ {
+ Offset o = pool->whatprovidesaux[i];
+ if (o < 2)
+ continue;
+ op = pool->whatprovidesauxdata + o;
+ pool->whatprovidesaux[i] = wp - pool->whatprovidesauxdata;
+ if (op < wp)
+ abort();
+ while ((id = *op++) != 0)
+ *wp++ = id;
+ }
+ newoff = wp - pool->whatprovidesauxdata;
+ pool->whatprovidesauxdata = solv_realloc(pool->whatprovidesauxdata, newoff * sizeof(Id));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesauxdata from %d to %d\n", pool->whatprovidesauxdataoff, newoff);
+ pool->whatprovidesauxdataoff = newoff;
+}
+
+
+/*
+ * pool_createwhatprovides()
+ *
+ * create hashes over pool of solvables to ease provide lookups
+ *
+ */
+void
+pool_createwhatprovides(Pool *pool)
+{
+ int i, num, np, extra;
+ Offset off;
+ Solvable *s;
+ Id id;
+ Offset *idp, n;
+ Offset *whatprovides;
+ Id *whatprovidesdata, *dp, *whatprovidesauxdata;
+ Offset *whatprovidesaux;
+ Repo *installed = pool->installed;
+ unsigned int now;
+
+ now = solv_timems(0);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "number of solvables: %d, memory used: %d K\n", pool->nsolvables, pool->nsolvables * (int)sizeof(Solvable) / 1024);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "number of ids: %d + %d\n", pool->ss.nstrings, pool->nrels);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "string memory used: %d K array + %d K data, rel memory used: %d K array\n", pool->ss.nstrings / (1024 / (int)sizeof(Id)), pool->ss.sstrings / 1024, pool->nrels * (int)sizeof(Reldep) / 1024);
+ if (pool->ss.stringhashmask || pool->relhashmask)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "string hash memory: %d K, rel hash memory : %d K\n", (pool->ss.stringhashmask + 1) / (int)(1024/sizeof(Id)), (pool->relhashmask + 1) / (int)(1024/sizeof(Id)));
+
+ pool_freeidhashes(pool); /* XXX: should not be here! */
+ pool_freewhatprovides(pool);
+ num = pool->ss.nstrings;
+ pool->whatprovides = whatprovides = solv_calloc_block(num, sizeof(Offset), WHATPROVIDES_BLOCK);
+ pool->whatprovides_rel = solv_calloc_block(pool->nrels, sizeof(Offset), WHATPROVIDES_BLOCK);
+
+ /* count providers for each name */
+ for (i = pool->nsolvables - 1; i > 0; i--)
+ {
+ Id *pp;
+ s = pool->solvables + i;
+ if (!s->provides || !s->repo || s->repo->disabled)
+ continue;
+ /* we always need the installed solvable in the whatprovides data,
+ otherwise obsoletes/conflicts on them won't work */
+ if (s->repo != installed && !pool_installable(pool, s))
+ continue;
+ pp = s->repo->idarraydata + s->provides;
+ while ((id = *pp++) != 0)
+ {
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ id = rd->name;
+ }
+ whatprovides[id]++; /* inc count of providers */
+ }
+ }
+
+ off = 4; /* first entry is undef, second is empty list, third is system solvable */
+ np = 0; /* number of names provided */
+ for (i = 0, idp = whatprovides; i < num; i++, idp++)
+ {
+ n = *idp;
+ if (!n) /* no providers */
+ {
+ *idp = 1; /* offset for empty list */
+ continue;
+ }
+ off += n; /* make space for all providers */
+ *idp = off++; /* now idp points to terminating zero */
+ np++; /* inc # of provider 'slots' for stats */
+ }
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "provide ids: %d\n", np);
+
+ /* reserve some space for relation data */
+ extra = 2 * pool->nrels;
+ if (extra < 256)
+ extra = 256;
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "provide space needed: %d + %d\n", off, extra);
+
+ /* alloc space for all providers + extra */
+ whatprovidesdata = solv_calloc(off + extra, sizeof(Id));
+ whatprovidesdata[2] = SYSTEMSOLVABLE;
+
+ /* alloc aux vector */
+ whatprovidesauxdata = 0;
+ if (!pool->nowhatprovidesaux)
+ {
+ pool->whatprovidesaux = whatprovidesaux = solv_calloc(num, sizeof(Offset));
+ pool->whatprovidesauxoff = num;
+ pool->whatprovidesauxdataoff = off;
+ pool->whatprovidesauxdata = whatprovidesauxdata = solv_calloc(pool->whatprovidesauxdataoff, sizeof(Id));
+ }
+
+ /* now fill data for all provides */
+ for (i = pool->nsolvables - 1; i > 0; i--)
+ {
+ Id *pp;
+ s = pool->solvables + i;
+ if (!s->provides || !s->repo || s->repo->disabled)
+ continue;
+ if (s->repo != installed && !pool_installable(pool, s))
+ continue;
+
+ /* for all provides of this solvable */
+ pp = s->repo->idarraydata + s->provides;
+ while ((id = *pp++) != 0)
+ {
+ Id auxid = id;
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ id = rd->name;
+ }
+ dp = whatprovidesdata + whatprovides[id]; /* offset into whatprovidesdata */
+ if (*dp != i) /* don't add same solvable twice */
+ {
+ dp[-1] = i;
+ whatprovides[id]--;
+ }
+ else
+ auxid = 1;
+ if (whatprovidesauxdata)
+ whatprovidesauxdata[whatprovides[id]] = auxid;
+ }
+ }
+ if (pool->whatprovidesaux)
+ memcpy(pool->whatprovidesaux, pool->whatprovides, num * sizeof(Id));
+ pool->whatprovidesdata = whatprovidesdata;
+ pool->whatprovidesdataoff = off;
+ pool->whatprovidesdataleft = extra;
+ pool_shrink_whatprovides(pool);
+ if (pool->whatprovidesaux)
+ pool_shrink_whatprovidesaux(pool);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovides memory used: %d K id array, %d K data\n", (pool->ss.nstrings + pool->nrels + WHATPROVIDES_BLOCK) / (int)(1024/sizeof(Id)), (pool->whatprovidesdataoff + pool->whatprovidesdataleft) / (int)(1024/sizeof(Id)));
+ if (pool->whatprovidesaux)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovidesaux memory used: %d K id array, %d K data\n", pool->whatprovidesauxoff / (int)(1024/sizeof(Id)), pool->whatprovidesauxdataoff / (int)(1024/sizeof(Id)));
+
+ queue_empty(&pool->lazywhatprovidesq);
+ if ((!pool->addedfileprovides && pool->disttype == DISTTYPE_RPM) || pool->addedfileprovides == 1)
+ {
+ if (!pool->addedfileprovides)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "WARNING: pool_addfileprovides was not called, this may result in slow operation\n");
+ /* lazyly add file provides */
+ for (i = 1; i < num; i++)
+ {
+ const char *str = pool->ss.stringspace + pool->ss.strings[i];
+ if (str[0] != '/')
+ continue;
+ if (pool->addedfileprovides == 1 && repodata_filelistfilter_matches(0, str))
+ continue;
+ /* setup lazy adding, but remember old value */
+ if (pool->whatprovides[i] > 1)
+ queue_push2(&pool->lazywhatprovidesq, i, pool->whatprovides[i]);
+ pool->whatprovides[i] = 0;
+ if (pool->whatprovidesaux)
+ pool->whatprovidesaux[i] = 0; /* sorry */
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "lazywhatprovidesq size: %d entries\n", pool->lazywhatprovidesq.count / 2);
+ }
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "createwhatprovides took %d ms\n", solv_timems(now));
+}
+
+/*
+ * free all of our whatprovides data
+ * be careful, everything internalized with pool_queuetowhatprovides is
+ * gone, too
+ */
+void
+pool_freewhatprovides(Pool *pool)
+{
+ pool->whatprovides = solv_free(pool->whatprovides);
+ pool->whatprovides_rel = solv_free(pool->whatprovides_rel);
+ pool->whatprovidesdata = solv_free(pool->whatprovidesdata);
+ pool->whatprovidesdataoff = 0;
+ pool->whatprovidesdataleft = 0;
+ pool->whatprovidesaux = solv_free(pool->whatprovidesaux);
+ pool->whatprovidesauxdata = solv_free(pool->whatprovidesauxdata);
+ pool->whatprovidesauxoff = 0;
+ pool->whatprovidesauxdataoff = 0;
+}
+
+
+/******************************************************************************/
+
+/*
+ * pool_queuetowhatprovides - add queue contents to whatprovidesdata
+ *
+ * used for whatprovides, jobs, learnt rules, selections
+ * input: q: queue of Ids
+ * returns: Offset into whatprovidesdata
+ *
+ */
+
+Id
+pool_ids2whatprovides(Pool *pool, Id *ids, int count)
+{
+ Offset off;
+
+ if (count == 0) /* queue empty -> 1 */
+ return 1;
+ if (count == 1 && *ids == SYSTEMSOLVABLE)
+ return 2;
+
+ /* extend whatprovidesdata if needed, +1 for 0-termination */
+ if (pool->whatprovidesdataleft < count + 1)
+ {
+ POOL_DEBUG(SOLV_DEBUG_STATS, "growing provides hash data...\n");
+ pool->whatprovidesdata = solv_realloc(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, ids, count * sizeof(Id));
+
+ /* adapt count and 0-terminate */
+ pool->whatprovidesdataoff += count;
+ pool->whatprovidesdata[pool->whatprovidesdataoff++] = 0;
+ pool->whatprovidesdataleft -= count + 1;
+
+ return (Id)off;
+}
+
+Id
+pool_queuetowhatprovides(Pool *pool, Queue *q)
+{
+ int count = q->count;
+ if (count == 0) /* queue empty -> 1 */
+ return 1;
+ if (count == 1 && q->elements[0] == SYSTEMSOLVABLE)
+ return 2;
+ return pool_ids2whatprovides(pool, q->elements, count);
+}
+
+
+/*************************************************************************/
+
+#if defined(MULTI_SEMANTICS)
+# define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE)
+#elif defined(DEBIAN)
+# define EVRCMP_DEPCMP EVRCMP_COMPARE
+#else
+# define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE
+#endif
+
+/* check if a package's nevr matches a dependency */
+/* semi-private, called from public pool_match_nevr */
+
+int
+pool_match_nevr_rel(Pool *pool, Solvable *s, Id d)
+{
+ Reldep *rd = GETRELDEP(pool, d);
+ Id name = rd->name;
+ Id evr = rd->evr;
+ int flags = rd->flags;
+
+ if (flags > 7)
+ {
+ switch (flags)
+ {
+ case REL_ARCH:
+ if (s->arch != evr)
+ {
+ if (evr != ARCH_SRC || s->arch != ARCH_NOSRC)
+ return 0;
+ }
+ return pool_match_nevr(pool, s, name);
+ case REL_OR:
+ if (pool_match_nevr(pool, s, name))
+ return 1;
+ return pool_match_nevr(pool, s, evr);
+ case REL_AND:
+ case REL_WITH:
+ if (!pool_match_nevr(pool, s, name))
+ return 0;
+ return pool_match_nevr(pool, s, evr);
+ case REL_MULTIARCH:
+ if (evr != ARCH_ANY)
+ return 0;
+ /* XXX : need to check for Multi-Arch: allowed! */
+ return pool_match_nevr(pool, s, name);
+ default:
+ return 0;
+ }
+ }
+ if (!pool_match_nevr(pool, s, name))
+ return 0;
+ if (evr == s->evr)
+ return (flags & REL_EQ) ? 1 : 0;
+ if (!flags)
+ return 0;
+ if (flags == 7)
+ return 1;
+ switch (pool_evrcmp(pool, s->evr, evr, EVRCMP_DEPCMP))
+ {
+ case -2:
+ return 1;
+ case -1:
+ return (flags & REL_LT) ? 1 : 0;
+ case 0:
+ return (flags & REL_EQ) ? 1 : 0;
+ case 1:
+ return (flags & REL_GT) ? 1 : 0;
+ case 2:
+ return (flags & REL_EQ) ? 1 : 0;
+ default:
+ break;
+ }
+ return 0;
+}
+
+#if defined(HAIKU) || defined(MULTI_SEMANTICS)
+/* forward declaration */
+static int pool_match_flags_evr_rel_compat(Pool *pool, Reldep *range, int flags, int evr);
+#endif
+
+/* match (flags, evr) against provider (pflags, pevr) */
+static inline int
+pool_match_flags_evr(Pool *pool, int pflags, Id pevr, int flags, int evr)
+{
+ if (!pflags || !flags || pflags >= 8 || flags >= 8)
+ return 0;
+ if (flags == 7 || pflags == 7)
+ return 1; /* rel provides every version */
+ if ((pflags & flags & (REL_LT | REL_GT)) != 0)
+ return 1; /* both rels show in the same direction */
+ if (pevr == evr)
+ return (flags & pflags & REL_EQ) ? 1 : 0;
+#if defined(HAIKU) || defined(MULTI_SEMANTICS)
+ if (ISRELDEP(pevr))
+ {
+ Reldep *rd = GETRELDEP(pool, pevr);
+ if (rd->flags == REL_COMPAT)
+ return pool_match_flags_evr_rel_compat(pool, rd, flags, evr);
+ }
+#endif
+ switch (pool_evrcmp(pool, pevr, evr, EVRCMP_DEPCMP))
+ {
+ case -2:
+ return (pflags & REL_EQ) ? 1 : 0;
+ case -1:
+ return (flags & REL_LT) || (pflags & REL_GT) ? 1 : 0;
+ case 0:
+ return (flags & pflags & REL_EQ) ? 1 : 0;
+ case 1:
+ return (flags & REL_GT) || (pflags & REL_LT) ? 1 : 0;
+ case 2:
+ return (flags & REL_EQ) ? 1 : 0;
+ default:
+ break;
+ }
+ return 0;
+}
+
+#if defined(HAIKU) || defined(MULTI_SEMANTICS)
+static int
+pool_match_flags_evr_rel_compat(Pool *pool, Reldep *range, int flags, int evr)
+{
+ /* range->name is the actual version, range->evr the backwards compatibility
+ version. If flags are '>=' or '>', we match the compatibility version
+ as well, otherwise only the actual version. */
+ if (!(flags & REL_GT) || (flags & REL_LT))
+ return pool_match_flags_evr(pool, REL_EQ, range->name, flags, evr);
+ return pool_match_flags_evr(pool, REL_LT | REL_EQ, range->name, flags, evr) &&
+ pool_match_flags_evr(pool, REL_GT | REL_EQ, range->evr, REL_EQ, evr);
+}
+#endif
+
+/* public (i.e. not inlined) version of pool_match_flags_evr */
+int
+pool_intersect_evrs(Pool *pool, int pflags, Id pevr, int flags, int evr)
+{
+ return pool_match_flags_evr(pool, pflags, pevr, flags, evr);
+}
+
+/* match two dependencies (d1 = provider) */
+
+int
+pool_match_dep(Pool *pool, Id d1, Id d2)
+{
+ Reldep *rd1, *rd2;
+
+ if (d1 == d2)
+ return 1;
+ if (!ISRELDEP(d1))
+ {
+ if (!ISRELDEP(d2))
+ return 0;
+ rd2 = GETRELDEP(pool, d2);
+ return pool_match_dep(pool, d1, rd2->name);
+ }
+ rd1 = GETRELDEP(pool, d1);
+ if (!ISRELDEP(d2))
+ {
+ return pool_match_dep(pool, rd1->name, d2);
+ }
+ rd2 = GETRELDEP(pool, d2);
+ /* first match name */
+ if (!pool_match_dep(pool, rd1->name, rd2->name))
+ return 0;
+ /* name matches, check flags and evr */
+ return pool_intersect_evrs(pool, rd1->flags, rd1->evr, rd2->flags, rd2->evr);
+}
+
+Id
+pool_searchlazywhatprovidesq(Pool *pool, Id d)
+{
+ int start = 0;
+ int end = pool->lazywhatprovidesq.count;
+ Id *elements;
+ if (!end)
+ return 0;
+ elements = pool->lazywhatprovidesq.elements;
+ while (end - start > 16)
+ {
+ int mid = (start + end) / 2 & ~1;
+ if (elements[mid] == d)
+ return elements[mid + 1];
+ if (elements[mid] < d)
+ start = mid + 2;
+ else
+ end = mid;
+ }
+ for (; start < end; start += 2)
+ if (elements[start] == d)
+ return elements[start + 1];
+ return 0;
+}
+
+/*
+ * addstdproviders
+ *
+ * lazy populating of the whatprovides array, non relation case
+ */
+static Id
+pool_addstdproviders(Pool *pool, Id d)
+{
+ const char *str;
+ Queue q;
+ Id qbuf[16];
+ Dataiterator di;
+ Id oldoffset;
+
+ if (pool->addedfileprovides == 2)
+ {
+ pool->whatprovides[d] = 1;
+ return 1;
+ }
+ str = pool->ss.stringspace + pool->ss.strings[d];
+ if (*str != '/')
+ {
+ pool->whatprovides[d] = 1;
+ return 1;
+ }
+ queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
+ dataiterator_init(&di, pool, 0, 0, SOLVABLE_FILELIST, str, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
+ for (; dataiterator_step(&di); dataiterator_skip_solvable(&di))
+ {
+ Solvable *s = pool->solvables + di.solvid;
+ /* XXX: maybe should add a provides dependency to the solvables
+ * OTOH this is only needed for rel deps that filter the provides,
+ * and those should not use filelist entries */
+ if (s->repo->disabled)
+ continue;
+ if (s->repo != pool->installed && !pool_installable(pool, s))
+ continue;
+ queue_push(&q, di.solvid);
+ }
+ dataiterator_free(&di);
+ oldoffset = pool_searchlazywhatprovidesq(pool, d);
+ if (!q.count)
+ pool->whatprovides[d] = oldoffset ? oldoffset : 1;
+ else
+ {
+ if (oldoffset)
+ {
+ Id *oo = pool->whatprovidesdata + oldoffset;
+ int i;
+ /* unify both queues. easy, as we know both are sorted */
+ for (i = 0; i < q.count; i++)
+ {
+ if (*oo > q.elements[i])
+ continue;
+ if (*oo < q.elements[i])
+ queue_insert(&q, i, *oo);
+ oo++;
+ if (!*oo)
+ break;
+ }
+ while (*oo)
+ queue_push(&q, *oo++);
+ if (q.count == oo - (pool->whatprovidesdata + oldoffset))
+ {
+ /* end result has same size as oldoffset -> no new entries */
+ queue_free(&q);
+ pool->whatprovides[d] = oldoffset;
+ return oldoffset;
+ }
+ }
+ pool->whatprovides[d] = pool_queuetowhatprovides(pool, &q);
+ }
+ queue_free(&q);
+ return pool->whatprovides[d];
+}
+
+
+static inline int
+pool_is_kind(Pool *pool, Id name, Id kind)
+{
+ const char *n;
+ if (!kind)
+ return 1;
+ n = pool_id2str(pool, name);
+ if (kind != 1)
+ {
+ const char *kn = pool_id2str(pool, kind);
+ int knl = strlen(kn);
+ return !strncmp(n, kn, knl) && n[knl] == ':' ? 1 : 0;
+ }
+ else
+ {
+ if (*n == ':')
+ return 1;
+ while(*n >= 'a' && *n <= 'z')
+ n++;
+ return *n == ':' ? 0 : 1;
+ }
+}
+
+/*
+ * addrelproviders
+ *
+ * add packages fulfilling the relation to whatprovides array
+ *
+ * some words about REL_AND and REL_IF: we assume the best case
+ * here, so that you get a "potential" result if you ask for a match.
+ * E.g. if you ask for "whatrequires A" and package X contains
+ * "Requires: A & B", you'll get "X" as an answer.
+ */
+Id
+pool_addrelproviders(Pool *pool, Id d)
+{
+ Reldep *rd;
+ Reldep *prd;
+ Queue plist;
+ Id buf[16];
+ Id name, evr, flags;
+ Id pid, *pidp;
+ Id p, *pp;
+
+ if (!ISRELDEP(d))
+ return pool_addstdproviders(pool, d);
+ rd = GETRELDEP(pool, d);
+ name = rd->name;
+ evr = rd->evr;
+ flags = rd->flags;
+ d = GETRELID(d);
+ queue_init_buffer(&plist, buf, sizeof(buf)/sizeof(*buf));
+
+ if (flags >= 8)
+ {
+ /* special relation */
+ Id wp = 0;
+ Id *pp2, *pp3;
+
+ switch (flags)
+ {
+ case REL_WITH:
+ wp = pool_whatprovides(pool, name);
+ pp2 = pool_whatprovides_ptr(pool, evr);
+ pp = pool->whatprovidesdata + wp;
+ while ((p = *pp++) != 0)
+ {
+ for (pp3 = pp2; *pp3; pp3++)
+ if (*pp3 == p)
+ break;
+ if (*pp3)
+ queue_push(&plist, p); /* found it */
+ else
+ wp = 0;
+ }
+ break;
+
+ case REL_AND:
+ case REL_OR:
+ case REL_COND:
+ if (flags == REL_COND)
+ {
+ if (ISRELDEP(evr))
+ {
+ Reldep *rd2 = GETRELDEP(pool, evr);
+ evr = rd2->flags == REL_ELSE ? rd2->evr : 0;
+ }
+ else
+ evr = 0; /* assume cond is true */
+ }
+ wp = pool_whatprovides(pool, name);
+ if (!pool->whatprovidesdata[wp])
+ wp = evr ? pool_whatprovides(pool, evr) : 1;
+ else if (evr)
+ {
+ /* sorted merge */
+ pp2 = pool_whatprovides_ptr(pool, evr);
+ pp = pool->whatprovidesdata + wp;
+ while (*pp && *pp2)
+ {
+ if (*pp < *pp2)
+ queue_push(&plist, *pp++);
+ else
+ {
+ if (*pp == *pp2)
+ pp++;
+ queue_push(&plist, *pp2++);
+ }
+ }
+ while (*pp)
+ queue_push(&plist, *pp++);
+ while (*pp2)
+ queue_push(&plist, *pp2++);
+ /* if the number of elements did not change, we can reuse wp */
+ if (pp - (pool->whatprovidesdata + wp) != plist.count)
+ wp = 0;
+ }
+ break;
+
+ case REL_NAMESPACE:
+ if (name == NAMESPACE_OTHERPROVIDERS)
+ {
+ wp = pool_whatprovides(pool, evr);
+ break;
+ }
+ if (pool->nscallback)
+ {
+ /* ask callback which packages provide the dependency
+ * 0: none
+ * 1: the system (aka SYSTEMSOLVABLE)
+ * >1: set of packages, stored as offset on whatprovidesdata
+ */
+ p = pool->nscallback(pool, pool->nscallbackdata, name, evr);
+ if (p > 1)
+ wp = p;
+ if (p == 1)
+ queue_push(&plist, SYSTEMSOLVABLE);
+ }
+ break;
+ case REL_ARCH:
+ /* small hack: make it possible to match <pkg>.src
+ * we have to iterate over the solvables as src packages do not
+ * provide anything, thus they are not indexed in our
+ * whatprovides hash */
+ if (evr == ARCH_SRC || evr == ARCH_NOSRC)
+ {
+ Solvable *s;
+ for (p = 1, s = pool->solvables + p; p < pool->nsolvables; p++, s++)
+ {
+ if (!s->repo)
+ continue;
+ if (s->arch != evr && s->arch != ARCH_NOSRC)
+ continue;
+ if (pool_disabled_solvable(pool, s))
+ continue;
+ if (!name || pool_match_nevr(pool, s, name))
+ queue_push(&plist, p);
+ }
+ break;
+ }
+ if (!name)
+ {
+ FOR_POOL_SOLVABLES(p)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->repo != pool->installed && !pool_installable(pool, s))
+ continue;
+ if (s->arch == evr)
+ queue_push(&plist, p);
+ }
+ break;
+ }
+ wp = pool_whatprovides(pool, name);
+ pp = pool->whatprovidesdata + wp;
+ while ((p = *pp++) != 0)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->arch == evr)
+ queue_push(&plist, p);
+ else
+ wp = 0;
+ }
+ break;
+ case REL_MULTIARCH:
+ if (evr != ARCH_ANY)
+ break;
+ /* XXX : need to check for Multi-Arch: allowed! */
+ wp = pool_whatprovides(pool, name);
+ break;
+ case REL_KIND:
+ /* package kind filtering */
+ if (!name)
+ {
+ FOR_POOL_SOLVABLES(p)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->repo != pool->installed && !pool_installable(pool, s))
+ continue;
+ if (pool_is_kind(pool, s->name, evr))
+ queue_push(&plist, p);
+ }
+ break;
+ }
+ wp = pool_whatprovides(pool, name);
+ pp = pool->whatprovidesdata + wp;
+ while ((p = *pp++) != 0)
+ {
+ Solvable *s = pool->solvables + p;
+ if (pool_is_kind(pool, s->name, evr))
+ queue_push(&plist, p);
+ else
+ wp = 0;
+ }
+ break;
+ case REL_FILECONFLICT:
+ pp = pool_whatprovides_ptr(pool, name);
+ while ((p = *pp++) != 0)
+ {
+ Id origd = MAKERELDEP(d);
+ Solvable *s = pool->solvables + p;
+ if (!s->provides)
+ continue;
+ pidp = s->repo->idarraydata + s->provides;
+ while ((pid = *pidp++) != 0)
+ if (pid == origd)
+ break;
+ if (pid)
+ queue_push(&plist, p);
+ }
+ break;
+ default:
+ break;
+ }
+ if (wp)
+ {
+ /* we can reuse an existing entry */
+ queue_free(&plist);
+ pool->whatprovides_rel[d] = wp;
+ return wp;
+ }
+ }
+ else if (flags)
+ {
+ Id *ppaux = 0;
+ /* simple version comparison relation */
+#if 0
+ POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: what provides %s?\n", pool_dep2str(pool, name));
+#endif
+ pp = pool_whatprovides_ptr(pool, name);
+ if (!ISRELDEP(name) && name < pool->whatprovidesauxoff)
+ ppaux = pool->whatprovidesaux[name] ? pool->whatprovidesauxdata + pool->whatprovidesaux[name] : 0;
+ while (ISRELDEP(name))
+ {
+ rd = GETRELDEP(pool, name);
+ name = rd->name;
+ }
+ while ((p = *pp++) != 0)
+ {
+ Solvable *s = pool->solvables + p;
+ if (ppaux)
+ {
+ pid = *ppaux++;
+ if (pid && pid != 1)
+ {
+#if 0
+ POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: aux hit %d %s\n", p, pool_dep2str(pool, pid));
+#endif
+ if (!ISRELDEP(pid))
+ {
+ if (pid != name)
+ continue; /* wrong provides name */
+ if (pool->disttype == DISTTYPE_DEB)
+ continue; /* unversioned provides can never match versioned deps */
+ }
+ else
+ {
+ prd = GETRELDEP(pool, pid);
+ if (prd->name != name)
+ continue; /* wrong provides name */
+ /* right package, both deps are rels. check flags/evr */
+ if (!pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr))
+ continue;
+ }
+ queue_push(&plist, p);
+ continue;
+ }
+ }
+ if (!s->provides)
+ {
+ /* no provides - check nevr */
+ if (pool_match_nevr_rel(pool, s, MAKERELDEP(d)))
+ queue_push(&plist, p);
+ continue;
+ }
+ /* solvable p provides name in some rels */
+ pidp = s->repo->idarraydata + s->provides;
+ while ((pid = *pidp++) != 0)
+ {
+ if (!ISRELDEP(pid))
+ {
+ if (pid != name)
+ continue; /* wrong provides name */
+ if (pool->disttype == DISTTYPE_DEB)
+ continue; /* unversioned provides can never match versioned deps */
+ break;
+ }
+ prd = GETRELDEP(pool, pid);
+ if (prd->name != name)
+ continue; /* wrong provides name */
+ /* right package, both deps are rels. check flags/evr */
+ if (pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr))
+ break; /* matches */
+ }
+ if (!pid)
+ continue; /* none of the providers matched */
+ queue_push(&plist, p);
+ }
+ /* make our system solvable provide all unknown rpmlib() stuff */
+ if (plist.count == 0 && !strncmp(pool_id2str(pool, name), "rpmlib(", 7))
+ queue_push(&plist, SYSTEMSOLVABLE);
+ }
+ /* add providers to whatprovides */
+#if 0
+ POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: adding %d packages to %d\n", plist.count, d);
+#endif
+ pool->whatprovides_rel[d] = pool_queuetowhatprovides(pool, &plist);
+ queue_free(&plist);
+
+ return pool->whatprovides_rel[d];
+}
+
+void
+pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr)
+{
+ int nrels = pool->nrels;
+ Id d;
+ Reldep *rd;
+
+ if (!pool->whatprovides_rel)
+ return;
+ for (d = 1, rd = pool->rels + d; d < nrels; d++, rd++)
+ {
+ if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS)
+ continue;
+ if (ns && rd->name != ns)
+ continue;
+ if (evr && rd->evr != evr)
+ continue;
+ pool->whatprovides_rel[d] = 0;
+ }
+}
+
+/* intersect dependencies in keyname with dep, return list of matching packages */
+void
+pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker)
+{
+ Id p;
+
+ queue_empty(q);
+ FOR_POOL_SOLVABLES(p)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->repo->disabled)
+ continue;
+ if (s->repo != pool->installed && !pool_installable(pool, s))
+ continue;
+ if (solvable_matchesdep(s, keyname, dep, marker))
+ queue_push(q, p);
+ }
+}
+
+/*************************************************************************/
+
+void
+pool_debug(Pool *pool, int type, const char *format, ...)
+{
+ va_list args;
+ char buf[1024];
+
+ if ((type & (SOLV_FATAL|SOLV_ERROR)) == 0)
+ {
+ if ((pool->debugmask & type) == 0)
+ return;
+ }
+ va_start(args, format);
+ if (!pool->debugcallback)
+ {
+ if ((type & (SOLV_FATAL|SOLV_ERROR)) == 0 && !(pool->debugmask & SOLV_DEBUG_TO_STDERR))
+ vprintf(format, args);
+ else
+ vfprintf(stderr, format, args);
+ return;
+ }
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+ pool->debugcallback(pool, pool->debugcallbackdata, type, buf);
+}
+
+int
+pool_error(Pool *pool, int ret, const char *format, ...)
+{
+ va_list args;
+ int l;
+ va_start(args, format);
+ if (!pool->errstr)
+ {
+ pool->errstra = 1024;
+ pool->errstr = solv_malloc(pool->errstra);
+ }
+ if (!*format)
+ {
+ *pool->errstr = 0;
+ l = 0;
+ }
+ else
+ l = vsnprintf(pool->errstr, pool->errstra, format, args);
+ va_end(args);
+ if (l >= 0 && l + 1 > pool->errstra)
+ {
+ pool->errstra = l + 256;
+ pool->errstr = solv_realloc(pool->errstr, pool->errstra);
+ va_start(args, format);
+ l = vsnprintf(pool->errstr, pool->errstra, format, args);
+ va_end(args);
+ }
+ if (l < 0)
+ strcpy(pool->errstr, "unknown error");
+ if (pool->debugmask & SOLV_ERROR)
+ pool_debug(pool, SOLV_ERROR, "%s\n", pool->errstr);
+ return ret;
+}
+
+char *
+pool_errstr(Pool *pool)
+{
+ return pool->errstr ? pool->errstr : "no error";
+}
+
+void
+pool_setdebuglevel(Pool *pool, int level)
+{
+ int mask = SOLV_DEBUG_RESULT;
+ if (level > 0)
+ mask |= SOLV_DEBUG_STATS|SOLV_DEBUG_ANALYZE|SOLV_DEBUG_UNSOLVABLE|SOLV_DEBUG_SOLVER|SOLV_DEBUG_TRANSACTION|SOLV_ERROR;
+ if (level > 1)
+ mask |= SOLV_DEBUG_JOB|SOLV_DEBUG_SOLUTIONS|SOLV_DEBUG_POLICY;
+ if (level > 2)
+ mask |= SOLV_DEBUG_PROPAGATE;
+ if (level > 3)
+ mask |= SOLV_DEBUG_RULE_CREATION;
+ mask |= pool->debugmask & SOLV_DEBUG_TO_STDERR; /* keep bit */
+ pool->debugmask = mask;
+}
+
+void pool_setdebugcallback(Pool *pool, void (*debugcallback)(struct _Pool *, void *data, int type, const char *str), void *debugcallbackdata)
+{
+ pool->debugcallback = debugcallback;
+ pool->debugcallbackdata = debugcallbackdata;
+}
+
+void pool_setdebugmask(Pool *pool, int mask)
+{
+ pool->debugmask = mask;
+}
+
+void pool_setloadcallback(Pool *pool, int (*cb)(struct _Pool *, struct _Repodata *, void *), void *loadcbdata)
+{
+ pool->loadcallback = cb;
+ pool->loadcallbackdata = loadcbdata;
+}
+
+void pool_setnamespacecallback(Pool *pool, Id (*cb)(struct _Pool *, void *, Id, Id), void *nscbdata)
+{
+ pool->nscallback = cb;
+ pool->nscallbackdata = nscbdata;
+}
+
+/*************************************************************************/
+
+struct searchfiles {
+ Id *ids;
+ int nfiles;
+ Map seen;
+};
+
+#define SEARCHFILES_BLOCK 127
+
+static void
+pool_addfileprovides_dep(Pool *pool, Id *ida, struct searchfiles *sf, struct searchfiles *isf)
+{
+ Id dep, sid;
+ const char *s;
+ struct searchfiles *csf;
+
+ while ((dep = *ida++) != 0)
+ {
+ csf = sf;
+ while (ISRELDEP(dep))
+ {
+ Reldep *rd;
+ sid = pool->ss.nstrings + GETRELID(dep);
+ if (MAPTST(&csf->seen, sid))
+ {
+ dep = 0;
+ break;
+ }
+ MAPSET(&csf->seen, sid);
+ rd = GETRELDEP(pool, dep);
+ if (rd->flags < 8)
+ dep = rd->name;
+ else if (rd->flags == REL_NAMESPACE)
+ {
+ if (rd->name == NAMESPACE_SPLITPROVIDES)
+ {
+ csf = isf;
+ if (!csf || MAPTST(&csf->seen, sid))
+ {
+ dep = 0;
+ break;
+ }
+ MAPSET(&csf->seen, sid);
+ }
+ dep = rd->evr;
+ }
+ else if (rd->flags == REL_FILECONFLICT)
+ {
+ dep = 0;
+ break;
+ }
+ else
+ {
+ Id ids[2];
+ ids[0] = rd->name;
+ ids[1] = 0;
+ pool_addfileprovides_dep(pool, ids, csf, isf);
+ dep = rd->evr;
+ }
+ }
+ if (!dep)
+ continue;
+ if (MAPTST(&csf->seen, dep))
+ continue;
+ MAPSET(&csf->seen, dep);
+ s = pool_id2str(pool, dep);
+ if (*s != '/')
+ continue;
+ if (csf != isf && pool->addedfileprovides == 1 && !repodata_filelistfilter_matches(0, s))
+ continue; /* skip non-standard locations csf == isf: installed case */
+ csf->ids = solv_extend(csf->ids, csf->nfiles, 1, sizeof(Id), SEARCHFILES_BLOCK);
+ csf->ids[csf->nfiles++] = dep;
+ }
+}
+
+struct addfileprovides_cbdata {
+ int nfiles;
+ Id *ids;
+ char **dirs;
+ char **names;
+
+ Id *dids;
+
+ Map providedids;
+
+ Map useddirs;
+};
+
+static int
+addfileprovides_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
+{
+ struct addfileprovides_cbdata *cbd = cbdata;
+ int i;
+
+ if (!cbd->useddirs.size)
+ {
+ map_init(&cbd->useddirs, data->dirpool.ndirs + 1);
+ if (!cbd->dirs)
+ {
+ cbd->dirs = solv_malloc2(cbd->nfiles, sizeof(char *));
+ cbd->names = solv_malloc2(cbd->nfiles, sizeof(char *));
+ for (i = 0; i < cbd->nfiles; i++)
+ {
+ char *s = solv_strdup(pool_id2str(data->repo->pool, cbd->ids[i]));
+ cbd->dirs[i] = s;
+ s = strrchr(s, '/');
+ *s = 0;
+ cbd->names[i] = s + 1;
+ }
+ }
+ for (i = 0; i < cbd->nfiles; i++)
+ {
+ Id did;
+ if (MAPTST(&cbd->providedids, cbd->ids[i]))
+ {
+ cbd->dids[i] = 0;
+ continue;
+ }
+ did = repodata_str2dir(data, cbd->dirs[i], 0);
+ cbd->dids[i] = did;
+ if (did)
+ MAPSET(&cbd->useddirs, did);
+ }
+ repodata_free_dircache(data);
+ }
+ if (value->id >= data->dirpool.ndirs || !MAPTST(&cbd->useddirs, value->id))
+ return 0;
+ for (i = 0; i < cbd->nfiles; i++)
+ if (cbd->dids[i] == value->id && !strcmp(cbd->names[i], value->str))
+ s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER);
+ return 0;
+}
+
+static void
+pool_addfileprovides_search(Pool *pool, struct addfileprovides_cbdata *cbd, struct searchfiles *sf, Repo *repoonly)
+{
+ Id p;
+ Repodata *data;
+ Repo *repo;
+ Queue fileprovidesq;
+ int i, j, repoid, repodataid;
+ int provstart, provend;
+ Map donemap;
+ int ndone, incomplete;
+
+ if (!pool->urepos)
+ return;
+
+ cbd->nfiles = sf->nfiles;
+ cbd->ids = sf->ids;
+ cbd->dirs = 0;
+ cbd->names = 0;
+ cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id));
+ map_init(&cbd->providedids, pool->ss.nstrings);
+
+ repoid = 1;
+ repo = repoonly ? repoonly : pool->repos[repoid];
+ map_init(&donemap, pool->nsolvables);
+ queue_init(&fileprovidesq);
+ provstart = provend = 0;
+ for (;;)
+ {
+ if (!repo || repo->disabled)
+ {
+ if (repoonly || ++repoid == pool->nrepos)
+ break;
+ repo = pool->repos[repoid];
+ continue;
+ }
+ ndone = 0;
+ FOR_REPODATAS(repo, repodataid, data)
+ {
+ if (ndone >= repo->nsolvables)
+ break;
+
+ if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
+ {
+ map_empty(&cbd->providedids);
+ for (i = 0; i < fileprovidesq.count; i++)
+ MAPSET(&cbd->providedids, fileprovidesq.elements[i]);
+ provstart = data->start;
+ provend = data->end;
+ for (i = 0; i < cbd->nfiles; i++)
+ if (!MAPTST(&cbd->providedids, cbd->ids[i]))
+ break;
+ if (i == cbd->nfiles)
+ {
+ /* great! no need to search files */
+ for (p = data->start; p < data->end; p++)
+ if (pool->solvables[p].repo == repo)
+ {
+ if (MAPTST(&donemap, p))
+ continue;
+ MAPSET(&donemap, p);
+ ndone++;
+ }
+ continue;
+ }
+ }
+
+ if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
+ continue;
+
+ if (data->start < provstart || data->end > provend)
+ {
+ map_empty(&cbd->providedids);
+ provstart = provend = 0;
+ }
+
+ /* check if the data is incomplete */
+ incomplete = 0;
+ if (data->state == REPODATA_AVAILABLE)
+ {
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ break;
+ if (j < data->nkeys)
+ {
+#if 0
+ for (i = 0; i < cbd->nfiles; i++)
+ if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
+ printf("need complete filelist because of %s\n", pool_id2str(pool, cbd->ids[i]));
+#endif
+ for (i = 0; i < cbd->nfiles; i++)
+ if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
+ break;
+ if (i < cbd->nfiles)
+ incomplete = 1;
+ }
+ }
+
+ /* do the search */
+ map_init(&cbd->useddirs, 0);
+ for (p = data->start; p < data->end; p++)
+ if (pool->solvables[p].repo == repo)
+ {
+ if (MAPTST(&donemap, p))
+ continue;
+ repodata_search(data, p, SOLVABLE_FILELIST, 0, addfileprovides_cb, cbd);
+ if (!incomplete)
+ {
+ MAPSET(&donemap, p);
+ ndone++;
+ }
+ }
+ map_free(&cbd->useddirs);
+ }
+
+ if (repoonly || ++repoid == pool->nrepos)
+ break;
+ repo = pool->repos[repoid];
+ }
+ map_free(&donemap);
+ queue_free(&fileprovidesq);
+ map_free(&cbd->providedids);
+ if (cbd->dirs)
+ {
+ for (i = 0; i < cbd->nfiles; i++)
+ solv_free(cbd->dirs[i]);
+ cbd->dirs = solv_free(cbd->dirs);
+ cbd->names = solv_free(cbd->names);
+ }
+}
+
+void
+pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst)
+{
+ Solvable *s;
+ Repo *installed, *repo;
+ struct searchfiles sf, isf, *isfp;
+ struct addfileprovides_cbdata cbd;
+ int i;
+ unsigned int now;
+
+ installed = pool->installed;
+ now = solv_timems(0);
+ memset(&sf, 0, sizeof(sf));
+ map_init(&sf.seen, pool->ss.nstrings + pool->nrels);
+ memset(&isf, 0, sizeof(isf));
+ map_init(&isf.seen, pool->ss.nstrings + pool->nrels);
+ pool->addedfileprovides = pool->addfileprovidesfiltered ? 1 : 2;
+
+ if (idq)
+ queue_empty(idq);
+ if (idqinst)
+ queue_empty(idqinst);
+ isfp = installed ? &isf : 0;
+ for (i = 1, s = pool->solvables + i; i < pool->nsolvables; i++, s++)
+ {
+ repo = s->repo;
+ if (!repo)
+ continue;
+ if (s->obsoletes)
+ pool_addfileprovides_dep(pool, repo->idarraydata + s->obsoletes, &sf, isfp);
+ if (s->conflicts)
+ pool_addfileprovides_dep(pool, repo->idarraydata + s->conflicts, &sf, isfp);
+ if (s->requires)
+ pool_addfileprovides_dep(pool, repo->idarraydata + s->requires, &sf, isfp);
+ if (s->recommends)
+ pool_addfileprovides_dep(pool, repo->idarraydata + s->recommends, &sf, isfp);
+ if (s->suggests)
+ pool_addfileprovides_dep(pool, repo->idarraydata + s->suggests, &sf, isfp);
+ if (s->supplements)
+ pool_addfileprovides_dep(pool, repo->idarraydata + s->supplements, &sf, isfp);
+ if (s->enhances)
+ pool_addfileprovides_dep(pool, repo->idarraydata + s->enhances, &sf, isfp);
+ }
+ map_free(&sf.seen);
+ map_free(&isf.seen);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file dependencies, %d installed file dependencies\n", sf.nfiles, isf.nfiles);
+ cbd.dids = 0;
+ if (sf.nfiles)
+ {
+#if 0
+ for (i = 0; i < sf.nfiles; i++)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in filelist\n", pool_id2str(pool, sf.ids[i]));
+#endif
+ pool_addfileprovides_search(pool, &cbd, &sf, 0);
+ if (idq)
+ for (i = 0; i < sf.nfiles; i++)
+ queue_push(idq, sf.ids[i]);
+ if (idqinst)
+ for (i = 0; i < sf.nfiles; i++)
+ queue_push(idqinst, sf.ids[i]);
+ solv_free(sf.ids);
+ }
+ if (isf.nfiles)
+ {
+#if 0
+ for (i = 0; i < isf.nfiles; i++)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in installed filelist\n", pool_id2str(pool, isf.ids[i]));
+#endif
+ if (installed)
+ pool_addfileprovides_search(pool, &cbd, &isf, installed);
+ if (installed && idqinst)
+ for (i = 0; i < isf.nfiles; i++)
+ queue_pushunique(idqinst, isf.ids[i]);
+ solv_free(isf.ids);
+ }
+ solv_free(cbd.dids);
+ pool_freewhatprovides(pool); /* as we have added provides */
+ POOL_DEBUG(SOLV_DEBUG_STATS, "addfileprovides took %d ms\n", solv_timems(now));
+}
+
+void
+pool_addfileprovides(Pool *pool)
+{
+ pool_addfileprovides_queue(pool, 0, 0);
+}
+
+void
+pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, struct _Repodata *data, struct _Repokey *key, struct _KeyValue *kv), void *cbdata)
+{
+ if (p)
+ {
+ if (pool->solvables[p].repo)
+ repo_search(pool->solvables[p].repo, p, key, match, flags, callback, cbdata);
+ return;
+ }
+ /* FIXME: obey callback return value! */
+ for (p = 1; p < pool->nsolvables; p++)
+ if (pool->solvables[p].repo)
+ repo_search(pool->solvables[p].repo, p, key, match, flags, callback, cbdata);
+}
+
+void
+pool_clear_pos(Pool *pool)
+{
+ memset(&pool->pos, 0, sizeof(pool->pos));
+}
+
+
+void
+pool_set_languages(Pool *pool, const char **languages, int nlanguages)
+{
+ int i;
+
+ pool->languagecache = solv_free(pool->languagecache);
+ pool->languagecacheother = 0;
+ for (i = 0; i < pool->nlanguages; i++)
+ free((char *)pool->languages[i]);
+ pool->languages = solv_free((void *)pool->languages);
+ pool->nlanguages = nlanguages;
+ if (!nlanguages)
+ return;
+ pool->languages = solv_calloc(nlanguages, sizeof(const char **));
+ for (i = 0; i < pool->nlanguages; i++)
+ pool->languages[i] = solv_strdup(languages[i]);
+}
+
+Id
+pool_id2langid(Pool *pool, Id id, const char *lang, int create)
+{
+ const char *n;
+ char buf[256], *p;
+ int l;
+
+ if (!lang || !*lang)
+ return id;
+ n = pool_id2str(pool, id);
+ l = strlen(n) + strlen(lang) + 2;
+ if (l > sizeof(buf))
+ p = solv_malloc(strlen(n) + strlen(lang) + 2);
+ else
+ p = buf;
+ sprintf(p, "%s:%s", n, lang);
+ id = pool_str2id(pool, p, create);
+ if (p != buf)
+ free(p);
+ return id;
+}
+
+char *
+pool_alloctmpspace(Pool *pool, int len)
+{
+ int n = pool->tmpspace.n;
+ if (!len)
+ return 0;
+ if (len > pool->tmpspace.len[n])
+ {
+ pool->tmpspace.buf[n] = solv_realloc(pool->tmpspace.buf[n], len + 32);
+ pool->tmpspace.len[n] = len + 32;
+ }
+ pool->tmpspace.n = (n + 1) % POOL_TMPSPACEBUF;
+ return pool->tmpspace.buf[n];
+}
+
+static char *
+pool_alloctmpspace_free(Pool *pool, const char *space, int len)
+{
+ if (space)
+ {
+ int n, oldn;
+ n = oldn = pool->tmpspace.n;
+ for (;;)
+ {
+ if (!n--)
+ n = POOL_TMPSPACEBUF - 1;
+ if (n == oldn)
+ break;
+ if (pool->tmpspace.buf[n] != space)
+ continue;
+ if (len > pool->tmpspace.len[n])
+ {
+ pool->tmpspace.buf[n] = solv_realloc(pool->tmpspace.buf[n], len + 32);
+ pool->tmpspace.len[n] = len + 32;
+ }
+ return pool->tmpspace.buf[n];
+ }
+ }
+ return 0;
+}
+
+void
+pool_freetmpspace(Pool *pool, const char *space)
+{
+ int n = pool->tmpspace.n;
+ if (!space)
+ return;
+ n = (n + (POOL_TMPSPACEBUF - 1)) % POOL_TMPSPACEBUF;
+ if (pool->tmpspace.buf[n] == space)
+ pool->tmpspace.n = n;
+}
+
+char *
+pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const char *str3)
+{
+ int l1, l2, l3;
+ char *s, *str;
+ l1 = str1 ? strlen(str1) : 0;
+ l2 = str2 ? strlen(str2) : 0;
+ l3 = str3 ? strlen(str3) : 0;
+ s = str = pool_alloctmpspace(pool, l1 + l2 + l3 + 1);
+ if (l1)
+ {
+ strcpy(s, str1);
+ s += l1;
+ }
+ if (l2)
+ {
+ strcpy(s, str2);
+ s += l2;
+ }
+ if (l3)
+ {
+ strcpy(s, str3);
+ s += l3;
+ }
+ *s = 0;
+ return str;
+}
+
+char *
+pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3)
+{
+ int l1, l2, l3;
+ char *s, *str;
+
+ l1 = str1 ? strlen(str1) : 0;
+ l2 = str2 ? strlen(str2) : 0;
+ l3 = str3 ? strlen(str3) : 0;
+ str = pool_alloctmpspace_free(pool, str1, l1 + l2 + l3 + 1);
+ if (str)
+ str1 = str;
+ else
+ str = pool_alloctmpspace(pool, l1 + l2 + l3 + 1);
+ s = str;
+ if (l1)
+ {
+ if (s != str1)
+ strcpy(s, str1);
+ s += l1;
+ }
+ if (l2)
+ {
+ strcpy(s, str2);
+ s += l2;
+ }
+ if (l3)
+ {
+ strcpy(s, str3);
+ s += l3;
+ }
+ *s = 0;
+ return str;
+}
+
+const char *
+pool_bin2hex(Pool *pool, const unsigned char *buf, int len)
+{
+ char *s;
+ if (!len)
+ return "";
+ s = pool_alloctmpspace(pool, 2 * len + 1);
+ solv_bin2hex(buf, len, s);
+ return s;
+}
+
+/*******************************************************************/
+
+struct mptree {
+ Id sibling;
+ Id child;
+ const char *comp;
+ int compl;
+ Id mountpoint;
+};
+
+struct ducbdata {
+ DUChanges *mps;
+ struct mptree *mptree;
+ int addsub;
+ int hasdu;
+
+ Id *dirmap;
+ int nmap;
+ Repodata *olddata;
+};
+
+
+static int
+solver_fill_DU_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
+{
+ struct ducbdata *cbd = cbdata;
+ Id mp;
+
+ if (data != cbd->olddata)
+ {
+ Id dn, mp, comp, *dirmap, *dirs;
+ int i, compl;
+ const char *compstr;
+ struct mptree *mptree;
+
+ /* create map from dir to mptree */
+ cbd->dirmap = solv_free(cbd->dirmap);
+ cbd->nmap = 0;
+ dirmap = solv_calloc(data->dirpool.ndirs, sizeof(Id));
+ mptree = cbd->mptree;
+ mp = 0;
+ for (dn = 2, dirs = data->dirpool.dirs + dn; dn < data->dirpool.ndirs; dn++)
+ {
+ comp = *dirs++;
+ if (comp <= 0)
+ {
+ mp = dirmap[-comp];
+ continue;
+ }
+ if (mp < 0)
+ {
+ /* unconnected */
+ dirmap[dn] = mp;
+ continue;
+ }
+ if (!mptree[mp].child)
+ {
+ dirmap[dn] = -mp;
+ continue;
+ }
+ if (data->localpool)
+ compstr = stringpool_id2str(&data->spool, comp);
+ else
+ compstr = pool_id2str(data->repo->pool, comp);
+ compl = strlen(compstr);
+ for (i = mptree[mp].child; i; i = mptree[i].sibling)
+ if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl))
+ break;
+ dirmap[dn] = i ? i : -mp;
+ }
+ /* change dirmap to point to mountpoint instead of mptree */
+ for (dn = 0; dn < data->dirpool.ndirs; dn++)
+ {
+ mp = dirmap[dn];
+ dirmap[dn] = mptree[mp > 0 ? mp : -mp].mountpoint;
+ }
+ cbd->dirmap = dirmap;
+ cbd->nmap = data->dirpool.ndirs;
+ cbd->olddata = data;
+ }
+ cbd->hasdu = 1;
+ if (value->id < 0 || value->id >= cbd->nmap)
+ return 0;
+ mp = cbd->dirmap[value->id];
+ if (mp < 0)
+ return 0;
+ if (cbd->addsub > 0)
+ {
+ cbd->mps[mp].kbytes += value->num;
+ cbd->mps[mp].files += value->num2;
+ }
+ else if (!(cbd->mps[mp].flags & DUCHANGES_ONLYADD))
+ {
+ cbd->mps[mp].kbytes -= value->num;
+ cbd->mps[mp].files -= value->num2;
+ }
+ return 0;
+}
+
+static void
+propagate_mountpoints(struct mptree *mptree, int pos, Id mountpoint)
+{
+ int i;
+ if (mptree[pos].mountpoint == -1)
+ mptree[pos].mountpoint = mountpoint;
+ else
+ mountpoint = mptree[pos].mountpoint;
+ for (i = mptree[pos].child; i; i = mptree[i].sibling)
+ propagate_mountpoints(mptree, i, mountpoint);
+}
+
+#define MPTREE_BLOCK 15
+
+static struct mptree *
+create_mptree(DUChanges *mps, int nmps)
+{
+ int i, nmptree;
+ struct mptree *mptree;
+ int pos, compl;
+ int mp;
+ const char *p, *path, *compstr;
+
+ mptree = solv_extend_resize(0, 1, sizeof(struct mptree), MPTREE_BLOCK);
+
+ /* our root node */
+ mptree[0].sibling = 0;
+ mptree[0].child = 0;
+ mptree[0].comp = 0;
+ mptree[0].compl = 0;
+ mptree[0].mountpoint = -1;
+ nmptree = 1;
+
+ /* create component tree */
+ for (mp = 0; mp < nmps; mp++)
+ {
+ mps[mp].kbytes = 0;
+ mps[mp].files = 0;
+ pos = 0;
+ path = mps[mp].path;
+ while(*path == '/')
+ path++;
+ while (*path)
+ {
+ if ((p = strchr(path, '/')) == 0)
+ {
+ compstr = path;
+ compl = strlen(compstr);
+ path += compl;
+ }
+ else
+ {
+ compstr = path;
+ compl = p - path;
+ path = p + 1;
+ while(*path == '/')
+ path++;
+ }
+ for (i = mptree[pos].child; i; i = mptree[i].sibling)
+ if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl))
+ break;
+ if (!i)
+ {
+ /* create new node */
+ mptree = solv_extend(mptree, nmptree, 1, sizeof(struct mptree), MPTREE_BLOCK);
+ i = nmptree++;
+ mptree[i].sibling = mptree[pos].child;
+ mptree[i].child = 0;
+ mptree[i].comp = compstr;
+ mptree[i].compl = compl;
+ mptree[i].mountpoint = -1;
+ mptree[pos].child = i;
+ }
+ pos = i;
+ }
+ mptree[pos].mountpoint = mp;
+ }
+
+ propagate_mountpoints(mptree, 0, mptree[0].mountpoint);
+
+#if 0
+ for (i = 0; i < nmptree; i++)
+ {
+ printf("#%d sibling: %d\n", i, mptree[i].sibling);
+ printf("#%d child: %d\n", i, mptree[i].child);
+ printf("#%d comp: %s\n", i, mptree[i].comp);
+ printf("#%d compl: %d\n", i, mptree[i].compl);
+ printf("#%d mountpont: %d\n", i, mptree[i].mountpoint);
+ }
+#endif
+
+ return mptree;
+}
+
+void
+pool_calc_duchanges(Pool *pool, Map *installedmap, DUChanges *mps, int nmps)
+{
+ struct mptree *mptree;
+ struct ducbdata cbd;
+ Solvable *s;
+ int i, sp;
+ Map ignoredu;
+ Repo *oldinstalled = pool->installed;
+ int haveonlyadd = 0;
+
+ map_init(&ignoredu, 0);
+ mptree = create_mptree(mps, nmps);
+
+ for (i = 0; i < nmps; i++)
+ if ((mps[i].flags & DUCHANGES_ONLYADD) != 0)
+ haveonlyadd = 1;
+ cbd.mps = mps;
+ cbd.dirmap = 0;
+ cbd.nmap = 0;
+ cbd.olddata = 0;
+ cbd.mptree = mptree;
+ cbd.addsub = 1;
+ for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++)
+ {
+ if (!s->repo || (oldinstalled && s->repo == oldinstalled))
+ continue;
+ if (!MAPTST(installedmap, sp))
+ continue;
+ cbd.hasdu = 0;
+ repo_search(s->repo, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
+ if (!cbd.hasdu && oldinstalled)
+ {
+ Id op, opp;
+ int didonlyadd = 0;
+ /* no du data available, ignore data of all installed solvables we obsolete */
+ if (!ignoredu.size)
+ map_grow(&ignoredu, oldinstalled->end - oldinstalled->start);
+ FOR_PROVIDES(op, opp, s->name)
+ {
+ Solvable *s2 = pool->solvables + op;
+ if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
+ continue;
+ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
+ continue;
+ if (op >= oldinstalled->start && op < oldinstalled->end)
+ {
+ MAPSET(&ignoredu, op - oldinstalled->start);
+ if (haveonlyadd && pool->solvables[op].repo == oldinstalled && !didonlyadd)
+ {
+ repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
+ cbd.addsub = -1;
+ repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
+ cbd.addsub = 1;
+ didonlyadd = 1;
+ }
+ }
+ }
+ if (s->obsoletes)
+ {
+ Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ FOR_PROVIDES(op, opp, obs)
+ {
+ Solvable *s2 = pool->solvables + op;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, s2, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+ continue;
+ if (op >= oldinstalled->start && op < oldinstalled->end)
+ {
+ MAPSET(&ignoredu, op - oldinstalled->start);
+ if (haveonlyadd && pool->solvables[op].repo == oldinstalled && !didonlyadd)
+ {
+ repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
+ cbd.addsub = -1;
+ repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
+ cbd.addsub = 1;
+ didonlyadd = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ cbd.addsub = -1;
+ if (oldinstalled)
+ {
+ /* assumes we allways have du data for installed solvables */
+ FOR_REPO_SOLVABLES(oldinstalled, sp, s)
+ {
+ if (MAPTST(installedmap, sp))
+ continue;
+ if (ignoredu.map && MAPTST(&ignoredu, sp - oldinstalled->start))
+ continue;
+ repo_search(oldinstalled, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
+ }
+ }
+ map_free(&ignoredu);
+ solv_free(cbd.dirmap);
+ solv_free(mptree);
+}
+
+int
+pool_calc_installsizechange(Pool *pool, Map *installedmap)
+{
+ Id sp;
+ Solvable *s;
+ int change = 0;
+ Repo *oldinstalled = pool->installed;
+
+ for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++)
+ {
+ if (!s->repo || (oldinstalled && s->repo == oldinstalled))
+ continue;
+ if (!MAPTST(installedmap, sp))
+ continue;
+ change += solvable_lookup_sizek(s, SOLVABLE_INSTALLSIZE, 0);
+ }
+ if (oldinstalled)
+ {
+ FOR_REPO_SOLVABLES(oldinstalled, sp, s)
+ {
+ if (MAPTST(installedmap, sp))
+ continue;
+ change -= solvable_lookup_sizek(s, SOLVABLE_INSTALLSIZE, 0);
+ }
+ }
+ return change;
+}
+
+/* map:
+ * 1: installed
+ * 2: conflicts with installed
+ * 8: interesting (only true if installed)
+ * 16: undecided
+ */
+
+static inline Id dep2name(Pool *pool, Id dep)
+{
+ while (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ dep = rd->name;
+ }
+ return dep;
+}
+
+static int providedbyinstalled_multiversion(Pool *pool, unsigned char *map, Id n, Id con)
+{
+ Id p, pp;
+ Solvable *sn = pool->solvables + n;
+
+ FOR_PROVIDES(p, pp, sn->name)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->name != sn->name || s->arch != sn->arch)
+ continue;
+ if ((map[p] & 9) != 9)
+ continue;
+ if (pool_match_nevr(pool, pool->solvables + p, con))
+ continue;
+ return 1; /* found installed package that doesn't conflict */
+ }
+ return 0;
+}
+
+static inline int providedbyinstalled(Pool *pool, unsigned char *map, Id dep, int ispatch, Map *multiversionmap)
+{
+ Id p, pp;
+ int r = 0;
+ FOR_PROVIDES(p, pp, dep)
+ {
+ if (p == SYSTEMSOLVABLE)
+ return 1; /* always boring, as never constraining */
+ if (ispatch && !pool_match_nevr(pool, pool->solvables + p, dep))
+ continue;
+ if (ispatch && multiversionmap && multiversionmap->size && MAPTST(multiversionmap, p) && ISRELDEP(dep))
+ if (providedbyinstalled_multiversion(pool, map, p, dep))
+ continue;
+ if ((map[p] & 9) == 9)
+ return 9;
+ r |= map[p] & 17;
+ }
+ return r;
+}
+
+/*
+ * pool_trivial_installable - calculate if a set of solvables is
+ * trivial installable without any other installs/deinstalls of
+ * packages not belonging to the set.
+ *
+ * the state is returned in the result queue:
+ * 1: solvable is installable without any other package changes
+ * 0: solvable is not installable
+ * -1: solvable is installable, but doesn't constrain any installed packages
+ */
+
+void
+pool_trivial_installable_multiversionmap(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res, Map *multiversionmap)
+{
+ int i, r, m, did;
+ Id p, *dp, con, *conp, req, *reqp;
+ unsigned char *map;
+ Solvable *s;
+
+ map = solv_calloc(pool->nsolvables, 1);
+ for (p = 1; p < pool->nsolvables; p++)
+ {
+ if (!MAPTST(installedmap, p))
+ continue;
+ map[p] |= 9;
+ s = pool->solvables + p;
+ if (!s->conflicts)
+ continue;
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ dp = pool_whatprovides_ptr(pool, con);
+ for (; *dp; dp++)
+ map[p] |= 2; /* XXX: self conflict ? */
+ }
+ }
+ for (i = 0; i < pkgs->count; i++)
+ map[pkgs->elements[i]] = 16;
+
+ for (i = 0, did = 0; did < pkgs->count; i++, did++)
+ {
+ if (i == pkgs->count)
+ i = 0;
+ p = pkgs->elements[i];
+ if ((map[p] & 16) == 0)
+ continue;
+ if ((map[p] & 2) != 0)
+ {
+ map[p] = 2;
+ continue;
+ }
+ s = pool->solvables + p;
+ m = 1;
+ if (s->requires)
+ {
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ continue;
+ r = providedbyinstalled(pool, map, req, 0, 0);
+ if (!r)
+ {
+ /* decided and miss */
+ map[p] = 2;
+ did = 0;
+ break;
+ }
+ if (r == 16)
+ break; /* undecided */
+ m |= r; /* 1 | 9 | 17 */
+ }
+ if (req)
+ continue;
+ if ((m & 9) == 9)
+ m = 9;
+ }
+ if (s->conflicts)
+ {
+ int ispatch = 0; /* see solver.c patch handling */
+
+ if (!strncmp("patch:", pool_id2str(pool, s->name), 6))
+ ispatch = 1;
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ if ((providedbyinstalled(pool, map, con, ispatch, multiversionmap) & 1) != 0)
+ {
+ map[p] = 2;
+ did = 0;
+ break;
+ }
+ if ((m == 1 || m == 17) && ISRELDEP(con))
+ {
+ con = dep2name(pool, con);
+ if ((providedbyinstalled(pool, map, con, ispatch, multiversionmap) & 1) != 0)
+ m = 9;
+ }
+ }
+ if (con)
+ continue; /* found a conflict */
+ }
+#if 0
+ if (s->repo && s->repo != oldinstalled)
+ {
+ Id p2, obs, *obsp, *pp;
+ Solvable *s2;
+ if (s->obsoletes)
+ {
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ if ((providedbyinstalled(pool, map, obs, 0, 0) & 1) != 0)
+ {
+ map[p] = 2;
+ break;
+ }
+ }
+ if (obs)
+ continue;
+ }
+ FOR_PROVIDES(p2, pp, s->name)
+ {
+ s2 = pool->solvables + p2;
+ if (s2->name == s->name && (map[p2] & 1) != 0)
+ {
+ map[p] = 2;
+ break;
+ }
+ }
+ if (p2)
+ continue;
+ }
+#endif
+ if (m != map[p])
+ {
+ map[p] = m;
+ did = 0;
+ }
+ }
+ queue_free(res);
+ queue_init_clone(res, pkgs);
+ for (i = 0; i < pkgs->count; i++)
+ {
+ m = map[pkgs->elements[i]];
+ if ((m & 9) == 9)
+ r = 1;
+ else if (m & 1)
+ r = -1;
+ else
+ r = 0;
+ res->elements[i] = r;
+ }
+ free(map);
+}
+
+void
+pool_trivial_installable(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res)
+{
+ pool_trivial_installable_multiversionmap(pool, installedmap, pkgs, res, 0);
+}
+
+const char *
+pool_lookup_str(Pool *pool, Id entry, Id keyname)
+{
+ if (entry == SOLVID_POS && pool->pos.repo)
+ return repo_lookup_str(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname);
+ if (entry <= 0)
+ return 0;
+ return solvable_lookup_str(pool->solvables + entry, keyname);
+}
+
+Id
+pool_lookup_id(Pool *pool, Id entry, Id keyname)
+{
+ if (entry == SOLVID_POS && pool->pos.repo)
+ return repo_lookup_id(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname);
+ if (entry <= 0)
+ return 0;
+ return solvable_lookup_id(pool->solvables + entry, keyname);
+}
+
+unsigned long long
+pool_lookup_num(Pool *pool, Id entry, Id keyname, unsigned long long notfound)
+{
+ if (entry == SOLVID_POS && pool->pos.repo)
+ return repo_lookup_num(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname, notfound);
+ if (entry <= 0)
+ return notfound;
+ return solvable_lookup_num(pool->solvables + entry, keyname, notfound);
+}
+
+int
+pool_lookup_void(Pool *pool, Id entry, Id keyname)
+{
+ if (entry == SOLVID_POS && pool->pos.repo)
+ return repo_lookup_void(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname);
+ if (entry <= 0)
+ return 0;
+ return solvable_lookup_void(pool->solvables + entry, keyname);
+}
+
+const unsigned char *
+pool_lookup_bin_checksum(Pool *pool, Id entry, Id keyname, Id *typep)
+{
+ if (entry == SOLVID_POS && pool->pos.repo)
+ return repo_lookup_bin_checksum(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname, typep);
+ if (entry <= 0)
+ return 0;
+ return solvable_lookup_bin_checksum(pool->solvables + entry, keyname, typep);
+}
+
+const char *
+pool_lookup_checksum(Pool *pool, Id entry, Id keyname, Id *typep)
+{
+ if (entry == SOLVID_POS && pool->pos.repo)
+ return repo_lookup_checksum(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname, typep);
+ if (entry <= 0)
+ return 0;
+ return solvable_lookup_checksum(pool->solvables + entry, keyname, typep);
+}
+
+int
+pool_lookup_idarray(Pool *pool, Id entry, Id keyname, Queue *q)
+{
+ if (entry == SOLVID_POS && pool->pos.repo)
+ return repo_lookup_idarray(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname, q);
+ if (entry <= 0)
+ return 0;
+ return solvable_lookup_idarray(pool->solvables + entry, keyname, q);
+}
+
+const char *
+pool_lookup_deltalocation(Pool *pool, Id entry, unsigned int *medianrp)
+{
+ const char *loc;
+ if (medianrp)
+ *medianrp = 0;
+ if (entry != SOLVID_POS)
+ return 0;
+ loc = pool_lookup_str(pool, entry, DELTA_LOCATION_DIR);
+ loc = pool_tmpjoin(pool, loc, loc ? "/" : 0, pool_lookup_str(pool, entry, DELTA_LOCATION_NAME));
+ loc = pool_tmpappend(pool, loc, "-", pool_lookup_str(pool, entry, DELTA_LOCATION_EVR));
+ loc = pool_tmpappend(pool, loc, ".", pool_lookup_str(pool, entry, DELTA_LOCATION_SUFFIX));
+ return loc;
+}
+
+static void
+add_new_provider(Pool *pool, Id id, Id p)
+{
+ Queue q;
+ Id *pp;
+
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ id = rd->name;
+ }
+
+ queue_init(&q);
+ for (pp = pool->whatprovidesdata + pool->whatprovides[id]; *pp; pp++)
+ {
+ if (*pp == p)
+ {
+ queue_free(&q);
+ return;
+ }
+ if (*pp > p)
+ {
+ queue_push(&q, p);
+ p = 0;
+ }
+ queue_push(&q, *pp);
+ }
+ if (p)
+ queue_push(&q, p);
+ pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
+ if (id < pool->whatprovidesauxoff)
+ pool->whatprovidesaux[id] = 0; /* sorry */
+ queue_free(&q);
+}
+
+void
+pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts)
+{
+ int hadhashes = pool->relhashtbl ? 1 : 0;
+ Solvable *s;
+ Id fn, p, q, md5;
+ Id id;
+ int i;
+
+ if (!conflicts->count)
+ return;
+ for (i = 0; i < conflicts->count; i += 6)
+ {
+ fn = conflicts->elements[i];
+ p = conflicts->elements[i + 1];
+ md5 = conflicts->elements[i + 2];
+ q = conflicts->elements[i + 4];
+ id = pool_rel2id(pool, fn, md5, REL_FILECONFLICT, 1);
+ s = pool->solvables + p;
+ if (!s->repo)
+ continue;
+ s->provides = repo_addid_dep(s->repo, s->provides, id, SOLVABLE_FILEMARKER);
+ if (pool->whatprovides)
+ add_new_provider(pool, fn, p);
+ if (pool->whatprovides_rel)
+ pool->whatprovides_rel[GETRELID(id)] = 0; /* clear cache */
+ s = pool->solvables + q;
+ if (!s->repo)
+ continue;
+ s->conflicts = repo_addid_dep(s->repo, s->conflicts, id, 0);
+ }
+ if (!hadhashes)
+ pool_freeidhashes(pool);
+}
+
+char *
+pool_prepend_rootdir(Pool *pool, const char *path)
+{
+ if (!path)
+ return 0;
+ if (!pool->rootdir)
+ return solv_strdup(path);
+ return solv_dupjoin(pool->rootdir, "/", *path == '/' ? path + 1 : path);
+}
+
+const char *
+pool_prepend_rootdir_tmp(Pool *pool, const char *path)
+{
+ if (!path)
+ return 0;
+ if (!pool->rootdir)
+ return path;
+ return pool_tmpjoin(pool, pool->rootdir, "/", *path == '/' ? path + 1 : path);
+}
+
+void
+pool_set_rootdir(Pool *pool, const char *rootdir)
+{
+ solv_free(pool->rootdir);
+ pool->rootdir = solv_strdup(rootdir);
+}
+
+const char *
+pool_get_rootdir(Pool *pool)
+{
+ return pool->rootdir;
+}
+
+/* only used in libzypp */
+void
+pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(Pool *, Solvable *, Solvable *))
+{
+ pool->custom_vendorcheck = vendorcheck;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * pool.h
+ *
+ */
+
+#ifndef LIBSOLV_POOL_H
+#define LIBSOLV_POOL_H
+
+#include <stdio.h>
+
+#include "solvversion.h"
+#include "pooltypes.h"
+#include "poolid.h"
+#include "solvable.h"
+#include "bitmap.h"
+#include "queue.h"
+#include "strpool.h"
+
+/* well known ids */
+#include "knownid.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* well known solvable */
+#define SYSTEMSOLVABLE 1
+
+
+/* how many strings to maintain (round robin) */
+#define POOL_TMPSPACEBUF 16
+
+/*----------------------------------------------- */
+
+struct _Repo;
+struct _Repodata;
+struct _Repokey;
+struct _KeyValue;
+
+typedef struct _Datapos {
+ struct _Repo *repo;
+ Id solvid;
+ Id repodataid;
+ Id schema;
+ Id dp;
+} Datapos;
+
+struct _Pool_tmpspace {
+ char *buf[POOL_TMPSPACEBUF];
+ int len[POOL_TMPSPACEBUF];
+ int n;
+};
+
+struct _Pool {
+ void *appdata; /* application private pointer */
+
+ struct _Stringpool ss;
+
+ Reldep *rels; /* table of rels: Id -> Reldep */
+ int nrels; /* number of unique rels */
+
+ struct _Repo **repos;
+ int nrepos; /* repos allocated */
+ int urepos; /* repos in use */
+
+ struct _Repo *installed; /* packages considered installed */
+
+ Solvable *solvables;
+ int nsolvables; /* solvables allocated */
+
+ const char **languages;
+ int nlanguages;
+
+ /* package manager type, deb/rpm */
+ int disttype;
+
+ Id *id2arch; /* map arch ids to scores */
+ unsigned char *id2color; /* map arch ids to colors */
+ Id lastarch; /* last valid entry in id2arch/id2color */
+
+ Queue vendormap; /* map vendor to vendorclasses mask */
+ const char **vendorclasses; /* vendor equivalence classes */
+
+ /* providers data, as two-step indirect list
+ * whatprovides[Id] -> Offset into whatprovidesdata for name
+ * whatprovidesdata[Offset] -> 0-terminated list of solvables providing Id
+ */
+ Offset *whatprovides; /* Offset to providers of a specific name, Id -> Offset */
+ Offset *whatprovides_rel; /* Offset to providers of a specific relation, Id -> Offset */
+
+ Id *whatprovidesdata; /* Ids of solvable providing Id */
+ Offset whatprovidesdataoff; /* next free slot within whatprovidesdata */
+ int whatprovidesdataleft; /* number of 'free slots' within whatprovidesdata */
+
+ /* If nonzero, then consider only the solvables with Ids set in this
+ bitmap for solving. If zero, consider all solvables. */
+ Map *considered;
+
+ /* callback for REL_NAMESPACE dependencies handled by the application */
+ Id (*nscallback)(struct _Pool *, void *data, Id name, Id evr);
+ void *nscallbackdata;
+
+ /* debug mask and callback */
+ int debugmask;
+ void (*debugcallback)(struct _Pool *, void *data, int type, const char *str);
+ void *debugcallbackdata;
+
+ /* load callback */
+ int (*loadcallback)(struct _Pool *, struct _Repodata *, void *);
+ void *loadcallbackdata;
+
+ /* search position */
+ Datapos pos;
+
+ Queue pooljobs; /* fixed jobs, like USERINSTALLED/MULTIVERSION */
+
+#ifdef LIBSOLV_INTERNAL
+ /* flags to tell the library how the installed package manager works */
+ int promoteepoch; /* true: missing epoch is replaced by epoch of dependency */
+ int havedistepoch; /* true: thr release part in the evr may contain a distepoch suffix */
+ int obsoleteusesprovides; /* true: obsoletes are matched against provides, not names */
+ int implicitobsoleteusesprovides; /* true: implicit obsoletes due to same name are matched against provides, not names */
+ int obsoleteusescolors; /* true: obsoletes check arch color */
+ int implicitobsoleteusescolors; /* true: implicit obsoletes check arch color */
+ int noinstalledobsoletes; /* true: ignore obsoletes of installed packages */
+ int forbidselfconflicts; /* true: packages which conflict with itself are not installable */
+ int noobsoletesmultiversion; /* true: obsoletes are ignored for multiversion installs */
+
+ Id noarchid; /* ARCH_NOARCH, ARCH_ALL, ARCH_ANY, ... */
+
+ /* hash for rel unification */
+ Hashtable relhashtbl; /* hashtable: (name,evr,op)Hash -> Id */
+ Hashval relhashmask;
+
+ Id *languagecache;
+ int languagecacheother;
+
+ /* our tmp space string space */
+ struct _Pool_tmpspace tmpspace;
+
+ char *errstr; /* last error string */
+ int errstra; /* allocated space for errstr */
+
+ char *rootdir;
+
+ int (*custom_vendorcheck)(struct _Pool *, Solvable *, Solvable *);
+
+ int addfileprovidesfiltered; /* 1: only use filtered file list for addfileprovides */
+ int addedfileprovides; /* true: application called addfileprovides */
+ Queue lazywhatprovidesq; /* queue to store old whatprovides offsets */
+ int nowhatprovidesaux; /* don't allocate and use the whatprovides aux helper */
+ Offset *whatprovidesaux;
+ Offset whatprovidesauxoff;
+ Id *whatprovidesauxdata;
+ Offset whatprovidesauxdataoff;
+
+#endif
+};
+
+#define DISTTYPE_RPM 0
+#define DISTTYPE_DEB 1
+#define DISTTYPE_ARCH 2
+#define DISTTYPE_HAIKU 3
+
+#define SOLV_FATAL (1<<0)
+#define SOLV_ERROR (1<<1)
+#define SOLV_WARN (1<<2)
+#define SOLV_DEBUG_STATS (1<<3)
+#define SOLV_DEBUG_RULE_CREATION (1<<4)
+#define SOLV_DEBUG_PROPAGATE (1<<5)
+#define SOLV_DEBUG_ANALYZE (1<<6)
+#define SOLV_DEBUG_UNSOLVABLE (1<<7)
+#define SOLV_DEBUG_SOLUTIONS (1<<8)
+#define SOLV_DEBUG_POLICY (1<<9)
+#define SOLV_DEBUG_RESULT (1<<10)
+#define SOLV_DEBUG_JOB (1<<11)
+#define SOLV_DEBUG_SOLVER (1<<12)
+#define SOLV_DEBUG_TRANSACTION (1<<13)
+
+#define SOLV_DEBUG_TO_STDERR (1<<30)
+
+#define POOL_FLAG_PROMOTEEPOCH 1
+#define POOL_FLAG_FORBIDSELFCONFLICTS 2
+#define POOL_FLAG_OBSOLETEUSESPROVIDES 3
+#define POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES 4
+#define POOL_FLAG_OBSOLETEUSESCOLORS 5
+#define POOL_FLAG_NOINSTALLEDOBSOLETES 6
+#define POOL_FLAG_HAVEDISTEPOCH 7
+#define POOL_FLAG_NOOBSOLETESMULTIVERSION 8
+#define POOL_FLAG_ADDFILEPROVIDESFILTERED 9
+#define POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS 10
+#define POOL_FLAG_NOWHATPROVIDESAUX 11
+
+/* ----------------------------------------------- */
+
+
+/* mark dependencies with relation by setting bit31 */
+
+#define MAKERELDEP(id) ((id) | 0x80000000)
+#define ISRELDEP(id) (((id) & 0x80000000) != 0)
+#define GETRELID(id) ((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
+
+#define REL_AND 16
+#define REL_OR 17
+#define REL_WITH 18
+#define REL_NAMESPACE 19
+#define REL_ARCH 20
+#define REL_FILECONFLICT 21
+#define REL_COND 22
+#define REL_COMPAT 23
+#define REL_KIND 24 /* for filters only */
+#define REL_MULTIARCH 25 /* debian multiarch annotation */
+#define REL_ELSE 26 /* only as evr part of REL_COND */
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+# define __attribute__(x)
+#endif
+
+extern Pool *pool_create(void);
+extern void pool_free(Pool *pool);
+extern void pool_freeallrepos(Pool *pool, int reuseids);
+
+extern void pool_setdebuglevel(Pool *pool, int level);
+extern int pool_setdisttype(Pool *pool, int disttype);
+extern int pool_set_flag(Pool *pool, int flag, int value);
+extern int pool_get_flag(Pool *pool, int flag);
+
+extern void pool_debug(Pool *pool, int type, const char *format, ...) __attribute__((format(printf, 3, 4)));
+extern void pool_setdebugcallback(Pool *pool, void (*debugcallback)(struct _Pool *, void *data, int type, const char *str), void *debugcallbackdata);
+extern void pool_setdebugmask(Pool *pool, int mask);
+extern void pool_setloadcallback(Pool *pool, int (*cb)(struct _Pool *, struct _Repodata *, void *), void *loadcbdata);
+extern void pool_setnamespacecallback(Pool *pool, Id (*cb)(struct _Pool *, void *, Id, Id), void *nscbdata);
+extern void pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr);
+
+extern void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(struct _Pool *, Solvable *, Solvable *));
+
+
+extern char *pool_alloctmpspace(Pool *pool, int len);
+extern void pool_freetmpspace(Pool *pool, const char *space);
+extern char *pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const char *str3);
+extern char *pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3);
+extern const char *pool_bin2hex(Pool *pool, const unsigned char *buf, int len);
+
+extern void pool_set_installed(Pool *pool, struct _Repo *repo);
+
+extern int pool_error(Pool *pool, int ret, const char *format, ...) __attribute__((format(printf, 3, 4)));
+extern char *pool_errstr(Pool *pool);
+
+extern void pool_set_rootdir(Pool *pool, const char *rootdir);
+extern const char *pool_get_rootdir(Pool *pool);
+extern char *pool_prepend_rootdir(Pool *pool, const char *dir);
+extern const char *pool_prepend_rootdir_tmp(Pool *pool, const char *dir);
+
+/**
+ * Solvable management
+ */
+extern Id pool_add_solvable(Pool *pool);
+extern Id pool_add_solvable_block(Pool *pool, int count);
+
+extern void pool_free_solvable_block(Pool *pool, Id start, int count, int reuseids);
+static inline Solvable *pool_id2solvable(const Pool *pool, Id p)
+{
+ return pool->solvables + p;
+}
+
+extern const char *pool_solvable2str(Pool *pool, Solvable *s);
+static inline const char *pool_solvid2str(Pool *pool, Id p)
+{
+ return pool_solvable2str(pool, pool->solvables + p);
+}
+
+void pool_set_languages(Pool *pool, const char **languages, int nlanguages);
+Id pool_id2langid(Pool *pool, Id id, const char *lang, int create);
+
+int solvable_trivial_installable_map(Solvable *s, Map *installedmap, Map *conflictsmap, Map *multiversionmap);
+int solvable_trivial_installable_repo(Solvable *s, struct _Repo *installed, Map *multiversionmap);
+int solvable_trivial_installable_queue(Solvable *s, Queue *installed, Map *multiversionmap);
+int solvable_is_irrelevant_patch(Solvable *s, Map *installedmap);
+
+void pool_create_state_maps(Pool *pool, Queue *installed, Map *installedmap, Map *conflictsmap);
+
+int pool_intersect_evrs(Pool *pool, int pflags, Id pevr, int flags, int evr);
+int pool_match_dep(Pool *pool, Id d1, Id d2);
+
+/* semi private, used in pool_match_nevr */
+int pool_match_nevr_rel(Pool *pool, Solvable *s, Id d);
+
+static inline int pool_match_nevr(Pool *pool, Solvable *s, Id d)
+{
+ if (!ISRELDEP(d))
+ return d == s->name;
+ else
+ return pool_match_nevr_rel(pool, s, d);
+}
+
+
+/**
+ * Prepares a pool for solving
+ */
+extern void pool_createwhatprovides(Pool *pool);
+extern void pool_addfileprovides(Pool *pool);
+extern void pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst);
+extern void pool_freewhatprovides(Pool *pool);
+extern Id pool_queuetowhatprovides(Pool *pool, Queue *q);
+extern Id pool_ids2whatprovides(Pool *pool, Id *ids, int count);
+extern Id pool_searchlazywhatprovidesq(Pool *pool, Id d);
+
+extern Id pool_addrelproviders(Pool *pool, Id d);
+
+static inline Id pool_whatprovides(Pool *pool, Id d)
+{
+ if (!ISRELDEP(d))
+ {
+ if (pool->whatprovides[d])
+ return pool->whatprovides[d];
+ }
+ else
+ {
+ Id v = GETRELID(d);
+ if (pool->whatprovides_rel[v])
+ return pool->whatprovides_rel[v];
+ }
+ return pool_addrelproviders(pool, d);
+}
+
+static inline Id *pool_whatprovides_ptr(Pool *pool, Id d)
+{
+ Id off = pool_whatprovides(pool, d);
+ return pool->whatprovidesdata + off;
+}
+
+void pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker);
+
+/* search the pool. the following filters are available:
+ * p - search just this solvable
+ * key - search only this key
+ * match - key must match this string
+ */
+void pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, struct _Repodata *data, struct _Repokey *key, struct _KeyValue *kv), void *cbdata);
+
+void pool_clear_pos(Pool *pool);
+
+
+#define DUCHANGES_ONLYADD 1
+
+typedef struct _DUChanges {
+ const char *path;
+ int kbytes;
+ int files;
+ int flags;
+} DUChanges;
+
+void pool_calc_duchanges(Pool *pool, Map *installedmap, DUChanges *mps, int nmps);
+int pool_calc_installsizechange(Pool *pool, Map *installedmap);
+void pool_trivial_installable(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res);
+void pool_trivial_installable_multiversionmap(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res, Map *multiversionmap);
+
+const char *pool_lookup_str(Pool *pool, Id entry, Id keyname);
+Id pool_lookup_id(Pool *pool, Id entry, Id keyname);
+unsigned long long pool_lookup_num(Pool *pool, Id entry, Id keyname, unsigned long long notfound);
+int pool_lookup_void(Pool *pool, Id entry, Id keyname);
+const unsigned char *pool_lookup_bin_checksum(Pool *pool, Id entry, Id keyname, Id *typep);
+int pool_lookup_idarray(Pool *pool, Id entry, Id keyname, Queue *q);
+const char *pool_lookup_checksum(Pool *pool, Id entry, Id keyname, Id *typep);
+const char *pool_lookup_deltalocation(Pool *pool, Id entry, unsigned int *medianrp);
+
+void pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts);
+
+
+
+/* loop over all providers of d */
+#define FOR_PROVIDES(v, vp, d) \
+ for (vp = pool_whatprovides(pool, d) ; (v = pool->whatprovidesdata[vp++]) != 0; )
+
+/* loop over all repositories */
+#define FOR_REPOS(repoid, r) \
+ for (repoid = 1; repoid < pool->nrepos; repoid++) \
+ if ((r = pool->repos[repoid]) == 0) \
+ continue; \
+ else
+
+#define FOR_POOL_SOLVABLES(p) \
+ for (p = 2; p < pool->nsolvables; p++) \
+ if (pool->solvables[p].repo == 0) \
+ continue; \
+ else
+
+#ifdef ENABLE_COMPS
+#define ISCONDDEP(id) (ISRELDEP(id) && (GETRELDEP(pool, id))->flags == REL_COND)
+#define MODIFYCONDDEP(id, tst) do { Reldep *condrd = GETRELDEP(pool, id); Id condp, condpp; FOR_PROVIDES(condrd->evr, condp, condpp) if (tst) break; id = condp ? condrd->name : 0;} while(0)
+#endif
+
+#define POOL_DEBUG(type, ...) do {if ((pool->debugmask & (type)) != 0) pool_debug(pool, (type), __VA_ARGS__);} while (0)
+#define IF_POOLDEBUG(type) if ((pool->debugmask & (type)) != 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* LIBSOLV_POOL_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * poolarch.c
+ *
+ * create architecture policies
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "poolid.h"
+#include "poolarch.h"
+#include "util.h"
+
+static const char *archpolicies[] = {
+#if defined(FEDORA) || defined(MAGEIA)
+ "x86_64", "x86_64:athlon:i686:i586:i486:i386",
+#else
+ "x86_64", "x86_64:i686:i586:i486:i386",
+#endif
+ "i686", "i686:i586:i486:i386",
+ "i586", "i586:i486:i386",
+ "i486", "i486:i386",
+ "i386", "i386",
+ "s390x", "s390x:s390",
+ "s390", "s390",
+ "ppc64le", "ppc64le",
+ "ppc64", "ppc64:ppc",
+ "ppc", "ppc",
+ "ppc64p7", "ppc64p7:ppc64:ppc",
+ "ia64", "ia64:i686:i586:i486:i386",
+ "aarch64", "aarch64",
+ "armv6hl", "armv6hl",
+ "armv7hnl", "armv7hnl:armv7hl:armv6hl",
+ "armv7hl", "armv7hl:armv6hl",
+ "armv7l", "armv7l:armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
+ "armv6l", "armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
+ "armv5tejl", "armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
+ "armv5tel", "armv5tel:armv5l:armv4tl:armv4l:armv3l",
+ "armv5tl", "armv5l:armv4tl:armv4l:armv3l",
+ "armv5l", "armv5l:armv4tl:armv4l:armv3l",
+ "armv4tl", "armv4tl:armv4l:armv3l",
+ "armv4l", "armv4l:armv3l",
+ "armv3l", "armv3l",
+ "sh3", "sh3",
+ "sh4", "sh4",
+ "sh4a", "sh4a:sh4",
+ "sparc64v", "sparc64v:sparc64:sparcv9v:sparcv9:sparcv8:sparc",
+ "sparc64", "sparc64:sparcv9:sparcv8:sparc",
+ "sparcv9v", "sparcv9v:sparcv9:sparcv8:sparc",
+ "sparcv9", "sparcv9:sparcv8:sparc",
+ "sparcv8", "sparcv8:sparc",
+ "sparc", "sparc",
+ "mips", "mips",
+ "mipsel", "mipsel",
+ "mips64", "mips64",
+ "mips64el", "mips64el",
+ "m68k", "m68k",
+#if defined(FEDORA) || defined(MAGEIA)
+ "ia32e", "ia32e:x86_64:athlon:i686:i586:i486:i386",
+ "athlon", "athlon:i686:i586:i486:i386",
+ "amd64", "amd64:x86_64:athlon:i686:i586:i486:i386",
+ "geode", "geode:i586:i486:i386",
+ "ppc64iseries", "ppc64iseries:ppc64:ppc",
+ "ppc64pseries", "ppc64pseries:ppc64:ppc",
+#endif
+ 0
+};
+
+void
+pool_setarch(Pool *pool, const char *arch)
+{
+ if (arch)
+ {
+ int i;
+
+ /* convert arch to known policy */
+ for (i = 0; archpolicies[i]; i += 2)
+ if (!strcmp(archpolicies[i], arch))
+ break;
+ if (archpolicies[i])
+ arch = archpolicies[i + 1];
+ else
+ arch = "";
+ }
+ pool_setarchpolicy(pool, arch);
+}
+
+/*
+ * we support three relations:
+ *
+ * a = b both architectures a and b are treated as equivalent
+ * a > b a is considered a "better" architecture, the solver
+ * should change from a to b, but must not change from b to a
+ * a : b a is considered a "better" architecture, the solver
+ * must not change the architecture from a to b or b to a
+ */
+void
+pool_setarchpolicy(Pool *pool, const char *arch)
+{
+ unsigned int score = 0x10001;
+ size_t l;
+ char d;
+ Id *id2arch;
+ Id id, lastarch;
+
+ pool->id2arch = solv_free(pool->id2arch);
+ pool->id2color = solv_free(pool->id2color);
+ if (!arch)
+ {
+ pool->lastarch = 0;
+ return;
+ }
+ id = pool->noarchid;
+ lastarch = id + 255;
+ id2arch = solv_calloc(lastarch + 1, sizeof(Id));
+ id2arch[id] = 1; /* the "noarch" class */
+
+ d = 0;
+ while (*arch)
+ {
+ l = strcspn(arch, ":=>");
+ if (l)
+ {
+ id = pool_strn2id(pool, arch, l, 1);
+ if (id > lastarch)
+ {
+ id2arch = solv_realloc2(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;
+ }
+ }
+ arch += l;
+ if ((d = *arch++) == 0)
+ break;
+ }
+ pool->id2arch = id2arch;
+ pool->lastarch = lastarch;
+}
+
+unsigned char
+pool_arch2color_slow(Pool *pool, Id arch)
+{
+ const char *s;
+ unsigned char color;
+
+ if (arch > pool->lastarch)
+ return ARCHCOLOR_ALL;
+ if (!pool->id2color)
+ pool->id2color = solv_calloc(pool->lastarch + 1, 1);
+ s = pool_id2str(pool, arch);
+ if (arch == ARCH_NOARCH || arch == ARCH_ALL || arch == ARCH_ANY)
+ color = ARCHCOLOR_ALL;
+ else if (!strcmp(s, "s390x") || strstr(s, "64"))
+ color = ARCHCOLOR_64;
+ else
+ color = ARCHCOLOR_32;
+ pool->id2color[arch] = color;
+ return color;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef LIBSOLV_POOLARCH_H
+#define LIBSOLV_POOLARCH_H
+
+#include "pool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void pool_setarch(Pool *, const char *);
+extern void pool_setarchpolicy(Pool *, const char *);
+extern unsigned char pool_arch2color_slow(Pool *pool, Id arch);
+
+#define ARCHCOLOR_32 1
+#define ARCHCOLOR_64 2
+#define ARCHCOLOR_ALL 255
+
+static inline unsigned char pool_arch2color(Pool *pool, Id arch)
+{
+ if (arch > pool->lastarch)
+ return ARCHCOLOR_ALL;
+ if (pool->id2color && pool->id2color[arch])
+ return pool->id2color[arch];
+ return pool_arch2color_slow(pool, arch);
+}
+
+static inline int pool_colormatch(Pool *pool, Solvable *s1, Solvable *s2)
+{
+ if (s1->arch == s2->arch)
+ return 1;
+ if ((pool_arch2color(pool, s1->arch) & pool_arch2color(pool, s2->arch)) != 0)
+ return 1;
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_POOLARCH_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * poolid.c
+ *
+ * Id management
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "pool.h"
+#include "poolid.h"
+#include "poolid_private.h"
+#include "util.h"
+
+
+/* intern string into pool, return id */
+
+Id
+pool_str2id(Pool *pool, const char *str, int create)
+{
+ int oldnstrings = pool->ss.nstrings;
+ Id id = stringpool_str2id(&pool->ss, str, create);
+ if (create && pool->whatprovides && oldnstrings != pool->ss.nstrings && (id & WHATPROVIDES_BLOCK) == 0)
+ {
+ /* grow whatprovides array */
+ pool->whatprovides = solv_realloc(pool->whatprovides, (id + (WHATPROVIDES_BLOCK + 1)) * sizeof(Offset));
+ memset(pool->whatprovides + id, 0, (WHATPROVIDES_BLOCK + 1) * sizeof(Offset));
+ }
+ return id;
+}
+
+Id
+pool_strn2id(Pool *pool, const char *str, unsigned int len, int create)
+{
+ int oldnstrings = pool->ss.nstrings;
+ Id id = stringpool_strn2id(&pool->ss, str, len, create);
+ if (create && pool->whatprovides && oldnstrings != pool->ss.nstrings && (id & WHATPROVIDES_BLOCK) == 0)
+ {
+ /* grow whatprovides array */
+ pool->whatprovides = solv_realloc(pool->whatprovides, (id + (WHATPROVIDES_BLOCK + 1)) * sizeof(Offset));
+ memset(pool->whatprovides + id, 0, (WHATPROVIDES_BLOCK + 1) * sizeof(Offset));
+ }
+ return id;
+}
+
+Id
+pool_rel2id(Pool *pool, Id name, Id evr, int flags, int create)
+{
+ Hashval h, hh, hashmask;
+ int i;
+ Id id;
+ Hashtable hashtbl;
+ Reldep *ran;
+
+ hashmask = pool->relhashmask;
+ hashtbl = pool->relhashtbl;
+ ran = pool->rels;
+
+ /* extend hashtable if needed */
+ if ((Hashval)pool->nrels * 2 > hashmask)
+ {
+ solv_free(pool->relhashtbl);
+ pool->relhashmask = hashmask = mkmask(pool->nrels + REL_BLOCK);
+ pool->relhashtbl = hashtbl = solv_calloc(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;
+
+ id = pool->nrels++;
+ /* extend rel space if needed */
+ pool->rels = solv_extend(pool->rels, id, 1, sizeof(Reldep), REL_BLOCK);
+ hashtbl[h] = id;
+ ran = pool->rels + id;
+ ran->name = name;
+ ran->evr = evr;
+ ran->flags = flags;
+
+ /* extend whatprovides_rel if needed */
+ if (pool->whatprovides_rel && (id & WHATPROVIDES_BLOCK) == 0)
+ {
+ pool->whatprovides_rel = solv_realloc2(pool->whatprovides_rel, id + (WHATPROVIDES_BLOCK + 1), sizeof(Offset));
+ memset(pool->whatprovides_rel + id, 0, (WHATPROVIDES_BLOCK + 1) * sizeof(Offset));
+ }
+ return MAKERELDEP(id);
+}
+
+
+/* Id -> String
+ * for rels (returns name only) and strings
+ */
+const char *
+pool_id2str(const Pool *pool, Id id)
+{
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ id = rd->name;
+ }
+ return pool->ss.stringspace + pool->ss.strings[id];
+}
+
+static const char *rels[] = {
+ " ! ",
+ " > ",
+ " = ",
+ " >= ",
+ " < ",
+ " <> ",
+ " <= ",
+ " <=> "
+};
+
+
+/* get operator for RelId */
+const char *
+pool_id2rel(const Pool *pool, Id id)
+{
+ Reldep *rd;
+ if (!ISRELDEP(id))
+ return "";
+ rd = GETRELDEP(pool, id);
+ switch (rd->flags)
+ {
+ /* debian special cases < and > */
+ /* haiku special cases <> (maybe we should use != for the others as well */
+ case 0: case REL_EQ: case REL_GT | REL_EQ:
+ case REL_LT | REL_EQ: case REL_LT | REL_EQ | REL_GT:
+#if !defined(DEBIAN) && !defined(MULTI_SEMANTICS)
+ case REL_LT: case REL_GT:
+#endif
+#if !defined(HAIKU) && !defined(MULTI_SEMANTICS)
+ case REL_LT | REL_GT:
+#endif
+ return rels[rd->flags];
+#if defined(DEBIAN) || defined(MULTI_SEMANTICS)
+ case REL_GT:
+ return pool->disttype == DISTTYPE_DEB ? " >> " : rels[rd->flags];
+ case REL_LT:
+ return pool->disttype == DISTTYPE_DEB ? " << " : rels[rd->flags];
+#endif
+#if defined(HAIKU) || defined(MULTI_SEMANTICS)
+ case REL_LT | REL_GT:
+ return pool->disttype == DISTTYPE_HAIKU ? " != " : rels[rd->flags];
+#endif
+ case REL_AND:
+ return " & ";
+ case REL_OR:
+ return " | ";
+ case REL_WITH:
+ return " + ";
+ case REL_NAMESPACE:
+ return " NAMESPACE "; /* actually not used in dep2str */
+ case REL_ARCH:
+ return ".";
+ case REL_MULTIARCH:
+ return ":";
+ case REL_FILECONFLICT:
+ return " FILECONFLICT ";
+ case REL_COND:
+ return " IF ";
+ case REL_COMPAT:
+ return " compat >= ";
+ case REL_KIND:
+ return " KIND ";
+ case REL_ELSE:
+ return " ELSE ";
+ default:
+ break;
+ }
+ return " ??? ";
+}
+
+
+/* get e:v.r for Id */
+const char *
+pool_id2evr(const Pool *pool, Id id)
+{
+ Reldep *rd;
+ if (!ISRELDEP(id))
+ return "";
+ rd = GETRELDEP(pool, id);
+ if (ISRELDEP(rd->evr))
+ return "(REL)";
+ return pool->ss.stringspace + pool->ss.strings[rd->evr];
+}
+
+static int
+dep2strlen(const Pool *pool, Id id)
+{
+ int l = 0;
+
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ /* add 2 for parens */
+ l += 2 + dep2strlen(pool, rd->name) + strlen(pool_id2rel(pool, id));
+ id = rd->evr;
+ }
+ return l + strlen(pool->ss.stringspace + pool->ss.strings[id]);
+}
+
+static void
+dep2strcpy(const Pool *pool, char *p, Id id, int oldrel)
+{
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ if (oldrel == REL_AND || oldrel == REL_OR || oldrel == REL_WITH)
+ if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH)
+ if (oldrel != rd->flags)
+ {
+ *p++ = '(';
+ dep2strcpy(pool, p, rd->name, rd->flags);
+ p += strlen(p);
+ strcpy(p, pool_id2rel(pool, id));
+ p += strlen(p);
+ dep2strcpy(pool, p, rd->evr, rd->flags);
+ strcat(p, ")");
+ return;
+ }
+ if (rd->flags == REL_KIND)
+ {
+ dep2strcpy(pool, p, rd->evr, rd->flags);
+ p += strlen(p);
+ *p++ = ':';
+ id = rd->name;
+ oldrel = rd->flags;
+ continue;
+ }
+ dep2strcpy(pool, p, rd->name, rd->flags);
+ p += strlen(p);
+ if (rd->flags == REL_NAMESPACE)
+ {
+ *p++ = '(';
+ dep2strcpy(pool, p, rd->evr, rd->flags);
+ strcat(p, ")");
+ return;
+ }
+ if (rd->flags == REL_FILECONFLICT)
+ {
+ *p = 0;
+ return;
+ }
+ strcpy(p, pool_id2rel(pool, id));
+ p += strlen(p);
+ id = rd->evr;
+ oldrel = rd->flags;
+ }
+ strcpy(p, pool->ss.stringspace + pool->ss.strings[id]);
+}
+
+const char *
+pool_dep2str(Pool *pool, Id id)
+{
+ char *p;
+ if (!ISRELDEP(id))
+ return pool->ss.stringspace + pool->ss.strings[id];
+ p = pool_alloctmpspace(pool, dep2strlen(pool, id) + 1);
+ dep2strcpy(pool, p, id, 0);
+ return p;
+}
+
+void
+pool_shrink_strings(Pool *pool)
+{
+ stringpool_shrink(&pool->ss);
+}
+
+void
+pool_shrink_rels(Pool *pool)
+{
+ pool->rels = solv_extend_resize(pool->rels, pool->nrels, sizeof(Reldep), REL_BLOCK);
+}
+
+/* free all hash tables */
+void
+pool_freeidhashes(Pool *pool)
+{
+ stringpool_freehash(&pool->ss);
+ pool->relhashtbl = solv_free(pool->relhashtbl);
+ pool->relhashmask = 0;
+}
+
+/* EOF */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * poolid.h
+ *
+ */
+
+#ifndef LIBSOLV_POOLID_H
+#define LIBSOLV_POOLID_H
+
+#include "pooltypes.h"
+#include "hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-----------------------------------------------
+ * Ids with relation
+ */
+
+typedef struct _Reldep {
+ Id name; /* "package" */
+ Id evr; /* "0:42-3" */
+ int flags; /* operation/relation, see REL_x in pool.h */
+} Reldep;
+
+extern Id pool_str2id(Pool *pool, const char *, int);
+extern Id pool_strn2id(Pool *pool, const char *, unsigned int, int);
+extern Id pool_rel2id(Pool *pool, Id, Id, int, int);
+extern const char *pool_id2str(const Pool *pool, Id);
+extern const char *pool_id2rel(const Pool *pool, Id);
+extern const char *pool_id2evr(const Pool *pool, Id);
+extern const char *pool_dep2str(Pool *pool, Id); /* might alloc tmpspace */
+
+extern void pool_shrink_strings(Pool *pool);
+extern void pool_shrink_rels(Pool *pool);
+extern void pool_freeidhashes(Pool *pool);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_POOLID_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * poolid_private.h
+ *
+ */
+
+#ifndef LIBSOLV_POOLID_PRIVATE_H
+#define LIBSOLV_POOLID_PRIVATE_H
+
+/* the size of all buffers is incremented in blocks
+ * these are the block values (increment values) for the
+ * rel hashtable
+ */
+#define REL_BLOCK 1023 /* hashtable for relations */
+#define WHATPROVIDES_BLOCK 1023
+
+#endif /* LIBSOLV_POOLID_PRIVATE_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * pooltypes.h
+ *
+ */
+
+#ifndef LIBSOLV_POOLTYPES_H
+#define LIBSOLV_POOLTYPES_H
+
+/* format version number for .solv files */
+#define SOLV_VERSION_0 0
+#define SOLV_VERSION_1 1
+#define SOLV_VERSION_2 2
+#define SOLV_VERSION_3 3
+#define SOLV_VERSION_4 4
+#define SOLV_VERSION_5 5
+#define SOLV_VERSION_6 6
+#define SOLV_VERSION_7 7
+#define SOLV_VERSION_8 8
+
+#define SOLV_FLAG_PREFIX_POOL 4
+#define SOLV_FLAG_SIZE_BYTES 8
+
+struct _Stringpool;
+typedef struct _Stringpool Stringpool;
+
+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 /* LIBSOLV_POOLTYPES_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* we need FNM_CASEFOLD */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <fnmatch.h>
+
+#include "pool.h"
+#include "poolid.h"
+#include "poolvendor.h"
+#include "util.h"
+
+/*
+ * const char *vendorsclasses[] = {
+ * "!openSUSE Build Service*",
+ * "SUSE*",
+ * "openSUSE*",
+ * "SGI*",
+ * "Novell*",
+ * "Silicon Graphics*",
+ * "Jpackage Project*",
+ * "ATI Technologies Inc.*",
+ * "Nvidia*",
+ * 0,
+ * 0,
+ * };
+ */
+
+/* allows for 32 different vendor classes */
+
+Id pool_vendor2mask(Pool *pool, Id vendor)
+{
+ const char *vstr;
+ int i;
+ Id mask, m;
+ const char **v, *vs;
+
+ if (vendor == 0 || !pool->vendorclasses)
+ return 0;
+ for (i = 0; i < pool->vendormap.count; i += 2)
+ if (pool->vendormap.elements[i] == vendor)
+ return pool->vendormap.elements[i + 1];
+ vstr = pool_id2str(pool, vendor);
+ m = 1;
+ mask = 0;
+ for (v = pool->vendorclasses; ; v++)
+ {
+ vs = *v;
+ if (vs == 0) /* end of block? */
+ {
+ v++;
+ if (*v == 0)
+ break;
+ if (m == (1 << 31))
+ break; /* sorry, out of bits */
+ m <<= 1; /* next vendor equivalence class */
+ }
+ if (fnmatch(*vs == '!' ? vs + 1 : vs, vstr, FNM_CASEFOLD) == 0)
+ {
+ if (*vs != '!')
+ mask |= m;
+ while (v[1]) /* forward to next block */
+ v++;
+ }
+ }
+ queue_push(&pool->vendormap, vendor);
+ queue_push(&pool->vendormap, mask);
+ return mask;
+}
+
+void
+pool_setvendorclasses(Pool *pool, const char **vendorclasses)
+{
+ int i;
+ const char **v;
+
+ if (pool->vendorclasses)
+ {
+ for (v = pool->vendorclasses; v[0] || v[1]; v++)
+ solv_free((void *)*v);
+ pool->vendorclasses = solv_free((void *)pool->vendorclasses);
+ }
+ if (!vendorclasses || !vendorclasses[0])
+ return;
+ for (v = vendorclasses; v[0] || v[1]; v++)
+ ;
+ pool->vendorclasses = solv_calloc(v - vendorclasses + 2, sizeof(const char *));
+ for (v = vendorclasses, i = 0; v[0] || v[1]; v++, i++)
+ pool->vendorclasses[i] = *v ? solv_strdup(*v) : 0;
+ pool->vendorclasses[i++] = 0;
+ pool->vendorclasses[i] = 0;
+ queue_empty(&pool->vendormap);
+}
+
+void
+pool_addvendorclass(Pool *pool, const char **vendorclass)
+{
+ int i, j;
+
+ if (!vendorclass || !vendorclass[0])
+ return;
+ for (j = 1; vendorclass[j]; j++)
+ ;
+ i = 0;
+ if (pool->vendorclasses)
+ {
+ for (i = 0; pool->vendorclasses[i] || pool->vendorclasses[i + 1]; i++)
+ ;
+ if (i)
+ i++;
+ }
+ pool->vendorclasses = solv_realloc2((void *)pool->vendorclasses, i + j + 2, sizeof(const char *));
+ for (j = 0; vendorclass[j]; j++)
+ pool->vendorclasses[i++] = solv_strdup(vendorclass[j]);
+ pool->vendorclasses[i++] = 0;
+ pool->vendorclasses[i] = 0;
+ queue_empty(&pool->vendormap);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef LIBSOLV_POOLVENDOR_H
+#define LIBSOLV_POOLVENDOR_H
+
+#include "pool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Id pool_vendor2mask(Pool *pool, Id vendor);
+void pool_setvendorclasses(Pool *pool, const char **vendorclasses);
+void pool_addvendorclass(Pool *pool, const char **vendorclass);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_POOLVENDOR_H */
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * problems.c
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "solver.h"
+#include "solver_private.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "util.h"
+#include "evr.h"
+#include "solverdebug.h"
+
+
+/**********************************************************************************/
+
+/* a problem is an item on the solver's problem list. It can either be >0, in that
+ * case it is a update/infarch/dup rule, or it can be <0, which makes it refer to a job
+ * consisting of multiple job rules.
+ */
+
+void
+solver_disableproblem(Solver *solv, Id v)
+{
+ Rule *r;
+ int i;
+ Id *jp;
+
+ if (v > 0)
+ {
+ if (v >= solv->infarchrules && v < solv->infarchrules_end)
+ {
+ Pool *pool = solv->pool;
+ Id name = pool->solvables[-solv->rules[v].p].name;
+ while (v > solv->infarchrules && pool->solvables[-solv->rules[v - 1].p].name == name)
+ v--;
+ for (; v < solv->infarchrules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
+ solver_disablerule(solv, solv->rules + v);
+ return;
+ }
+ if (v >= solv->duprules && v < solv->duprules_end)
+ {
+ Pool *pool = solv->pool;
+ Id name = pool->solvables[-solv->rules[v].p].name;
+ while (v > solv->duprules && pool->solvables[-solv->rules[v - 1].p].name == name)
+ v--;
+ for (; v < solv->duprules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
+ solver_disablerule(solv, solv->rules + v);
+ return;
+ }
+ solver_disablerule(solv, solv->rules + v);
+#if 0
+ /* XXX: doesn't work */
+ if (v >= solv->updaterules && v < solv->updaterules_end)
+ {
+ /* enable feature rule if we disabled the update rule */
+ r = solv->rules + (v - solv->updaterules + solv->featurerules);
+ if (r->p)
+ solver_enablerule(solv, r);
+ }
+#endif
+ return;
+ }
+ v = -(v + 1);
+ jp = solv->ruletojob.elements;
+ for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
+ if (*jp == v)
+ solver_disablerule(solv, r);
+}
+
+/*-------------------------------------------------------------------
+ * enableproblem
+ */
+
+void
+solver_enableproblem(Solver *solv, Id v)
+{
+ Rule *r;
+ int i;
+ Id *jp;
+
+ if (v > 0)
+ {
+ if (v >= solv->infarchrules && v < solv->infarchrules_end)
+ {
+ Pool *pool = solv->pool;
+ Id name = pool->solvables[-solv->rules[v].p].name;
+ while (v > solv->infarchrules && pool->solvables[-solv->rules[v - 1].p].name == name)
+ v--;
+ for (; v < solv->infarchrules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
+ solver_enablerule(solv, solv->rules + v);
+ return;
+ }
+ if (v >= solv->duprules && v < solv->duprules_end)
+ {
+ Pool *pool = solv->pool;
+ Id name = pool->solvables[-solv->rules[v].p].name;
+ while (v > solv->duprules && pool->solvables[-solv->rules[v - 1].p].name == name)
+ v--;
+ for (; v < solv->duprules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
+ solver_enablerule(solv, solv->rules + v);
+ return;
+ }
+ if (v >= solv->featurerules && v < solv->featurerules_end)
+ {
+ /* do not enable feature rule if update rule is enabled */
+ r = solv->rules + (v - solv->featurerules + solv->updaterules);
+ if (r->d >= 0)
+ return;
+ }
+ solver_enablerule(solv, solv->rules + v);
+ if (v >= solv->updaterules && v < solv->updaterules_end)
+ {
+ /* disable feature rule when enabling update rule */
+ r = solv->rules + (v - solv->updaterules + solv->featurerules);
+ if (r->p)
+ solver_disablerule(solv, r);
+ }
+ return;
+ }
+ v = -(v + 1);
+ jp = solv->ruletojob.elements;
+ for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
+ if (*jp == v)
+ solver_enablerule(solv, r);
+}
+
+
+/*-------------------------------------------------------------------
+ * enable weak rules
+ *
+ * Reenable all disabled weak rules (marked in weakrulemap)
+ *
+ */
+
+static void
+enableweakrules(Solver *solv)
+{
+ int i;
+ Rule *r;
+
+ if (!solv->weakrulemap.size)
+ return;
+ for (i = 1, r = solv->rules + i; i < solv->learntrules; i++, r++)
+ {
+ if (r->d >= 0) /* already enabled? */
+ continue;
+ if (!MAPTST(&solv->weakrulemap, i))
+ continue;
+ solver_enablerule(solv, r);
+ }
+ /* make sure broken orphan rules stay disabled */
+ if (solv->brokenorphanrules)
+ for (i = 0; i < solv->brokenorphanrules->count; i++)
+ solver_disablerule(solv, solv->rules + solv->brokenorphanrules->elements[i]);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * refine_suggestion
+ *
+ * at this point, all rules that led to conflicts are disabled.
+ * we re-enable all rules of a problem set but rule "sug", then
+ * continue to disable more rules until there as again a solution.
+ */
+
+/* FIXME: think about conflicting assertions */
+
+static void
+refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essentialok)
+{
+ Pool *pool = solv->pool;
+ int i, j;
+ Id v;
+ Queue disabled;
+ int disabledcnt;
+
+ IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "refine_suggestion start\n");
+ for (i = 0; problem[i]; i++)
+ {
+ if (problem[i] == sug)
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "=> ");
+ solver_printproblem(solv, problem[i]);
+ }
+ }
+ queue_empty(refined);
+ if (!essentialok && sug < 0 && (solv->job.elements[-sug - 1] & SOLVER_ESSENTIAL) != 0)
+ return;
+ queue_init(&disabled);
+ queue_push(refined, sug);
+
+ /* re-enable all problem rules with the exception of "sug"(gestion) */
+ solver_reset(solv);
+
+ for (i = 0; problem[i]; i++)
+ if (problem[i] != sug)
+ solver_enableproblem(solv, problem[i]);
+
+ if (sug < 0)
+ solver_reenablepolicyrules(solv, -sug);
+ else if (sug >= solv->updaterules && sug < solv->updaterules_end)
+ {
+ /* enable feature rule */
+ Rule *r = solv->rules + solv->featurerules + (sug - solv->updaterules);
+ if (r->p)
+ solver_enablerule(solv, r);
+ }
+
+ enableweakrules(solv);
+
+ for (;;)
+ {
+ int njob, nfeature, nupdate, pass;
+ queue_empty(&solv->problems);
+ solver_reset(solv);
+ solver_run_sat(solv, 0, 0);
+
+ if (!solv->problems.count)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "no more problems!\n");
+ break; /* great, no more problems */
+ }
+ disabledcnt = disabled.count;
+ /* start with 1 to skip over proof index */
+ njob = nfeature = nupdate = 0;
+ for (pass = 0; pass < 2; pass++)
+ {
+ for (i = 1; i < solv->problems.count - 1; i++)
+ {
+ /* ignore solutions in refined */
+ v = solv->problems.elements[i];
+ if (v == 0)
+ break; /* end of problem reached */
+ if (sug != v)
+ {
+ /* check if v is in the given problems list
+ * we allow disabling all problem rules *after* sug in
+ * pass 2, to prevent getting the same solution twice */
+ for (j = 0; problem[j]; j++)
+ if (problem[j] == v || (pass && problem[j] == sug))
+ break;
+ if (problem[j] == v)
+ continue;
+ }
+ if (v >= solv->featurerules && v < solv->featurerules_end)
+ nfeature++;
+ else if (v > 0)
+ nupdate++;
+ else
+ {
+ if (!essentialok && (solv->job.elements[-v - 1] & SOLVER_ESSENTIAL) != 0)
+ continue; /* not that one! */
+ njob++;
+ }
+ queue_push(&disabled, v);
+ }
+ if (disabled.count != disabledcnt)
+ break;
+ }
+ if (disabled.count == disabledcnt)
+ {
+ /* no solution found, this was an invalid suggestion! */
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "no solution found!\n");
+ refined->count = 0;
+ break;
+ }
+ if (!njob && nupdate && nfeature)
+ {
+ /* got only update rules, filter out feature rules */
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "throwing away feature rules\n");
+ for (i = j = disabledcnt; i < disabled.count; i++)
+ {
+ v = disabled.elements[i];
+ if (v < solv->featurerules || v >= solv->featurerules_end)
+ disabled.elements[j++] = v;
+ }
+ disabled.count = j;
+ nfeature = 0;
+ }
+ if (disabled.count == disabledcnt + 1)
+ {
+ /* just one suggestion, add it to refined list */
+ v = disabled.elements[disabledcnt];
+ if (!nfeature && v != sug)
+ queue_push(refined, v); /* do not record feature rules */
+ solver_disableproblem(solv, v);
+ if (v >= solv->updaterules && v < solv->updaterules_end)
+ {
+ Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
+ if (r->p)
+ solver_enablerule(solv, r); /* enable corresponding feature rule */
+ }
+ if (v < 0)
+ solver_reenablepolicyrules(solv, -v);
+ }
+ else
+ {
+ /* more than one solution, disable all */
+ /* do not push anything on refine list, as we do not know which solution to choose */
+ /* thus, the user will get another problem if he selects this solution, where he
+ * can choose the right one */
+ IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "more than one solution found:\n");
+ for (i = disabledcnt; i < disabled.count; i++)
+ solver_printproblem(solv, disabled.elements[i]);
+ }
+ for (i = disabledcnt; i < disabled.count; i++)
+ {
+ v = disabled.elements[i];
+ solver_disableproblem(solv, v);
+ if (v >= solv->updaterules && v < solv->updaterules_end)
+ {
+ Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
+ if (r->p)
+ solver_enablerule(solv, r);
+ }
+ }
+ }
+ }
+ /* all done, get us back into the same state as before */
+ /* enable refined rules again */
+ for (i = 0; i < disabled.count; i++)
+ solver_enableproblem(solv, disabled.elements[i]);
+ queue_free(&disabled);
+ /* reset policy rules */
+ for (i = 0; problem[i]; i++)
+ solver_enableproblem(solv, problem[i]);
+ solver_disablepolicyrules(solv);
+ /* disable problem rules again */
+ for (i = 0; problem[i]; i++)
+ solver_disableproblem(solv, problem[i]);
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "refine_suggestion end\n");
+}
+
+
+/*-------------------------------------------------------------------
+ * sorting helper for problems
+ *
+ * bring update rules before job rules
+ * make essential job rules last
+ */
+
+static int
+problems_sortcmp(const void *ap, const void *bp, void *dp)
+{
+ Queue *job = dp;
+ Id a = *(Id *)ap, b = *(Id *)bp;
+ if (a < 0 && b > 0)
+ return 1;
+ if (a > 0 && b < 0)
+ return -1;
+ if (a < 0 && b < 0)
+ {
+ int af = job->elements[-a - 1] & SOLVER_ESSENTIAL;
+ int bf = job->elements[-b - 1] & SOLVER_ESSENTIAL;
+ int x = af - bf;
+ if (x)
+ return x;
+ }
+ return a - b;
+}
+
+/*
+ * convert a solution rule into a job modifier
+ */
+static void
+convertsolution(Solver *solv, Id why, Queue *solutionq)
+{
+ Pool *pool = solv->pool;
+ if (why < 0)
+ {
+ why = -why;
+ if (why < solv->pooljobcnt)
+ {
+ queue_push(solutionq, SOLVER_SOLUTION_POOLJOB);
+ queue_push(solutionq, why);
+ }
+ else
+ {
+ queue_push(solutionq, SOLVER_SOLUTION_JOB);
+ queue_push(solutionq, why - solv->pooljobcnt);
+ }
+ return;
+ }
+ if (why >= solv->infarchrules && why < solv->infarchrules_end)
+ {
+ Id p, name;
+ /* infarch rule, find replacement */
+ assert(solv->rules[why].p < 0);
+ name = pool->solvables[-solv->rules[why].p].name;
+ while (why > solv->infarchrules && pool->solvables[-solv->rules[why - 1].p].name == name)
+ why--;
+ p = 0;
+ for (; why < solv->infarchrules_end && pool->solvables[-solv->rules[why].p].name == name; why++)
+ if (solv->decisionmap[-solv->rules[why].p] > 0)
+ {
+ p = -solv->rules[why].p;
+ break;
+ }
+ if (!p)
+ return; /* false alarm */
+ queue_push(solutionq, SOLVER_SOLUTION_INFARCH);
+ queue_push(solutionq, p);
+ return;
+ }
+ if (why >= solv->duprules && why < solv->duprules_end)
+ {
+ Id p, name;
+ /* dist upgrade rule, find replacement */
+ assert(solv->rules[why].p < 0);
+ name = pool->solvables[-solv->rules[why].p].name;
+ while (why > solv->duprules && pool->solvables[-solv->rules[why - 1].p].name == name)
+ why--;
+ p = 0;
+ for (; why < solv->duprules_end && pool->solvables[-solv->rules[why].p].name == name; why++)
+ if (solv->decisionmap[-solv->rules[why].p] > 0)
+ {
+ p = -solv->rules[why].p;
+ break;
+ }
+ if (!p)
+ return; /* false alarm */
+ queue_push(solutionq, SOLVER_SOLUTION_DISTUPGRADE);
+ queue_push(solutionq, p);
+ return;
+ }
+ if (why >= solv->updaterules && why < solv->updaterules_end)
+ {
+ /* update rule, find replacement package */
+ Id p, pp, rp = 0;
+ Rule *rr;
+
+ /* check if this is a false positive, i.e. the update rule is fulfilled */
+ rr = solv->rules + why;
+ FOR_RULELITERALS(p, pp, rr)
+ if (p > 0 && solv->decisionmap[p] > 0)
+ return; /* false alarm */
+
+ p = solv->installed->start + (why - solv->updaterules);
+ if (solv->dupmap_all && solv->rules[why].p != p && solv->decisionmap[p] > 0)
+ {
+ /* distupgrade case, allow to keep old package */
+ queue_push(solutionq, SOLVER_SOLUTION_DISTUPGRADE);
+ queue_push(solutionq, p);
+ return;
+ }
+ if (solv->decisionmap[p] > 0)
+ return; /* false alarm, turned out we can keep the package */
+ rr = solv->rules + solv->featurerules + (why - solv->updaterules);
+ if (!rr->p)
+ rr = solv->rules + why;
+ if (rr->w2)
+ {
+ int mvrp = 0; /* multi-version replacement */
+ FOR_RULELITERALS(rp, pp, rr)
+ {
+ if (rp > 0 && solv->decisionmap[rp] > 0 && pool->solvables[rp].repo != solv->installed)
+ {
+ mvrp = rp;
+ if (!(solv->multiversion.size && MAPTST(&solv->multiversion, rp)))
+ break;
+ }
+ }
+ if (!rp && mvrp)
+ {
+ /* found only multi-version replacements */
+ /* have to split solution into two parts */
+ queue_push(solutionq, p);
+ queue_push(solutionq, mvrp);
+ }
+ }
+ queue_push(solutionq, p);
+ queue_push(solutionq, rp);
+ return;
+ }
+ if (why >= solv->bestrules && why < solv->bestrules_end)
+ {
+ int mvrp;
+ Id p, pp, rp = 0;
+ Rule *rr;
+ /* check false positive */
+ rr = solv->rules + why;
+ FOR_RULELITERALS(p, pp, rr)
+ if (p > 0 && solv->decisionmap[p] > 0)
+ return; /* false alarm */
+ /* check update/feature rule */
+ p = solv->bestrules_pkg[why - solv->bestrules];
+ if (p < 0)
+ {
+ /* install job */
+ queue_push(solutionq, 0);
+ queue_push(solutionq, solv->ruletojob.elements[-p - solv->jobrules] + 1);
+ return;
+ }
+ if (solv->decisionmap[p] > 0)
+ {
+ /* disable best rule by keeping the old package */
+ queue_push(solutionq, SOLVER_SOLUTION_BEST);
+ queue_push(solutionq, p);
+ return;
+ }
+ rr = solv->rules + solv->featurerules + (p - solv->installed->start);
+ if (!rr->p)
+ rr = solv->rules + solv->updaterules + (p - solv->installed->start);
+ mvrp = 0; /* multi-version replacement */
+ FOR_RULELITERALS(rp, pp, rr)
+ if (rp > 0 && solv->decisionmap[rp] > 0 && pool->solvables[rp].repo != solv->installed)
+ {
+ mvrp = rp;
+ if (!(solv->multiversion.size && MAPTST(&solv->multiversion, rp)))
+ break;
+ }
+ if (!rp && mvrp)
+ {
+ queue_push(solutionq, SOLVER_SOLUTION_BEST); /* split, see above */
+ queue_push(solutionq, mvrp);
+ queue_push(solutionq, p);
+ queue_push(solutionq, 0);
+ return;
+ }
+ if (rp)
+ {
+ queue_push(solutionq, SOLVER_SOLUTION_BEST);
+ queue_push(solutionq, rp);
+ }
+ return;
+ }
+}
+
+/*
+ * convert problem data into a form usable for refining.
+ * Returns the number of problems.
+ */
+int
+solver_prepare_solutions(Solver *solv)
+{
+ int i, j = 1, idx;
+
+ if (!solv->problems.count)
+ return 0;
+ queue_empty(&solv->solutions);
+ queue_push(&solv->solutions, 0); /* dummy so idx is always nonzero */
+ idx = solv->solutions.count;
+ queue_push(&solv->solutions, -1); /* unrefined */
+ /* proofidx stays in position, thus we start with 1 */
+ for (i = 1; i < solv->problems.count; i++)
+ {
+ Id p = solv->problems.elements[i];
+ queue_push(&solv->solutions, p);
+ if (p)
+ continue;
+ /* end of problem reached */
+ solv->problems.elements[j++] = idx;
+ if (i + 1 >= solv->problems.count)
+ break;
+ /* start another problem */
+ solv->problems.elements[j++] = solv->problems.elements[++i]; /* copy proofidx */
+ idx = solv->solutions.count;
+ queue_push(&solv->solutions, -1); /* unrefined */
+ }
+ solv->problems.count = j;
+ return j / 2;
+}
+
+/*
+ * refine the simple solution rule list provided by
+ * the solver into multiple lists of job modifiers.
+ */
+static void
+create_solutions(Solver *solv, int probnr, int solidx)
+{
+ Pool *pool = solv->pool;
+ Queue redoq;
+ Queue problem, solution, problems_save, branches_save;
+ int i, j, nsol;
+ int essentialok;
+ unsigned int now;
+ int oldmistakes = solv->cleandeps_mistakes ? solv->cleandeps_mistakes->count : 0;
+ Id extraflags = -1;
+ int decisioncnt_update;
+ int decisioncnt_keep;
+ int decisioncnt_resolve;
+ int decisioncnt_weak;
+ int decisioncnt_orphan;
+
+ now = solv_timems(0);
+ queue_init(&redoq);
+ /* save decisionq, decisionq_why, decisionmap, and decisioncnt */
+ for (i = 0; i < solv->decisionq.count; i++)
+ {
+ Id p = solv->decisionq.elements[i];
+ queue_push(&redoq, p);
+ queue_push(&redoq, solv->decisionq_why.elements[i]);
+ queue_push(&redoq, solv->decisionmap[p > 0 ? p : -p]);
+ }
+ decisioncnt_update = solv->decisioncnt_update;
+ decisioncnt_keep = solv->decisioncnt_keep;
+ decisioncnt_resolve = solv->decisioncnt_resolve;
+ decisioncnt_weak = solv->decisioncnt_weak;
+ decisioncnt_orphan = solv->decisioncnt_orphan;
+
+ /* save problems queue */
+ problems_save = solv->problems;
+ memset(&solv->problems, 0, sizeof(solv->problems));
+
+ /* save branches queue */
+ branches_save = solv->problems;
+ memset(&solv->branches, 0, sizeof(solv->branches));
+
+ /* extract problem from queue */
+ queue_init(&problem);
+ for (i = solidx + 1; i < solv->solutions.count; i++)
+ {
+ Id v = solv->solutions.elements[i];
+ if (!v)
+ break;
+ queue_push(&problem, v);
+ if (v < 0)
+ extraflags &= solv->job.elements[-v - 1];
+ }
+ if (extraflags == -1)
+ extraflags = 0;
+ if (problem.count > 1)
+ solv_sort(problem.elements, problem.count, sizeof(Id), problems_sortcmp, &solv->job);
+ queue_push(&problem, 0); /* mark end for refine_suggestion */
+ problem.count--;
+#if 0
+ for (i = 0; i < problem.count; i++)
+ printf("PP %d %d\n", i, problem.elements[i]);
+#endif
+
+ /* refine each solution element */
+ nsol = 0;
+ essentialok = 0;
+ queue_init(&solution);
+ for (i = 0; i < problem.count; i++)
+ {
+ int solstart = solv->solutions.count;
+ refine_suggestion(solv, problem.elements, problem.elements[i], &solution, essentialok);
+ queue_push(&solv->solutions, 0); /* reserve room for number of elements */
+ for (j = 0; j < solution.count; j++)
+ convertsolution(solv, solution.elements[j], &solv->solutions);
+ if (solv->solutions.count == solstart + 1)
+ {
+ solv->solutions.count--; /* this one did not work out */
+ if (nsol || i + 1 < problem.count)
+ continue; /* got one or still hope */
+ if (!essentialok)
+ {
+ /* nothing found, start over */
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "nothing found, re-run with essentialok = 1\n");
+ essentialok = 1;
+ i = -1;
+ continue;
+ }
+ /* this is bad, we found no solution */
+ /* for now just offer a rule */
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "nothing found, already did essentialok, fake it\n");
+ queue_push(&solv->solutions, 0);
+ for (j = 0; j < problem.count; j++)
+ {
+ convertsolution(solv, problem.elements[j], &solv->solutions);
+ if (solv->solutions.count > solstart + 1)
+ break;
+ }
+ if (solv->solutions.count == solstart + 1)
+ {
+ solv->solutions.count--;
+ continue; /* sorry */
+ }
+ }
+ /* patch in number of solution elements */
+ solv->solutions.elements[solstart] = (solv->solutions.count - (solstart + 1)) / 2;
+ queue_push(&solv->solutions, 0); /* add end marker */
+ queue_push(&solv->solutions, 0); /* add end marker */
+ queue_push(&solv->solutions, problem.elements[i]); /* just for bookkeeping */
+ queue_push(&solv->solutions, extraflags & SOLVER_CLEANDEPS); /* our extraflags */
+ solv->solutions.elements[solidx + 1 + nsol++] = solstart;
+ }
+ solv->solutions.elements[solidx + 1 + nsol] = 0; /* end marker */
+ solv->solutions.elements[solidx] = nsol;
+ queue_free(&problem);
+ queue_free(&solution);
+
+ /* restore decisions */
+ memset(solv->decisionmap, 0, pool->nsolvables * sizeof(Id));
+ queue_empty(&solv->decisionq);
+ queue_empty(&solv->decisionq_why);
+ for (i = 0; i < redoq.count; i += 3)
+ {
+ Id p = redoq.elements[i];
+ queue_push(&solv->decisionq, p);
+ queue_push(&solv->decisionq_why, redoq.elements[i + 1]);
+ solv->decisionmap[p > 0 ? p : -p] = redoq.elements[i + 2];
+ }
+ queue_free(&redoq);
+ solv->decisioncnt_update = decisioncnt_update;
+ solv->decisioncnt_keep = decisioncnt_keep;
+ solv->decisioncnt_resolve = decisioncnt_resolve;
+ solv->decisioncnt_weak = decisioncnt_weak;
+ solv->decisioncnt_orphan = decisioncnt_orphan;
+
+ /* restore problems */
+ queue_free(&solv->problems);
+ solv->problems = problems_save;
+
+ /* restore branches */
+ queue_free(&solv->branches);
+ solv->branches = branches_save;
+
+ if (solv->cleandeps_mistakes)
+ {
+ if (oldmistakes)
+ queue_truncate(solv->cleandeps_mistakes, oldmistakes);
+ else
+ {
+ queue_free(solv->cleandeps_mistakes);
+ solv->cleandeps_mistakes = solv_free(solv->cleandeps_mistakes);
+ }
+ }
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "create_solutions for problem #%d took %d ms\n", probnr, solv_timems(now));
+}
+
+
+/**************************************************************************/
+
+unsigned int
+solver_problem_count(Solver *solv)
+{
+ return solv->problems.count / 2;
+}
+
+Id
+solver_next_problem(Solver *solv, Id problem)
+{
+ if (!problem)
+ return solv->problems.count ? 1 : 0;
+ return (problem + 1) * 2 - 1 < solv->problems.count ? problem + 1 : 0;
+}
+
+unsigned int
+solver_solution_count(Solver *solv, Id problem)
+{
+ Id solidx = solv->problems.elements[problem * 2 - 1];
+ if (solv->solutions.elements[solidx] < 0)
+ create_solutions(solv, problem, solidx);
+ return solv->solutions.elements[solidx];
+}
+
+Id
+solver_next_solution(Solver *solv, Id problem, Id solution)
+{
+ Id solidx = solv->problems.elements[problem * 2 - 1];
+ if (solv->solutions.elements[solidx] < 0)
+ create_solutions(solv, problem, solidx);
+ return solv->solutions.elements[solidx + solution + 1] ? solution + 1 : 0;
+}
+
+unsigned int
+solver_solutionelement_count(Solver *solv, Id problem, Id solution)
+{
+ Id solidx = solv->problems.elements[problem * 2 - 1];
+ solidx = solv->solutions.elements[solidx + solution];
+ return solv->solutions.elements[solidx];
+}
+
+Id
+solver_solutionelement_internalid(Solver *solv, Id problem, Id solution)
+{
+ Id solidx = solv->problems.elements[problem * 2 - 1];
+ solidx = solv->solutions.elements[solidx + solution];
+ return solv->solutions.elements[solidx + 2 * solv->solutions.elements[solidx] + 3];
+}
+
+Id
+solver_solutionelement_extrajobflags(Solver *solv, Id problem, Id solution)
+{
+ Id solidx = solv->problems.elements[problem * 2 - 1];
+ solidx = solv->solutions.elements[solidx + solution];
+ return solv->solutions.elements[solidx + 2 * solv->solutions.elements[solidx] + 4];
+}
+
+
+/*
+ * return the next item of the proposed solution
+ * here are the possibilities for p / rp and what
+ * the solver expects the application to do:
+ * p rp
+ * -------------------------------------------------------
+ * SOLVER_SOLUTION_INFARCH pkgid
+ * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
+ * SOLVER_SOLUTION_DISTUPGRADE pkgid
+ * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
+ * SOLVER_SOLUTION_BEST pkgid
+ * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
+ * SOLVER_SOLUTION_JOB jobidx
+ * -> remove job (jobidx - 1, jobidx) from job queue
+ * SOLVER_SOLUTION_POOLJOB jobidx
+ * -> remove job (jobidx - 1, jobidx) from pool job queue
+ * pkgid (> 0) 0
+ * -> add (SOLVER_ERASE|SOLVER_SOLVABLE, p) to the job
+ * pkgid (> 0) pkgid (> 0)
+ * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
+ * (this will replace package p)
+ *
+ * Thus, the solver will either ask the application to remove
+ * a specific job from the job queue, or ask to add an install/erase
+ * job to it.
+ *
+ */
+
+Id
+solver_next_solutionelement(Solver *solv, Id problem, Id solution, Id element, Id *p, Id *rp)
+{
+ Id solidx = solv->problems.elements[problem * 2 - 1];
+ solidx = solv->solutions.elements[solidx + solution];
+ if (!solidx)
+ return 0;
+ solidx += 1 + element * 2;
+ if (!solv->solutions.elements[solidx] && !solv->solutions.elements[solidx + 1])
+ return 0;
+ *p = solv->solutions.elements[solidx];
+ *rp = solv->solutions.elements[solidx + 1];
+ return element + 1;
+}
+
+void
+solver_take_solutionelement(Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job)
+{
+ int i;
+
+ if (p == SOLVER_SOLUTION_POOLJOB)
+ {
+ solv->pool->pooljobs.elements[rp - 1] = SOLVER_NOOP;
+ solv->pool->pooljobs.elements[rp] = 0;
+ return;
+ }
+ if (p == SOLVER_SOLUTION_JOB)
+ {
+ job->elements[rp - 1] = SOLVER_NOOP;
+ job->elements[rp] = 0;
+ return;
+ }
+ if (rp <= 0 && p <= 0)
+ return; /* just in case */
+ if (rp > 0)
+ p = SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_NOTBYUSER|extrajobflags;
+ else
+ {
+ rp = p;
+ p = SOLVER_ERASE|SOLVER_SOLVABLE|extrajobflags;
+ }
+ for (i = 0; i < job->count; i += 2)
+ if (job->elements[i] == p && job->elements[i + 1] == rp)
+ return;
+ queue_push2(job, p, rp);
+}
+
+void
+solver_take_solution(Solver *solv, Id problem, Id solution, Queue *job)
+{
+ Id p, rp, element = 0;
+ Id extrajobflags = solver_solutionelement_extrajobflags(solv, problem, solution);
+ while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
+ solver_take_solutionelement(solv, p, rp, extrajobflags, job);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * find problem rule
+ */
+
+static void
+findproblemrule_internal(Solver *solv, Id idx, Id *reqrp, Id *conrp, Id *sysrp, Id *jobrp, Map *rseen)
+{
+ Id rid, d;
+ Id lreqr, lconr, lsysr, ljobr;
+ Rule *r;
+ Id jobassert = 0;
+ int i, reqset = 0; /* 0: unset, 1: installed, 2: jobassert, 3: assert */
+ int conset = 0; /* 0: unset, 1: installed */
+
+ /* find us a jobassert rule */
+ for (i = idx; (rid = solv->learnt_pool.elements[i]) != 0; i++)
+ {
+ if (rid < solv->jobrules || rid >= solv->jobrules_end)
+ continue;
+ r = solv->rules + rid;
+ d = r->d < 0 ? -r->d - 1 : r->d;
+ if (!d && r->w2 == 0 && r->p > 0)
+ {
+ jobassert = r->p;
+ break;
+ }
+ }
+
+ /* the problem rules are somewhat ordered from "near to the problem" to
+ * "near to the job" */
+ lreqr = lconr = lsysr = ljobr = 0;
+ while ((rid = solv->learnt_pool.elements[idx++]) != 0)
+ {
+ assert(rid > 0);
+ if (rid >= solv->learntrules)
+ {
+ if (MAPTST(rseen, rid - solv->learntrules))
+ continue;
+ MAPSET(rseen, rid - solv->learntrules);
+ findproblemrule_internal(solv, solv->learnt_why.elements[rid - solv->learntrules], &lreqr, &lconr, &lsysr, &ljobr, rseen);
+ }
+ else if ((rid >= solv->jobrules && rid < solv->jobrules_end) || (rid >= solv->infarchrules && rid < solv->infarchrules_end) || (rid >= solv->duprules && rid < solv->duprules_end) || (rid >= solv->bestrules && rid < solv->bestrules_end) || (rid >= solv->yumobsrules && rid <= solv->yumobsrules_end))
+ {
+ if (!*jobrp)
+ *jobrp = rid;
+ }
+ else if (rid >= solv->updaterules && rid < solv->updaterules_end)
+ {
+ if (!*sysrp)
+ *sysrp = rid;
+ }
+ else
+ {
+ assert(rid < solv->pkgrules_end);
+ r = solv->rules + rid;
+ d = r->d < 0 ? -r->d - 1 : r->d;
+ if (!d && r->w2 < 0)
+ {
+ /* prefer conflicts of installed packages */
+ if (solv->installed && !conset)
+ {
+ if (r->p < 0 && (solv->pool->solvables[-r->p].repo == solv->installed ||
+ solv->pool->solvables[-r->w2].repo == solv->installed))
+ {
+ *conrp = rid;
+ conset = 1;
+ }
+ }
+ if (!*conrp)
+ *conrp = rid;
+ }
+ else
+ {
+ if (!d && r->w2 == 0 && reqset < 3)
+ {
+ if (*reqrp > 0 && r->p < -1)
+ {
+ Id op = -solv->rules[*reqrp].p;
+ if (op > 1 && solv->pool->solvables[op].arch != solv->pool->solvables[-r->p].arch)
+ continue; /* different arch, skip */
+ }
+ /* prefer assertions */
+ *reqrp = rid;
+ reqset = 3;
+ }
+ else if (jobassert && r->p == -jobassert)
+ {
+ /* prefer rules of job assertions */
+ *reqrp = rid;
+ reqset = 2;
+ }
+ else if (solv->installed && r->p < 0 && solv->pool->solvables[-r->p].repo == solv->installed && reqset <= 1)
+ {
+ /* prefer rules of job installed package so that the user doesn't get confused by strange packages */
+ *reqrp = rid;
+ reqset = 1;
+ }
+ else if (!*reqrp)
+ *reqrp = rid;
+ }
+ }
+ }
+ if (!*reqrp && lreqr)
+ *reqrp = lreqr;
+ if (!*conrp && lconr)
+ *conrp = lconr;
+ if (!*jobrp && ljobr)
+ *jobrp = ljobr;
+ if (!*sysrp && lsysr)
+ *sysrp = lsysr;
+}
+
+/*
+ * find problem rule
+ *
+ * search for a rule that describes the problem to the
+ * user. Actually a pretty hopeless task that may leave the user
+ * puzzled. To get all of the needed information use
+ * solver_findallproblemrules() instead.
+ */
+
+Id
+solver_findproblemrule(Solver *solv, Id problem)
+{
+ Id reqr, conr, sysr, jobr;
+ Id idx = solv->problems.elements[2 * problem - 2];
+ Map rseen;
+ reqr = conr = sysr = jobr = 0;
+ map_init(&rseen, solv->learntrules ? solv->nrules - solv->learntrules : 0);
+ findproblemrule_internal(solv, idx, &reqr, &conr, &sysr, &jobr, &rseen);
+ map_free(&rseen);
+ /* check if the request is about a not-installed package requiring a installed
+ * package conflicting with the non-installed package. In that case return the conflict */
+ if (reqr && conr && solv->installed && solv->rules[reqr].p < 0 && solv->rules[conr].p < 0 && solv->rules[conr].w2 < 0)
+ {
+ Pool *pool = solv->pool;
+ Solvable *s = pool->solvables - solv->rules[reqr].p;
+ Solvable *s1 = pool->solvables - solv->rules[conr].p;
+ Solvable *s2 = pool->solvables - solv->rules[conr].w2;
+ Id cp = 0;
+ if (s == s1 && s2->repo == solv->installed)
+ cp = -solv->rules[conr].w2;
+ else if (s == s2 && s1->repo == solv->installed)
+ cp = -solv->rules[conr].p;
+ if (cp && s1->name != s2->name && s->repo != solv->installed)
+ {
+ Id p, pp;
+ Rule *r = solv->rules + reqr;
+ FOR_RULELITERALS(p, pp, r)
+ if (p == cp)
+ return conr;
+ }
+ }
+ if (reqr)
+ return reqr; /* some requires */
+ if (conr)
+ return conr; /* some conflict */
+ if (sysr)
+ return sysr; /* an update rule */
+ if (jobr)
+ return jobr; /* a user request */
+ assert(0);
+ return 0;
+}
+
+/*-------------------------------------------------------------------*/
+
+static void
+findallproblemrules_internal(Solver *solv, Id idx, Queue *rules, Map *rseen)
+{
+ Id rid;
+ while ((rid = solv->learnt_pool.elements[idx++]) != 0)
+ {
+ if (rid >= solv->learntrules)
+ {
+ if (MAPTST(rseen, rid - solv->learntrules))
+ continue;
+ MAPSET(rseen, rid - solv->learntrules);
+ findallproblemrules_internal(solv, solv->learnt_why.elements[rid - solv->learntrules], rules, rseen);
+ continue;
+ }
+ queue_pushunique(rules, rid);
+ }
+}
+
+/*
+ * find all problem rule
+ *
+ * return all rules that lead to the problem. This gives the user
+ * all of the information to understand the problem, but the result
+ * can be a large number of rules.
+ */
+
+void
+solver_findallproblemrules(Solver *solv, Id problem, Queue *rules)
+{
+ Map rseen;
+ queue_empty(rules);
+ map_init(&rseen, solv->learntrules ? solv->nrules - solv->learntrules : 0);
+ findallproblemrules_internal(solv, solv->problems.elements[2 * problem - 2], rules, &rseen);
+ map_free(&rseen);
+}
+
+const char *
+solver_problemruleinfo2str(Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep)
+{
+ Pool *pool = solv->pool;
+ char *s;
+ switch (type)
+ {
+ case SOLVER_RULE_DISTUPGRADE:
+ return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not belong to a distupgrade repository", 0);
+ case SOLVER_RULE_INFARCH:
+ return pool_tmpjoin(pool, pool_solvid2str(pool, source), " has inferior architecture", 0);
+ case SOLVER_RULE_UPDATE:
+ return pool_tmpjoin(pool, "problem with installed package ", pool_solvid2str(pool, source), 0);
+ case SOLVER_RULE_JOB:
+ return "conflicting requests";
+ case SOLVER_RULE_JOB_UNSUPPORTED:
+ return "unsupported request";
+ case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
+ return pool_tmpjoin(pool, "nothing provides requested ", pool_dep2str(pool, dep), 0);
+ case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
+ return pool_tmpjoin(pool, "package ", pool_dep2str(pool, dep), " does not exist");
+ case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
+ return pool_tmpjoin(pool, pool_dep2str(pool, dep), " is provided by the system", 0);
+ case SOLVER_RULE_PKG:
+ return "some dependency problem";
+ case SOLVER_RULE_BEST:
+ if (source > 0)
+ return pool_tmpjoin(pool, "cannot install the best update candidate for package ", pool_solvid2str(pool, source), 0);
+ return "cannot install the best candidate for the job";
+ case SOLVER_RULE_PKG_NOT_INSTALLABLE:
+ return pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " is not installable");
+ case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
+ s = pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0);
+ return pool_tmpappend(pool, s, " needed by ", pool_solvid2str(pool, source));
+ case SOLVER_RULE_PKG_SAME_NAME:
+ s = pool_tmpjoin(pool, "cannot install both ", pool_solvid2str(pool, source), 0);
+ return pool_tmpappend(pool, s, " and ", pool_solvid2str(pool, target));
+ case SOLVER_RULE_PKG_CONFLICTS:
+ s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
+ s = pool_tmpappend(pool, s, " conflicts with ", pool_dep2str(pool, dep));
+ return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+ case SOLVER_RULE_PKG_OBSOLETES:
+ s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
+ s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep));
+ return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+ case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+ s = pool_tmpjoin(pool, "installed package ", pool_solvid2str(pool, source), 0);
+ s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep));
+ return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+ case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+ s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
+ s = pool_tmpappend(pool, s, " implicitly obsoletes ", pool_dep2str(pool, dep));
+ return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+ case SOLVER_RULE_PKG_REQUIRES:
+ s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " requires ");
+ return pool_tmpappend(pool, s, pool_dep2str(pool, dep), ", but none of the providers can be installed");
+ case SOLVER_RULE_PKG_SELF_CONFLICT:
+ s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " conflicts with ");
+ return pool_tmpappend(pool, s, pool_dep2str(pool, dep), " provided by itself");
+ case SOLVER_RULE_YUMOBS:
+ s = pool_tmpjoin(pool, "both package ", pool_solvid2str(pool, source), " and ");
+ s = pool_tmpjoin(pool, s, pool_solvid2str(pool, target), " obsolete ");
+ return pool_tmpappend(pool, s, pool_dep2str(pool, dep), 0);
+ default:
+ return "bad problem rule type";
+ }
+}
+
+/* convenience function */
+const char *
+solver_problem2str(Solver *solv, Id problem)
+{
+ Id type, source, target, dep;
+ Id r = solver_findproblemrule(solv, problem);
+ if (!r)
+ return "no problem rule?";
+ type = solver_ruleinfo(solv, r, &source, &target, &dep);
+ return solver_problemruleinfo2str(solv, type, source, target, dep);
+}
+
+const char *
+solver_solutionelement2str(Solver *solv, Id p, Id rp)
+{
+ Pool *pool = solv->pool;
+ if (p == SOLVER_SOLUTION_JOB || p == SOLVER_SOLUTION_POOLJOB)
+ {
+ Id how, what;
+ if (p == SOLVER_SOLUTION_JOB)
+ rp += solv->pooljobcnt;
+ how = solv->job.elements[rp - 1];
+ what = solv->job.elements[rp];
+ return pool_tmpjoin(pool, "do not ask to ", pool_job2str(pool, how, what, 0), 0);
+ }
+ else if (p == SOLVER_SOLUTION_INFARCH)
+ {
+ Solvable *s = pool->solvables + rp;
+ if (solv->installed && s->repo == solv->installed)
+ return pool_tmpjoin(pool, "keep ", pool_solvable2str(pool, s), " despite the inferior architecture");
+ else
+ return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the inferior architecture");
+ }
+ else if (p == SOLVER_SOLUTION_DISTUPGRADE)
+ {
+ Solvable *s = pool->solvables + rp;
+ if (solv->installed && s->repo == solv->installed)
+ return pool_tmpjoin(pool, "keep obsolete ", pool_solvable2str(pool, s), 0);
+ else
+ return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " from excluded repository");
+ }
+ else if (p == SOLVER_SOLUTION_BEST)
+ {
+ Solvable *s = pool->solvables + rp;
+ if (solv->installed && s->repo == solv->installed)
+ return pool_tmpjoin(pool, "keep old ", pool_solvable2str(pool, s), 0);
+ else
+ return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the old version");
+ }
+ else if (p > 0 && rp == 0)
+ return pool_tmpjoin(pool, "allow deinstallation of ", pool_solvid2str(pool, p), 0);
+ else if (p > 0 && rp > 0)
+ {
+ const char *sp = pool_solvid2str(pool, p);
+ const char *srp = pool_solvid2str(pool, rp);
+ const char *str = pool_tmpjoin(pool, "allow replacement of ", sp, 0);
+ return pool_tmpappend(pool, str, " with ", srp);
+ }
+ else
+ return "bad solution element";
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * problems.h
+ *
+ */
+
+#ifndef LIBSOLV_PROBLEMS_H
+#define LIBSOLV_PROBLEMS_H
+
+#include "rules.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct _Solver;
+
+#define SOLVER_SOLUTION_JOB (0)
+#define SOLVER_SOLUTION_DISTUPGRADE (-1)
+#define SOLVER_SOLUTION_INFARCH (-2)
+#define SOLVER_SOLUTION_BEST (-3)
+#define SOLVER_SOLUTION_POOLJOB (-4)
+
+void solver_disableproblem(struct _Solver *solv, Id v);
+void solver_enableproblem(struct _Solver *solv, Id v);
+int solver_prepare_solutions(struct _Solver *solv);
+
+unsigned int solver_problem_count(struct _Solver *solv);
+Id solver_next_problem(struct _Solver *solv, Id problem);
+unsigned int solver_solution_count(struct _Solver *solv, Id problem);
+Id solver_next_solution(struct _Solver *solv, Id problem, Id solution);
+unsigned int solver_solutionelement_count(struct _Solver *solv, Id problem, Id solution);
+Id solver_solutionelement_internalid(struct _Solver *solv, Id problem, Id solution);
+Id solver_solutionelement_extrajobflags(struct _Solver *solv, Id problem, Id solution);
+Id solver_next_solutionelement(struct _Solver *solv, Id problem, Id solution, Id element, Id *p, Id *rp);
+
+void solver_take_solutionelement(struct _Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job);
+void solver_take_solution(struct _Solver *solv, Id problem, Id solution, Queue *job);
+
+Id solver_findproblemrule(struct _Solver *solv, Id problem);
+void solver_findallproblemrules(struct _Solver *solv, Id problem, Queue *rules);
+
+extern const char *solver_problemruleinfo2str(struct _Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep);
+extern const char *solver_problem2str(struct _Solver *solv, Id problem);
+extern const char *solver_solutionelement2str(struct _Solver *solv, Id p, Id rp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * qsort taken from FreeBSD, slightly modified to match glibc's
+ * argument ordering
+ */
+
+/* FIXME: should use mergesort instead */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+
+/* $FreeBSD: src/lib/libc/stdlib/qsort.c,v 1.13.2.1.8.1 2010/12/21 17:10:29 kensmith Exp $ */
+
+#include <stdlib.h>
+
+typedef int cmp_t(const void *, const void *, void *);
+static inline char *med3(char *, char *, char *, cmp_t *, void *);
+static inline void swapfunc(char *, char *, int, int);
+
+#ifndef min
+#define min(a, b) (a) < (b) ? a : b
+#endif
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define swapcode(TYPE, parmi, parmj, n) { \
+ long i = (n) / sizeof (TYPE); \
+ TYPE *pi = (TYPE *) (parmi); \
+ TYPE *pj = (TYPE *) (parmj); \
+ do { \
+ TYPE t = *pi; \
+ *pi++ = *pj; \
+ *pj++ = t; \
+ } while (--i > 0); \
+}
+
+#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static inline void
+swapfunc(a, b, n, swaptype)
+ char *a, *b;
+ int n, swaptype;
+{
+ if(swaptype <= 1)
+ swapcode(long, a, b, n)
+ else
+ swapcode(char, a, b, n)
+}
+
+#define swap(a, b) \
+ if (swaptype == 0) { \
+ long t = *(long *)(a); \
+ *(long *)(a) = *(long *)(b); \
+ *(long *)(b) = t; \
+ } else \
+ swapfunc(a, b, es, swaptype)
+
+#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
+
+#define CMP(t, x, y) (cmp((x), (y), (t)))
+
+static inline char *
+med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk)
+{
+ return CMP(thunk, a, b) < 0 ?
+ (CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a ))
+ :(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c ));
+}
+
+void
+solv_sort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk)
+{
+ char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+ size_t d, r;
+ int cmp_result;
+ int swaptype, swap_cnt;
+
+loop: SWAPINIT(a, es);
+ swap_cnt = 0;
+ if (n < 7) {
+ for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
+ for (pl = pm;
+ pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+ pm = (char *)a + (n / 2) * es;
+ if (n > 7) {
+ pl = a;
+ pn = (char *)a + (n - 1) * es;
+ if (n > 40) {
+ d = (n / 8) * es;
+ pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk);
+ pm = med3(pm - d, pm, pm + d, cmp, thunk);
+ pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk);
+ }
+ pm = med3(pl, pm, pn, cmp, thunk);
+ }
+ swap(a, pm);
+ pa = pb = (char *)a + es;
+
+ pc = pd = (char *)a + (n - 1) * es;
+ for (;;) {
+ while (pb <= pc && (cmp_result = CMP(thunk, pb, a)) <= 0) {
+ if (cmp_result == 0) {
+ swap_cnt = 1;
+ swap(pa, pb);
+ pa += es;
+ }
+ pb += es;
+ }
+ while (pb <= pc && (cmp_result = CMP(thunk, pc, a)) >= 0) {
+ if (cmp_result == 0) {
+ swap_cnt = 1;
+ swap(pc, pd);
+ pd -= es;
+ }
+ pc -= es;
+ }
+ if (pb > pc)
+ break;
+ swap(pb, pc);
+ swap_cnt = 1;
+ pb += es;
+ pc -= es;
+ }
+ if (swap_cnt == 0) { /* Switch to insertion sort */
+ for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
+ for (pl = pm;
+ pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+
+ pn = (char *)a + n * es;
+ r = min(pa - (char *)a, pb - pa);
+ vecswap(a, pb - r, r);
+ r = min(pd - pc, pn - pd - es);
+ vecswap(pb, pn - r, r);
+ if ((r = pb - pa) > es)
+ solv_sort(a, r / es, es, cmp, thunk);
+ if ((r = pd - pc) > es) {
+ /* Iterate rather than recurse to save stack space */
+ a = pn - r;
+ n = r / es;
+ goto loop;
+ }
+/* qsort(pn - r, r / es, es, cmp);*/
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * queue.c
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "queue.h"
+#include "util.h"
+
+#define EXTRA_SPACE 8
+#define EXTRA_SPACE_HEAD 8
+
+void
+queue_init(Queue *q)
+{
+ q->alloc = q->elements = 0;
+ q->count = q->left = 0;
+}
+
+void
+queue_init_clone(Queue *t, Queue *s)
+{
+ if (!s->elements)
+ {
+ t->alloc = t->elements = 0;
+ t->count = t->left = 0;
+ return;
+ }
+ t->alloc = t->elements = solv_malloc2(s->count + EXTRA_SPACE, sizeof(Id));
+ if (s->count)
+ memcpy(t->alloc, s->elements, s->count * sizeof(Id));
+ t->count = s->count;
+ t->left = EXTRA_SPACE;
+}
+
+void
+queue_init_buffer(Queue *q, Id *buf, int size)
+{
+ q->alloc = 0;
+ q->elements = buf;
+ q->count = 0;
+ q->left = size;
+}
+
+void
+queue_free(Queue *q)
+{
+ if (q->alloc)
+ solv_free(q->alloc);
+ q->alloc = q->elements = 0;
+ q->count = q->left = 0;
+}
+
+void
+queue_alloc_one(Queue *q)
+{
+ if (!q->alloc)
+ {
+ q->alloc = solv_malloc2(q->count + EXTRA_SPACE, sizeof(Id));
+ if (q->count)
+ memcpy(q->alloc, q->elements, q->count * sizeof(Id));
+ q->elements = q->alloc;
+ q->left = EXTRA_SPACE;
+ }
+ else if (q->alloc != q->elements)
+ {
+ int l = q->elements - q->alloc;
+ if (q->count)
+ memmove(q->alloc, q->elements, q->count * sizeof(Id));
+ q->elements -= l;
+ q->left += l;
+ }
+ else
+ {
+ q->elements = q->alloc = solv_realloc2(q->alloc, q->count + EXTRA_SPACE, sizeof(Id));
+ q->left = EXTRA_SPACE;
+ }
+}
+
+/* make room for an element in front of queue */
+void
+queue_alloc_one_head(Queue *q)
+{
+ int l;
+ if (!q->alloc || !q->left)
+ queue_alloc_one(q);
+ l = q->left > EXTRA_SPACE_HEAD ? EXTRA_SPACE_HEAD : q->left;
+ if (q->count)
+ memmove(q->elements + l, q->elements, q->count * sizeof(Id));
+ q->elements += l;
+ q->left -= l;
+}
+
+void
+queue_insert(Queue *q, int pos, Id id)
+{
+ queue_push(q, id); /* make room */
+ if (pos < q->count - 1)
+ {
+ memmove(q->elements + pos + 1, q->elements + pos, (q->count - 1 - pos) * sizeof(Id));
+ q->elements[pos] = id;
+ }
+}
+
+void
+queue_delete(Queue *q, int pos)
+{
+ if (pos >= q->count)
+ return;
+ if (pos < q->count - 1)
+ memmove(q->elements + pos, q->elements + pos + 1, (q->count - 1 - pos) * sizeof(Id));
+ q->left++;
+ q->count--;
+}
+
+void
+queue_insert2(Queue *q, int pos, Id id1, Id id2)
+{
+ queue_push(q, id1); /* make room */
+ queue_push(q, id2); /* make room */
+ if (pos < q->count - 2)
+ {
+ memmove(q->elements + pos + 2, q->elements + pos, (q->count - 2 - pos) * sizeof(Id));
+ q->elements[pos] = id1;
+ q->elements[pos + 1] = id2;
+ }
+}
+
+void
+queue_delete2(Queue *q, int pos)
+{
+ if (pos >= q->count)
+ return;
+ if (pos == q->count - 1)
+ {
+ q->left++;
+ q->count--;
+ return;
+ }
+ if (pos < q->count - 2)
+ memmove(q->elements + pos, q->elements + pos + 2, (q->count - 2 - pos) * sizeof(Id));
+ q->left += 2;
+ q->count -= 2;
+}
+
+void
+queue_insertn(Queue *q, int pos, int n, Id *elements)
+{
+ if (n <= 0)
+ return;
+ if (pos > q->count)
+ pos = q->count;
+ if (q->left < n)
+ {
+ int off;
+ if (!q->alloc)
+ queue_alloc_one(q);
+ off = q->elements - q->alloc;
+ q->alloc = solv_realloc2(q->alloc, off + q->count + n + EXTRA_SPACE, sizeof(Id));
+ q->elements = q->alloc + off;
+ q->left = n + EXTRA_SPACE;
+ }
+ if (pos < q->count)
+ memmove(q->elements + pos + n, q->elements + pos, (q->count - pos) * sizeof(Id));
+ if (elements)
+ memcpy(q->elements + pos, elements, n * sizeof(Id));
+ else
+ memset(q->elements + pos, 0, n * sizeof(Id));
+ q->left -= n;
+ q->count += n;
+}
+
+void
+queue_deleten(Queue *q, int pos, int n)
+{
+ if (n <= 0 || pos >= q->count)
+ return;
+ if (pos + n >= q->count)
+ n = q->count - pos;
+ else
+ memmove(q->elements + pos, q->elements + pos + n, (q->count - n - pos) * sizeof(Id));
+ q->left += n;
+ q->count -= n;
+}
+
+/* allocate room for n more elements */
+void
+queue_prealloc(Queue *q, int n)
+{
+ int off;
+ if (n <= 0 || q->left >= n)
+ return;
+ if (!q->alloc)
+ queue_alloc_one(q);
+ off = q->elements - q->alloc;
+ q->alloc = solv_realloc2(q->alloc, off + q->count + n + EXTRA_SPACE, sizeof(Id));
+ q->elements = q->alloc + off;
+ q->left = n + EXTRA_SPACE;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * queue.h
+ *
+ */
+
+#ifndef LIBSOLV_QUEUE_H
+#define LIBSOLV_QUEUE_H
+
+#include "pooltypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _Queue {
+ Id *elements; /* pointer to elements */
+ int count; /* current number of elements in queue */
+ Id *alloc; /* this is whats actually allocated, elements > alloc if shifted */
+ int left; /* space left in alloc *after* elements+count */
+} Queue;
+
+
+extern void queue_alloc_one(Queue *q); /* internal */
+extern void queue_alloc_one_head(Queue *q); /* internal */
+
+/* clear queue */
+static inline void
+queue_empty(Queue *q)
+{
+ if (q->alloc)
+ {
+ q->left += (q->elements - q->alloc) + q->count;
+ q->elements = q->alloc;
+ }
+ else
+ q->left += q->count;
+ q->count = 0;
+}
+
+static inline Id
+queue_shift(Queue *q)
+{
+ if (!q->count)
+ return 0;
+ q->count--;
+ return *q->elements++;
+}
+
+static inline Id
+queue_pop(Queue *q)
+{
+ if (!q->count)
+ return 0;
+ q->left++;
+ return q->elements[--q->count];
+}
+
+static inline void
+queue_unshift(Queue *q, Id id)
+{
+ if (!q->alloc || q->alloc == q->elements)
+ queue_alloc_one_head(q);
+ *--q->elements = id;
+ q->count++;
+}
+
+static inline void
+queue_push(Queue *q, Id id)
+{
+ if (!q->left)
+ queue_alloc_one(q);
+ q->elements[q->count++] = id;
+ q->left--;
+}
+
+static inline void
+queue_pushunique(Queue *q, Id id)
+{
+ int i;
+ for (i = q->count; i > 0; )
+ if (q->elements[--i] == id)
+ return;
+ queue_push(q, id);
+}
+
+static inline void
+queue_push2(Queue *q, Id id1, Id id2)
+{
+ queue_push(q, id1);
+ queue_push(q, id2);
+}
+
+static inline void
+queue_truncate(Queue *q, int n)
+{
+ if (q->count > n)
+ {
+ q->left += q->count - n;
+ q->count = n;
+ }
+}
+
+extern void queue_init(Queue *q);
+extern void queue_init_buffer(Queue *q, Id *buf, int size);
+extern void queue_init_clone(Queue *t, Queue *s);
+extern void queue_free(Queue *q);
+
+extern void queue_insert(Queue *q, int pos, Id id);
+extern void queue_insert2(Queue *q, int pos, Id id1, Id id2);
+extern void queue_insertn(Queue *q, int pos, int n, Id *elements);
+extern void queue_delete(Queue *q, int pos);
+extern void queue_delete2(Queue *q, int pos);
+extern void queue_deleten(Queue *q, int pos, int n);
+extern void queue_prealloc(Queue *q, int n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_QUEUE_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo.c
+ *
+ * Manage metadata coming from one repository
+ *
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <fnmatch.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+#include "repo.h"
+#include "pool.h"
+#include "poolid_private.h"
+#include "util.h"
+#include "chksum.h"
+
+#define IDARRAY_BLOCK 4095
+
+
+/*
+ * create empty repo
+ * and add to pool
+ */
+
+Repo *
+repo_create(Pool *pool, const char *name)
+{
+ Repo *repo;
+
+ pool_freewhatprovides(pool);
+ repo = (Repo *)solv_calloc(1, sizeof(*repo));
+ if (!pool->nrepos)
+ {
+ pool->nrepos = 1; /* start with repoid 1 */
+ pool->repos = (Repo **)solv_calloc(2, sizeof(Repo *));
+ }
+ else
+ pool->repos = (Repo **)solv_realloc2(pool->repos, pool->nrepos + 1, sizeof(Repo *));
+ pool->repos[pool->nrepos] = repo;
+ pool->urepos++;
+ repo->repoid = pool->nrepos++;
+ repo->name = name ? solv_strdup(name) : 0;
+ repo->pool = pool;
+ repo->start = pool->nsolvables;
+ repo->end = pool->nsolvables;
+ repo->nsolvables = 0;
+ return repo;
+}
+
+void
+repo_freedata(Repo *repo)
+{
+ int i;
+ for (i = 1; i < repo->nrepodata; i++)
+ repodata_freedata(repo->repodata + i);
+ solv_free(repo->repodata);
+ solv_free(repo->idarraydata);
+ solv_free(repo->rpmdbid);
+ solv_free(repo->lastidhash);
+ solv_free((char *)repo->name);
+ solv_free(repo);
+}
+
+/* delete all solvables and repodata blocks from this repo */
+
+void
+repo_empty(Repo *repo, int reuseids)
+{
+ Pool *pool = repo->pool;
+ Solvable *s;
+ int i;
+
+ pool_freewhatprovides(pool);
+ if (reuseids && repo->end == pool->nsolvables)
+ {
+ /* it's ok to reuse the ids. As this is the last repo, we can
+ just shrink the solvable array */
+ for (i = repo->end - 1, s = pool->solvables + i; i >= repo->start; i--, s--)
+ if (s->repo != repo)
+ break;
+ pool_free_solvable_block(pool, i + 1, repo->end - (i + 1), reuseids);
+ repo->end = i + 1;
+ }
+ /* zero out (i.e. free) solvables belonging to this repo */
+ for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
+ if (s->repo == repo)
+ memset(s, 0, sizeof(*s));
+ repo->end = repo->start;
+ repo->nsolvables = 0;
+
+ /* free all data belonging to this repo */
+ repo->idarraydata = solv_free(repo->idarraydata);
+ repo->idarraysize = 0;
+ repo->lastoff = 0;
+ repo->rpmdbid = solv_free(repo->rpmdbid);
+ for (i = 1; i < repo->nrepodata; i++)
+ repodata_freedata(repo->repodata + i);
+ solv_free(repo->repodata);
+ repo->repodata = 0;
+ repo->nrepodata = 0;
+}
+
+/*
+ * remove repo from pool, delete solvables
+ *
+ */
+
+void
+repo_free(Repo *repo, int reuseids)
+{
+ Pool *pool = repo->pool;
+ int i;
+
+ if (repo == pool->installed)
+ pool->installed = 0;
+ repo_empty(repo, reuseids);
+ for (i = 1; i < pool->nrepos; i++) /* find repo in pool */
+ if (pool->repos[i] == repo)
+ break;
+ if (i == pool->nrepos) /* repo not in pool, return */
+ return;
+ if (i == pool->nrepos - 1 && reuseids)
+ pool->nrepos--;
+ else
+ pool->repos[i] = 0;
+ pool->urepos--;
+ repo_freedata(repo);
+}
+
+Id
+repo_add_solvable(Repo *repo)
+{
+ Id p = pool_add_solvable(repo->pool);
+ if (!repo->start || repo->start == repo->end)
+ repo->start = repo->end = p;
+ /* warning: sidedata must be extended before adapting start/end */
+ if (repo->rpmdbid)
+ repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, 1);
+ if (p < repo->start)
+ repo->start = p;
+ if (p + 1 > repo->end)
+ repo->end = p + 1;
+ repo->nsolvables++;
+ repo->pool->solvables[p].repo = repo;
+ return p;
+}
+
+Id
+repo_add_solvable_block(Repo *repo, int count)
+{
+ Id p;
+ Solvable *s;
+ if (!count)
+ return 0;
+ p = pool_add_solvable_block(repo->pool, count);
+ if (!repo->start || repo->start == repo->end)
+ repo->start = repo->end = p;
+ /* warning: sidedata must be extended before adapting start/end */
+ if (repo->rpmdbid)
+ repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, count);
+ if (p < repo->start)
+ repo->start = p;
+ if (p + count > repo->end)
+ repo->end = p + count;
+ repo->nsolvables += count;
+ for (s = repo->pool->solvables + p; count--; s++)
+ s->repo = repo;
+ return p;
+}
+
+void
+repo_free_solvable(Repo *repo, Id p, int reuseids)
+{
+ repo_free_solvable_block(repo, p, 1, reuseids);
+}
+
+void
+repo_free_solvable_block(Repo *repo, Id start, int count, int reuseids)
+{
+ Solvable *s;
+ Repodata *data;
+ int i;
+ if (start + count == repo->end)
+ repo->end -= count;
+ repo->nsolvables -= count;
+ for (s = repo->pool->solvables + start, i = count; i--; s++)
+ s->repo = 0;
+ pool_free_solvable_block(repo->pool, start, count, reuseids);
+ FOR_REPODATAS(repo, i, data)
+ {
+ int dstart, dend;
+ if (data->end > repo->end)
+ repodata_shrink(data, repo->end);
+ dstart = data->start > start ? data->start : start;
+ dend = data->end < start + count ? data->end : start + count;
+ if (dstart < dend)
+ {
+ if (data->attrs)
+ {
+ int j;
+ for (j = dstart; j < dend; j++)
+ data->attrs[j - data->start] = solv_free(data->attrs[j - data->start]);
+ }
+ if (data->incoreoffset)
+ memset(data->incoreoffset + (dstart - data->start), 0, (dend - dstart) * sizeof(Id));
+ }
+ }
+}
+
+/* specialized version of repo_add_solvable_block that inserts the new solvable
+ * block before the indicated repo, which gets relocated.
+ * used in repo_add_rpmdb
+ */
+Id
+repo_add_solvable_block_before(Repo *repo, int count, Repo *beforerepo)
+{
+ Pool *pool = repo->pool;
+ Id p;
+ Solvable *s;
+ Repodata *data;
+ int i;
+
+ if (!count || !beforerepo || beforerepo->end != pool->nsolvables || beforerepo->start == beforerepo->end)
+ return repo_add_solvable_block(repo, count);
+ p = beforerepo->start;
+ /* make sure all solvables belong to beforerepo */
+ for (i = p, s = pool->solvables + i; i < beforerepo->end; i++, s++)
+ if (s->repo && s->repo != beforerepo)
+ return repo_add_solvable_block(repo, count);
+ /* now move beforerepo to back */
+ pool_add_solvable_block(pool, count); /* must return beforerepo->end! */
+ memmove(pool->solvables + p + count, pool->solvables + p, (beforerepo->end - p) * sizeof(Solvable));
+ memset(pool->solvables + p, 0, sizeof(Solvable) * count);
+ /* adapt repodata */
+ FOR_REPODATAS(beforerepo, i, data)
+ {
+ if (data->start < p)
+ continue;
+ data->start += count;
+ data->end += count;
+ }
+ beforerepo->start += count;
+ beforerepo->end += count;
+ /* we now have count free solvables at id p */
+ /* warning: sidedata must be extended before adapting start/end */
+ if (repo->rpmdbid)
+ repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, count);
+ if (p < repo->start)
+ repo->start = p;
+ if (p + count > repo->end)
+ repo->end = p + count;
+ repo->nsolvables += count;
+ for (s = pool->solvables + p; count--; s++)
+ s->repo = repo;
+ return p;
+}
+
+
+/* repository sidedata is solvable data allocated on demand.
+ * It is used for data that is normally not present
+ * in the solvable like the rpmdbid.
+ * The solvable allocation funcions need to make sure that
+ * the sidedata gets extended if new solvables get added.
+ */
+
+#define REPO_SIDEDATA_BLOCK 63
+
+void *
+repo_sidedata_create(Repo *repo, size_t size)
+{
+ return solv_calloc_block(repo->end - repo->start, size, REPO_SIDEDATA_BLOCK);
+}
+
+void *
+repo_sidedata_extend(Repo *repo, void *b, size_t size, Id p, int count)
+{
+ int n = repo->end - repo->start;
+ if (p < repo->start)
+ {
+ int d = repo->start - p;
+ b = solv_extend(b, n, d, size, REPO_SIDEDATA_BLOCK);
+ memmove((char *)b + d * size, b, n * size);
+ memset(b, 0, d * size);
+ n += d;
+ }
+ if (p + count > repo->end)
+ {
+ int d = p + count - repo->end;
+ b = solv_extend(b, n, d, size, REPO_SIDEDATA_BLOCK);
+ memset((char *)b + n * size, 0, d * size);
+ }
+ return b;
+}
+
+/*
+ * add Id to idarraydata used to store dependencies
+ * olddeps: old array offset to extend
+ * returns new array offset
+ */
+
+Offset
+repo_addid(Repo *repo, Offset olddeps, Id id)
+{
+ Id *idarray;
+ int idarraysize;
+ int i;
+
+ idarray = repo->idarraydata;
+ idarraysize = repo->idarraysize;
+
+ if (!idarray) /* alloc idarray if not done yet */
+ {
+ idarraysize = 1;
+ idarray = solv_extend_resize(0, 1, sizeof(Id), IDARRAY_BLOCK);
+ idarray[0] = 0;
+ repo->lastoff = 0;
+ }
+
+ if (!olddeps) /* no deps yet */
+ {
+ olddeps = idarraysize;
+ idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
+ }
+ else if (olddeps == repo->lastoff) /* extend at end */
+ idarraysize--;
+ else /* can't extend, copy old */
+ {
+ i = olddeps;
+ olddeps = idarraysize;
+ for (; idarray[i]; i++)
+ {
+ idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
+ idarray[idarraysize++] = idarray[i];
+ }
+ idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
+ }
+
+ idarray[idarraysize++] = id; /* insert Id into array */
+ idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
+ idarray[idarraysize++] = 0; /* ensure NULL termination */
+
+ repo->idarraydata = idarray;
+ repo->idarraysize = idarraysize;
+ repo->lastoff = olddeps;
+
+ return olddeps;
+}
+
+#define REPO_ADDID_DEP_HASHTHRES 64
+#define REPO_ADDID_DEP_HASHMIN 128
+
+/*
+ * Optimization for packages with an excessive amount of provides/requires:
+ * if the number of deps exceed a threshold, we build a hash of the already
+ * seen ids.
+ */
+static Offset
+repo_addid_dep_hash(Repo *repo, Offset olddeps, Id id, Id marker, int size)
+{
+ Id oid, *oidp;
+ int before;
+ Hashval h, hh;
+ Id hid;
+
+ before = 0;
+ if (marker)
+ {
+ if (marker < 0)
+ {
+ marker = -marker;
+ before = 1;
+ }
+ if (marker == id)
+ marker = 0;
+ }
+
+ /* maintain hash and lastmarkerpos */
+ if (repo->lastidhash_idarraysize != repo->idarraysize || (Hashval)size * 2 > repo->lastidhash_mask || repo->lastmarker != marker)
+ {
+ repo->lastmarkerpos = 0;
+ if (size * 2 > (Hashval)repo->lastidhash_mask)
+ {
+ repo->lastidhash_mask = mkmask(size < REPO_ADDID_DEP_HASHMIN ? REPO_ADDID_DEP_HASHMIN : size);
+ repo->lastidhash = solv_realloc2(repo->lastidhash, repo->lastidhash_mask + 1, sizeof(Id));
+ }
+ memset(repo->lastidhash, 0, (repo->lastidhash_mask + 1) * sizeof(Id));
+ for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
+ {
+ h = oid & repo->lastidhash_mask;
+ hh = HASHCHAIN_START;
+ while (repo->lastidhash[h] != 0)
+ h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
+ repo->lastidhash[h] = oid;
+ if (marker && oid == marker)
+ repo->lastmarkerpos = oidp - repo->idarraydata;
+ }
+ repo->lastmarker = marker;
+ repo->lastidhash_idarraysize = repo->idarraysize;
+ }
+
+ /* check the hash! */
+ h = id & repo->lastidhash_mask;
+ hh = HASHCHAIN_START;
+ while ((hid = repo->lastidhash[h]) != 0 && hid != id)
+ h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
+ /* put new element in hash */
+ if (!hid)
+ repo->lastidhash[h] = id;
+ else if (marker == SOLVABLE_FILEMARKER && (!before || !repo->lastmarkerpos))
+ return olddeps;
+ if (marker && !before && !repo->lastmarkerpos)
+ {
+ /* we have to add the marker first */
+ repo->lastmarkerpos = repo->idarraysize - 1;
+ olddeps = repo_addid(repo, olddeps, marker);
+ /* now put marker in hash */
+ h = marker & repo->lastidhash_mask;
+ hh = HASHCHAIN_START;
+ while (repo->lastidhash[h] != 0)
+ h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
+ repo->lastidhash[h] = marker;
+ repo->lastidhash_idarraysize = repo->idarraysize;
+ }
+ if (!hid)
+ {
+ /* new entry, insert in correct position */
+ if (marker && before && repo->lastmarkerpos)
+ {
+ /* need to add it before the marker */
+ olddeps = repo_addid(repo, olddeps, id); /* dummy to make room */
+ memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (repo->idarraysize - repo->lastmarkerpos - 2) * sizeof(Id));
+ repo->idarraydata[repo->lastmarkerpos++] = id;
+ }
+ else
+ {
+ /* just append it to the end */
+ olddeps = repo_addid(repo, olddeps, id);
+ }
+ repo->lastidhash_idarraysize = repo->idarraysize;
+ return olddeps;
+ }
+ /* we already have it in the hash */
+ if (!marker)
+ return olddeps;
+ if (marker == SOLVABLE_FILEMARKER)
+ {
+ /* check if it is in the wrong half */
+ /* (we already made sure that "before" and "lastmarkerpos" are set, see above) */
+ for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++)
+ if (oid == id)
+ break;
+ if (!oid)
+ return olddeps;
+ /* yes, wrong half. copy it over */
+ memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (oidp - (repo->idarraydata + repo->lastmarkerpos)) * sizeof(Id));
+ repo->idarraydata[repo->lastmarkerpos++] = id;
+ return olddeps;
+ }
+ if (before)
+ return olddeps;
+ /* check if it is in the correct half */
+ for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++)
+ if (oid == id)
+ return olddeps;
+ /* nope, copy it over */
+ for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
+ if (oid == id)
+ break;
+ if (!oid)
+ return olddeps; /* should not happen */
+ memmove(oidp, oidp + 1, (repo->idarraydata + repo->idarraysize - oidp - 2) * sizeof(Id));
+ repo->idarraydata[repo->idarraysize - 2] = id;
+ repo->lastmarkerpos--; /* marker has been moved */
+ return olddeps;
+}
+
+/*
+ * add dependency (as Id) to repo, also unifies dependencies
+ * olddeps = offset into idarraydata
+ * marker= 0 for normal dep
+ * marker > 0 add dep after marker
+ * marker < 0 add dep before -marker
+ * returns new start of dependency array
+ */
+Offset
+repo_addid_dep(Repo *repo, Offset olddeps, Id id, Id marker)
+{
+ Id oid, *oidp, *markerp;
+ int before;
+
+ if (!olddeps)
+ {
+ if (marker > 0)
+ olddeps = repo_addid(repo, olddeps, marker);
+ return repo_addid(repo, olddeps, id);
+ }
+
+ /* check if we should use the hash optimization */
+ if (olddeps == repo->lastoff)
+ {
+ int size = repo->idarraysize - 1 - repo->lastoff;
+ if (size >= REPO_ADDID_DEP_HASHTHRES)
+ return repo_addid_dep_hash(repo, olddeps, id, marker, size);
+ }
+
+ before = 0;
+ if (marker)
+ {
+ if (marker < 0)
+ {
+ marker = -marker;
+ before = 1;
+ }
+ if (marker == id)
+ marker = 0;
+ }
+
+ if (!marker)
+ {
+ for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
+ if (oid == id)
+ return olddeps;
+ return repo_addid(repo, olddeps, id);
+ }
+
+ markerp = 0;
+ for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
+ {
+ if (oid == marker)
+ markerp = oidp;
+ else if (oid == id)
+ break;
+ }
+
+ if (oid)
+ {
+ if (marker == SOLVABLE_FILEMARKER)
+ {
+ if (!markerp || !before)
+ return olddeps;
+ /* we found it, but in the second half */
+ memmove(markerp + 1, markerp, (oidp - markerp) * sizeof(Id));
+ *markerp = id;
+ return olddeps;
+ }
+ if (markerp || before)
+ return olddeps;
+ /* we found it, but in the first half */
+ markerp = oidp++;
+ for (; (oid = *oidp) != 0; oidp++)
+ if (oid == marker)
+ break;
+ if (!oid)
+ {
+ /* no marker in array yet */
+ oidp--;
+ if (markerp < oidp)
+ memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
+ *oidp = marker;
+ return repo_addid(repo, olddeps, id);
+ }
+ while (oidp[1])
+ oidp++;
+ memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
+ *oidp = id;
+ return olddeps;
+ }
+ /* id not yet in array */
+ if (!before && !markerp)
+ olddeps = repo_addid(repo, olddeps, marker);
+ else if (before && markerp)
+ {
+ *markerp++ = id;
+ id = *--oidp;
+ if (markerp < oidp)
+ memmove(markerp + 1, markerp, (oidp - markerp) * sizeof(Id));
+ *markerp = marker;
+ }
+ return repo_addid(repo, olddeps, id);
+}
+
+/* return standard marker for the keyname dependency.
+ * 1: return positive marker, -1: return negative marker
+ */
+Id
+solv_depmarker(Id keyname, Id marker)
+{
+ if (marker != 1 && marker != -1)
+ return marker;
+ if (keyname == SOLVABLE_PROVIDES)
+ return marker < 0 ? -SOLVABLE_FILEMARKER : SOLVABLE_FILEMARKER;
+ if (keyname == SOLVABLE_REQUIRES)
+ return marker < 0 ? -SOLVABLE_PREREQMARKER : SOLVABLE_PREREQMARKER;
+ return 0;
+}
+
+/*
+ * reserve Ids
+ * make space for 'num' more dependencies
+ * returns new start of dependency array
+ *
+ * reserved ids will always begin at offset idarraysize
+ */
+Offset
+repo_reserve_ids(Repo *repo, Offset olddeps, int num)
+{
+ num++; /* room for trailing ID_NULL */
+
+ if (!repo->idarraysize) /* ensure buffer space */
+ {
+ repo->idarraysize = 1;
+ repo->idarraydata = solv_extend_resize(0, 1 + num, sizeof(Id), IDARRAY_BLOCK);
+ repo->idarraydata[0] = 0;
+ repo->lastoff = 1;
+ return 1;
+ }
+
+ if (olddeps && olddeps != repo->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 = repo->idarraydata + olddeps; *idend++; ) /* find end */
+ ;
+ count = idend - idstart - 1 + num; /* new size */
+
+ repo->idarraydata = solv_extend(repo->idarraydata, repo->idarraysize, count, sizeof(Id), IDARRAY_BLOCK);
+ /* move old deps to end */
+ olddeps = repo->lastoff = repo->idarraysize;
+ memcpy(repo->idarraydata + olddeps, idstart, count - num);
+ repo->idarraysize = olddeps + count - num;
+
+ return olddeps;
+ }
+
+ if (olddeps) /* appending */
+ repo->idarraysize--;
+
+ /* make room*/
+ repo->idarraydata = solv_extend(repo->idarraydata, repo->idarraysize, num, sizeof(Id), IDARRAY_BLOCK);
+
+ /* appending or new */
+ repo->lastoff = olddeps ? olddeps : repo->idarraysize;
+
+ return repo->lastoff;
+}
+
+
+/***********************************************************************/
+
+/*
+ * some SUSE specific fixups, should go into a separate file
+ */
+
+Offset
+repo_fix_supplements(Repo *repo, Offset provides, Offset supplements, Offset freshens)
+{
+ Pool *pool = repo->pool;
+ Id id, idp, idl;
+ char buf[1024], *p, *dep;
+ int i, l;
+
+ if (provides)
+ {
+ for (i = provides; repo->idarraydata[i]; i++)
+ {
+ id = repo->idarraydata[i];
+ if (ISRELDEP(id))
+ continue;
+ dep = (char *)pool_id2str(pool, id);
+ if (!strncmp(dep, "locale(", 7) && strlen(dep) < sizeof(buf) - 2)
+ {
+ idp = 0;
+ strcpy(buf + 2, dep);
+ dep = buf + 2 + 7;
+ if ((p = strchr(dep, ':')) != 0 && p != dep)
+ {
+ *p++ = 0;
+ idp = pool_str2id(pool, dep, 1);
+ dep = p;
+ }
+ id = 0;
+ while ((p = strchr(dep, ';')) != 0)
+ {
+ if (p == dep)
+ {
+ dep = p + 1;
+ continue;
+ }
+ *p++ = 0;
+ idl = pool_str2id(pool, dep, 1);
+ idl = pool_rel2id(pool, NAMESPACE_LANGUAGE, idl, REL_NAMESPACE, 1);
+ if (id)
+ id = pool_rel2id(pool, id, idl, REL_OR, 1);
+ else
+ id = idl;
+ dep = p;
+ }
+ if (dep[0] && dep[1])
+ {
+ for (p = dep; *p && *p != ')'; p++)
+ ;
+ *p = 0;
+ idl = pool_str2id(pool, dep, 1);
+ idl = pool_rel2id(pool, NAMESPACE_LANGUAGE, idl, REL_NAMESPACE, 1);
+ if (id)
+ id = pool_rel2id(pool, id, idl, REL_OR, 1);
+ else
+ id = idl;
+ }
+ if (idp)
+ id = pool_rel2id(pool, idp, id, REL_AND, 1);
+ if (id)
+ supplements = repo_addid_dep(repo, supplements, id, 0);
+ }
+ else if ((p = strchr(dep, ':')) != 0 && p != dep && p[1] == '/' && strlen(dep) < sizeof(buf))
+ {
+ strcpy(buf, dep);
+ p = buf + (p - dep);
+ *p++ = 0;
+ idp = pool_str2id(pool, buf, 1);
+ /* strip trailing slashes */
+ l = strlen(p);
+ while (l > 1 && p[l - 1] == '/')
+ p[--l] = 0;
+ id = pool_str2id(pool, p, 1);
+ id = pool_rel2id(pool, idp, id, REL_WITH, 1);
+ id = pool_rel2id(pool, NAMESPACE_SPLITPROVIDES, id, REL_NAMESPACE, 1);
+ supplements = repo_addid_dep(repo, supplements, id, 0);
+ }
+ }
+ }
+ if (supplements)
+ {
+ for (i = supplements; repo->idarraydata[i]; i++)
+ {
+ id = repo->idarraydata[i];
+ if (ISRELDEP(id))
+ continue;
+ dep = (char *)pool_id2str(pool, id);
+ if (!strncmp(dep, "system:modalias(", 16))
+ dep += 7;
+ if (!strncmp(dep, "modalias(", 9) && dep[9] && dep[10] && strlen(dep) < sizeof(buf))
+ {
+ strcpy(buf, dep);
+ p = strchr(buf + 9, ':');
+ if (p && p != buf + 9 && strchr(p + 1, ':'))
+ {
+ *p++ = 0;
+ idp = pool_str2id(pool, buf + 9, 1);
+ p[strlen(p) - 1] = 0;
+ id = pool_str2id(pool, p, 1);
+ id = pool_rel2id(pool, NAMESPACE_MODALIAS, id, REL_NAMESPACE, 1);
+ id = pool_rel2id(pool, idp, id, REL_AND, 1);
+ }
+ else
+ {
+ p = buf + 9;
+ p[strlen(p) - 1] = 0;
+ id = pool_str2id(pool, p, 1);
+ id = pool_rel2id(pool, NAMESPACE_MODALIAS, id, REL_NAMESPACE, 1);
+ }
+ if (id)
+ repo->idarraydata[i] = id;
+ }
+ else if (!strncmp(dep, "packageand(", 11) && strlen(dep) < sizeof(buf))
+ {
+ strcpy(buf, dep);
+ id = 0;
+ dep = buf + 11;
+ while ((p = strchr(dep, ':')) != 0)
+ {
+ if (p == dep)
+ {
+ dep = p + 1;
+ continue;
+ }
+ /* argh, allow pattern: prefix. sigh */
+ if (p - dep == 7 && !strncmp(dep, "pattern", 7))
+ {
+ p = strchr(p + 1, ':');
+ if (!p)
+ break;
+ }
+ *p++ = 0;
+ idp = pool_str2id(pool, dep, 1);
+ if (id)
+ id = pool_rel2id(pool, id, idp, REL_AND, 1);
+ else
+ id = idp;
+ dep = p;
+ }
+ if (dep[0] && dep[1])
+ {
+ dep[strlen(dep) - 1] = 0;
+ idp = pool_str2id(pool, dep, 1);
+ if (id)
+ id = pool_rel2id(pool, id, idp, REL_AND, 1);
+ else
+ id = idp;
+ }
+ if (id)
+ repo->idarraydata[i] = id;
+ }
+ else if (!strncmp(dep, "filesystem(", 11) && strlen(dep) < sizeof(buf))
+ {
+ strcpy(buf, dep + 11);
+ if ((p = strrchr(buf, ')')) != 0)
+ *p = 0;
+ id = pool_str2id(pool, buf, 1);
+ id = pool_rel2id(pool, NAMESPACE_FILESYSTEM, id, REL_NAMESPACE, 1);
+ repo->idarraydata[i] = id;
+ }
+ }
+ }
+ if (freshens && repo->idarraydata[freshens])
+ {
+ Id idsupp = 0, idfresh = 0;
+ if (!supplements || !repo->idarraydata[supplements])
+ return freshens;
+ for (i = supplements; repo->idarraydata[i]; i++)
+ {
+ if (!idsupp)
+ idsupp = repo->idarraydata[i];
+ else
+ idsupp = pool_rel2id(pool, idsupp, repo->idarraydata[i], REL_OR, 1);
+ }
+ for (i = freshens; repo->idarraydata[i]; i++)
+ {
+ if (!idfresh)
+ idfresh = repo->idarraydata[i];
+ else
+ idfresh = pool_rel2id(pool, idfresh, repo->idarraydata[i], REL_OR, 1);
+ }
+ if (!idsupp)
+ idsupp = idfresh;
+ else
+ idsupp = pool_rel2id(pool, idsupp, idfresh, REL_AND, 1);
+ supplements = repo_addid_dep(repo, 0, idsupp, 0);
+ }
+ return supplements;
+}
+
+Offset
+repo_fix_conflicts(Repo *repo, Offset conflicts)
+{
+ char buf[1024], *p, *dep;
+ Pool *pool = repo->pool;
+ Id id;
+ int i;
+
+ if (!conflicts)
+ return conflicts;
+ for (i = conflicts; repo->idarraydata[i]; i++)
+ {
+ id = repo->idarraydata[i];
+ if (ISRELDEP(id))
+ continue;
+ dep = (char *)pool_id2str(pool, id);
+ if (!strncmp(dep, "otherproviders(", 15) && strlen(dep) < sizeof(buf) - 2)
+ {
+ strcpy(buf, dep + 15);
+ if ((p = strchr(buf, ')')) != 0)
+ *p = 0;
+ id = pool_str2id(pool, buf, 1);
+ id = pool_rel2id(pool, NAMESPACE_OTHERPROVIDERS, id, REL_NAMESPACE, 1);
+ repo->idarraydata[i] = id;
+ }
+ }
+ return conflicts;
+}
+
+/***********************************************************************/
+
+struct matchdata
+{
+ Pool *pool;
+ int flags;
+ Datamatcher matcher;
+ int stop;
+ int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv);
+ void *callback_data;
+};
+
+int
+repo_matchvalue(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
+{
+ struct matchdata *md = cbdata;
+
+ if (md->matcher.match)
+ {
+ const char *str;
+ if (key->name == SOLVABLE_FILELIST && key->type == REPOKEY_TYPE_DIRSTRARRAY && (md->matcher.flags & SEARCH_FILES) != 0)
+ if (!datamatcher_checkbasename(&md->matcher, kv->str))
+ return 0;
+ if (!(str = repodata_stringify(md->pool, data, key, kv, md->flags)))
+ return 0;
+ if (!datamatcher_match(&md->matcher, str))
+ return 0;
+ }
+ md->stop = md->callback(md->callback_data, s, data, key, kv);
+ return md->stop;
+}
+
+
+/* list of all keys we store in the solvable */
+/* also used in the dataiterator code in repodata.c */
+Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
+ { SOLVABLE_NAME, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_ARCH, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_EVR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_VENDOR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_PROVIDES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_OBSOLETES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_CONFLICTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_REQUIRES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_RECOMMENDS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_SUGGESTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+ { SOLVABLE_ENHANCES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+ { RPM_RPMDBID, REPOKEY_TYPE_NUM, 0, KEY_STORAGE_SOLVABLE },
+};
+
+static void
+domatch_idarray(Solvable *s, Id keyname, struct matchdata *md, Id *ida)
+{
+ KeyValue kv;
+ kv.entry = 0;
+ kv.parent = 0;
+ for (; *ida && !md->stop; ida++)
+ {
+ kv.id = *ida;
+ kv.eof = ida[1] ? 0 : 1;
+ repo_matchvalue(md, s, 0, repo_solvablekeys + (keyname - SOLVABLE_NAME), &kv);
+ kv.entry++;
+ }
+}
+
+static void
+repo_search_md(Repo *repo, Id p, Id keyname, struct matchdata *md)
+{
+ KeyValue kv;
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i, j, flags;
+ Solvable *s;
+
+ kv.parent = 0;
+ md->stop = 0;
+ if (!p)
+ {
+ for (p = repo->start, s = repo->pool->solvables + p; p < repo->end; p++, s++)
+ {
+ if (s->repo == repo)
+ repo_search_md(repo, p, keyname, md);
+ if (md->stop > SEARCH_NEXT_SOLVABLE)
+ break;
+ }
+ return;
+ }
+ else if (p < 0)
+ /* The callback only supports solvables, so we can't iterate over the
+ extra things. */
+ return;
+ flags = md->flags;
+ if (!(flags & SEARCH_NO_STORAGE_SOLVABLE))
+ {
+ s = pool->solvables + p;
+ switch(keyname)
+ {
+ case 0:
+ case SOLVABLE_NAME:
+ if (s->name)
+ {
+ kv.id = s->name;
+ repo_matchvalue(md, s, 0, repo_solvablekeys + 0, &kv);
+ }
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_ARCH:
+ if (s->arch)
+ {
+ kv.id = s->arch;
+ repo_matchvalue(md, s, 0, repo_solvablekeys + 1, &kv);
+ }
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_EVR:
+ if (s->evr)
+ {
+ kv.id = s->evr;
+ repo_matchvalue(md, s, 0, repo_solvablekeys + 2, &kv);
+ }
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_VENDOR:
+ if (s->vendor)
+ {
+ kv.id = s->vendor;
+ repo_matchvalue(md, s, 0, repo_solvablekeys + 3, &kv);
+ }
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_PROVIDES:
+ if (s->provides)
+ domatch_idarray(s, SOLVABLE_PROVIDES, md, repo->idarraydata + s->provides);
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_OBSOLETES:
+ if (s->obsoletes)
+ domatch_idarray(s, SOLVABLE_OBSOLETES, md, repo->idarraydata + s->obsoletes);
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_CONFLICTS:
+ if (s->conflicts)
+ domatch_idarray(s, SOLVABLE_CONFLICTS, md, repo->idarraydata + s->conflicts);
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_REQUIRES:
+ if (s->requires)
+ domatch_idarray(s, SOLVABLE_REQUIRES, md, repo->idarraydata + s->requires);
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_RECOMMENDS:
+ if (s->recommends)
+ domatch_idarray(s, SOLVABLE_RECOMMENDS, md, repo->idarraydata + s->recommends);
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_SUPPLEMENTS:
+ if (s->supplements)
+ domatch_idarray(s, SOLVABLE_SUPPLEMENTS, md, repo->idarraydata + s->supplements);
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_SUGGESTS:
+ if (s->suggests)
+ domatch_idarray(s, SOLVABLE_SUGGESTS, md, repo->idarraydata + s->suggests);
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case SOLVABLE_ENHANCES:
+ if (s->enhances)
+ domatch_idarray(s, SOLVABLE_ENHANCES, md, repo->idarraydata + s->enhances);
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ case RPM_RPMDBID:
+ if (repo->rpmdbid)
+ {
+ kv.num = repo->rpmdbid[p - repo->start];
+ kv.num2 = 0;
+ repo_matchvalue(md, s, 0, repo_solvablekeys + (RPM_RPMDBID - SOLVABLE_NAME), &kv);
+ }
+ if (keyname || md->stop > SEARCH_NEXT_KEY)
+ return;
+ break;
+ default:
+ break;
+ }
+ }
+
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (p < data->start || p >= data->end)
+ continue;
+ if (keyname && !repodata_precheck_keyname(data, keyname))
+ continue;
+ if (keyname == SOLVABLE_FILELIST && !(md->flags & SEARCH_COMPLETE_FILELIST))
+ {
+ /* do not search filelist extensions */
+ if (data->state != REPODATA_AVAILABLE)
+ continue;
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ break;
+ if (j == data->nkeys)
+ continue;
+ }
+ if (data->state == REPODATA_STUB)
+ {
+ if (keyname)
+ {
+ for (j = 1; j < data->nkeys; j++)
+ if (keyname == data->keys[j].name)
+ break;
+ if (j == data->nkeys)
+ continue;
+ }
+ /* load it */
+ if (data->loadcallback)
+ data->loadcallback(data);
+ else
+ data->state = REPODATA_ERROR;
+ }
+ if (data->state == REPODATA_ERROR)
+ continue;
+ repodata_search(data, p, keyname, md->flags, repo_matchvalue, md);
+ if (md->stop > SEARCH_NEXT_KEY)
+ break;
+ }
+}
+
+void
+repo_search(Repo *repo, Id p, Id keyname, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
+{
+ struct matchdata md;
+
+ if (repo->disabled && !(flags & SEARCH_DISABLED_REPOS))
+ return;
+ memset(&md, 0, sizeof(md));
+ md.pool = repo->pool;
+ md.flags = flags;
+ md.callback = callback;
+ md.callback_data = cbdata;
+ if (match)
+ datamatcher_init(&md.matcher, match, flags);
+ repo_search_md(repo, p, keyname, &md);
+ if (match)
+ datamatcher_free(&md.matcher);
+}
+
+const char *
+repo_lookup_str(Repo *repo, Id entry, Id keyname)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i;
+ const char *str;
+
+ if (entry >= 0)
+ {
+ switch (keyname)
+ {
+ case SOLVABLE_NAME:
+ return pool_id2str(pool, pool->solvables[entry].name);
+ case SOLVABLE_ARCH:
+ return pool_id2str(pool, pool->solvables[entry].arch);
+ case SOLVABLE_EVR:
+ return pool_id2str(pool, pool->solvables[entry].evr);
+ case SOLVABLE_VENDOR:
+ return pool_id2str(pool, pool->solvables[entry].vendor);
+ }
+ }
+ else if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
+ return repodata_lookup_str(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname);
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
+ continue;
+ if (!repodata_precheck_keyname(data, keyname))
+ continue;
+ str = repodata_lookup_str(data, entry, keyname);
+ if (str)
+ return str;
+ if (repodata_lookup_type(data, entry, keyname))
+ return 0;
+ }
+ return 0;
+}
+
+
+unsigned long long
+repo_lookup_num(Repo *repo, Id entry, Id keyname, unsigned long long notfound)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i;
+ unsigned long long value;
+
+ if (entry >= 0)
+ {
+ if (keyname == RPM_RPMDBID)
+ {
+ if (repo->rpmdbid && entry >= repo->start && entry < repo->end)
+ return repo->rpmdbid[entry - repo->start];
+ return notfound;
+ }
+ }
+ else if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
+ return repodata_lookup_num(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname, &value) ? value : notfound;
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
+ continue;
+ if (!repodata_precheck_keyname(data, keyname))
+ continue;
+ if (repodata_lookup_num(data, entry, keyname, &value))
+ return value;
+ if (repodata_lookup_type(data, entry, keyname))
+ return notfound;
+ }
+ return notfound;
+}
+
+Id
+repo_lookup_id(Repo *repo, Id entry, Id keyname)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i;
+ Id id;
+
+ if (entry >= 0)
+ {
+ switch (keyname)
+ {
+ case SOLVABLE_NAME:
+ return repo->pool->solvables[entry].name;
+ case SOLVABLE_ARCH:
+ return repo->pool->solvables[entry].arch;
+ case SOLVABLE_EVR:
+ return repo->pool->solvables[entry].evr;
+ case SOLVABLE_VENDOR:
+ return repo->pool->solvables[entry].vendor;
+ }
+ }
+ else if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
+ {
+ Repodata *data = pool->pos.repo->repodata + pool->pos.repodataid;
+ Id id = repodata_lookup_id(data, entry, keyname);
+ return data->localpool ? repodata_globalize_id(data, id, 1) : id;
+ }
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
+ continue;
+ if (!repodata_precheck_keyname(data, keyname))
+ continue;
+ id = repodata_lookup_id(data, entry, keyname);
+ if (id)
+ return data->localpool ? repodata_globalize_id(data, id, 1) : id;
+ if (repodata_lookup_type(data, entry, keyname))
+ return 0;
+ }
+ return 0;
+}
+
+static int
+lookup_idarray_solvable(Repo *repo, Offset off, Queue *q)
+{
+ Id *p;
+
+ queue_empty(q);
+ if (off)
+ for (p = repo->idarraydata + off; *p; p++)
+ queue_push(q, *p);
+ return 1;
+}
+
+int
+repo_lookup_idarray(Repo *repo, Id entry, Id keyname, Queue *q)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i;
+ if (entry >= 0)
+ {
+ switch (keyname)
+ {
+ case SOLVABLE_PROVIDES:
+ return lookup_idarray_solvable(repo, repo->pool->solvables[entry].provides, q);
+ case SOLVABLE_OBSOLETES:
+ return lookup_idarray_solvable(repo, repo->pool->solvables[entry].obsoletes, q);
+ case SOLVABLE_CONFLICTS:
+ return lookup_idarray_solvable(repo, repo->pool->solvables[entry].conflicts, q);
+ case SOLVABLE_REQUIRES:
+ return lookup_idarray_solvable(repo, repo->pool->solvables[entry].requires, q);
+ case SOLVABLE_RECOMMENDS:
+ return lookup_idarray_solvable(repo, repo->pool->solvables[entry].recommends, q);
+ case SOLVABLE_SUGGESTS:
+ return lookup_idarray_solvable(repo, repo->pool->solvables[entry].suggests, q);
+ case SOLVABLE_SUPPLEMENTS:
+ return lookup_idarray_solvable(repo, repo->pool->solvables[entry].supplements, q);
+ case SOLVABLE_ENHANCES:
+ return lookup_idarray_solvable(repo, repo->pool->solvables[entry].enhances, q);
+ }
+ }
+ else if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
+ {
+ Repodata *data = pool->pos.repo->repodata + pool->pos.repodataid;
+ if (repodata_lookup_idarray(data, entry, keyname, q))
+ {
+ if (data->localpool)
+ {
+ for (i = 0; i < q->count; i++)
+ q->elements[i] = repodata_globalize_id(data, q->elements[i], 1);
+ }
+ return 1;
+ }
+ }
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
+ continue;
+ if (!repodata_precheck_keyname(data, keyname))
+ continue;
+ if (repodata_lookup_idarray(data, entry, keyname, q))
+ {
+ if (data->localpool)
+ {
+ for (i = 0; i < q->count; i++)
+ q->elements[i] = repodata_globalize_id(data, q->elements[i], 1);
+ }
+ return 1;
+ }
+ if (repodata_lookup_type(data, entry, keyname))
+ break;
+ }
+ queue_empty(q);
+ return 0;
+}
+
+int
+repo_lookup_deparray(Repo *repo, Id entry, Id keyname, Queue *q, Id marker)
+{
+ int r = repo_lookup_idarray(repo, entry, keyname, q);
+ if (!r)
+ return 0;
+ if (marker == -1 || marker == 1)
+ marker = solv_depmarker(keyname, marker);
+ if (marker && q->count)
+ {
+ int i;
+ if (marker < 0)
+ {
+ marker = -marker;
+ for (i = 0; i < q->count; i++)
+ if (q->elements[i] == marker)
+ {
+ queue_truncate(q, i);
+ return r;
+ }
+ }
+ else
+ {
+ for (i = 0; i < q->count; i++)
+ if (q->elements[i] == marker)
+ {
+ queue_deleten(q, 0, i + 1);
+ return r;
+ }
+ queue_empty(q);
+ }
+ }
+ return r;
+}
+
+const unsigned char *
+repo_lookup_bin_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i;
+ const unsigned char *chk;
+
+ if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
+ return repodata_lookup_bin_checksum(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname, typep);
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
+ continue;
+ if (!repodata_precheck_keyname(data, keyname))
+ continue;
+ chk = repodata_lookup_bin_checksum(data, entry, keyname, typep);
+ if (chk)
+ return chk;
+ if (repodata_lookup_type(data, entry, keyname))
+ return 0;
+ }
+ *typep = 0;
+ return 0;
+}
+
+const char *
+repo_lookup_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
+{
+ const unsigned char *chk = repo_lookup_bin_checksum(repo, entry, keyname, typep);
+ return chk ? pool_bin2hex(repo->pool, chk, solv_chksum_len(*typep)) : 0;
+}
+
+int
+repo_lookup_void(Repo *repo, Id entry, Id keyname)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i;
+ Id type;
+
+ if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
+ return repodata_lookup_void(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname);
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
+ continue;
+ if (!repodata_precheck_keyname(data, keyname))
+ continue;
+ type = repodata_lookup_type(data, entry, keyname);
+ if (type)
+ return type == REPOKEY_TYPE_VOID;
+ }
+ return 0;
+}
+
+Id
+repo_lookup_type(Repo *repo, Id entry, Id keyname)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i;
+ Id type;
+
+ if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
+ return repodata_lookup_type(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname);
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
+ continue;
+ if (!repodata_precheck_keyname(data, keyname))
+ continue;
+ type = repodata_lookup_type(data, entry, keyname);
+ if (type)
+ return type == REPOKEY_TYPE_DELETED ? 0 : type;
+ }
+ return 0;
+}
+
+const void *
+repo_lookup_binary(Repo *repo, Id entry, Id keyname, int *lenp)
+{
+ Pool *pool = repo->pool;
+ Repodata *data;
+ int i;
+ const void *bin;
+
+ if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
+ return repodata_lookup_binary(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname, lenp);
+ FOR_REPODATAS(repo, i, data)
+ {
+ if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
+ continue;
+ if (!repodata_precheck_keyname(data, keyname))
+ continue;
+ bin = repodata_lookup_binary(data, entry, keyname, lenp);
+ if (bin)
+ return bin;
+ }
+ *lenp = 0;
+ return 0;
+}
+
+/***********************************************************************/
+
+Repodata *
+repo_add_repodata(Repo *repo, int flags)
+{
+ Repodata *data;
+ int i;
+ if ((flags & REPO_USE_LOADING) != 0)
+ {
+ for (i = repo->nrepodata - 1; i > 0; i--)
+ if (repo->repodata[i].state == REPODATA_LOADING)
+ {
+ Repodata *data = repo->repodata + i;
+ /* re-init */
+ /* hack: we mis-use REPO_REUSE_REPODATA here */
+ if (!(flags & REPO_REUSE_REPODATA))
+ repodata_empty(data, (flags & REPO_LOCALPOOL) ? 1 : 0);
+ return data;
+ }
+ return 0; /* must not create a new repodata! */
+ }
+ if ((flags & REPO_REUSE_REPODATA) != 0)
+ {
+ for (i = repo->nrepodata - 1; i > 0; i--)
+ if (repo->repodata[i].state != REPODATA_STUB)
+ return repo->repodata + i;
+ }
+ if (!repo->nrepodata)
+ {
+ repo->nrepodata = 2; /* start with id 1 */
+ repo->repodata = solv_calloc(repo->nrepodata, sizeof(*data));
+ }
+ else
+ {
+ repo->nrepodata++;
+ repo->repodata = solv_realloc2(repo->repodata, repo->nrepodata, sizeof(*data));
+ }
+ data = repo->repodata + repo->nrepodata - 1;
+ repodata_initdata(data, repo, (flags & REPO_LOCALPOOL) ? 1 : 0);
+ return data;
+}
+
+Repodata *
+repo_id2repodata(Repo *repo, Id id)
+{
+ return id ? repo->repodata + id : 0;
+}
+
+Repodata *
+repo_last_repodata(Repo *repo)
+{
+ int i;
+ for (i = repo->nrepodata - 1; i > 0; i--)
+ if (repo->repodata[i].state != REPODATA_STUB)
+ return repo->repodata + i;
+ return repo_add_repodata(repo, 0);
+}
+
+void
+repo_set_id(Repo *repo, Id p, Id keyname, Id id)
+{
+ Repodata *data;
+ if (p >= 0)
+ {
+ switch (keyname)
+ {
+ case SOLVABLE_NAME:
+ repo->pool->solvables[p].name = id;
+ return;
+ case SOLVABLE_ARCH:
+ repo->pool->solvables[p].arch = id;
+ return;
+ case SOLVABLE_EVR:
+ repo->pool->solvables[p].evr = id;
+ return;
+ case SOLVABLE_VENDOR:
+ repo->pool->solvables[p].vendor = id;
+ return;
+ }
+ }
+ data = repo_last_repodata(repo);
+ if (data->localpool)
+ id = repodata_localize_id(data, id, 1);
+ repodata_set_id(data, p, keyname, id);
+}
+
+void
+repo_set_num(Repo *repo, Id p, Id keyname, unsigned long long num)
+{
+ Repodata *data;
+ if (p >= 0)
+ {
+ if (keyname == RPM_RPMDBID)
+ {
+ if (!repo->rpmdbid)
+ repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
+ repo->rpmdbid[p - repo->start] = num;
+ return;
+ }
+ }
+ data = repo_last_repodata(repo);
+ repodata_set_num(data, p, keyname, num);
+}
+
+void
+repo_set_str(Repo *repo, Id p, Id keyname, const char *str)
+{
+ Repodata *data;
+ if (p >= 0)
+ {
+ switch (keyname)
+ {
+ case SOLVABLE_NAME:
+ case SOLVABLE_ARCH:
+ case SOLVABLE_EVR:
+ case SOLVABLE_VENDOR:
+ repo_set_id(repo, p, keyname, pool_str2id(repo->pool, str, 1));
+ return;
+ }
+ }
+ data = repo_last_repodata(repo);
+ repodata_set_str(data, p, keyname, str);
+}
+
+void
+repo_set_poolstr(Repo *repo, Id p, Id keyname, const char *str)
+{
+ Repodata *data;
+ if (p >= 0)
+ {
+ switch (keyname)
+ {
+ case SOLVABLE_NAME:
+ case SOLVABLE_ARCH:
+ case SOLVABLE_EVR:
+ case SOLVABLE_VENDOR:
+ repo_set_id(repo, p, keyname, pool_str2id(repo->pool, str, 1));
+ return;
+ }
+ }
+ data = repo_last_repodata(repo);
+ repodata_set_poolstr(data, p, keyname, str);
+}
+
+void
+repo_add_poolstr_array(Repo *repo, Id p, Id keyname, const char *str)
+{
+ Repodata *data = repo_last_repodata(repo);
+ repodata_add_poolstr_array(data, p, keyname, str);
+}
+
+void
+repo_add_deparray(Repo *repo, Id p, Id keyname, Id dep, Id marker)
+{
+ Repodata *data;
+ if (marker == -1 || marker == 1)
+ marker = solv_depmarker(keyname, marker);
+ if (p >= 0)
+ {
+ Solvable *s = repo->pool->solvables + p;
+ switch (keyname)
+ {
+ case SOLVABLE_PROVIDES:
+ s->provides = repo_addid_dep(repo, s->provides, dep, marker);
+ return;
+ case SOLVABLE_OBSOLETES:
+ s->obsoletes = repo_addid_dep(repo, s->obsoletes, dep, marker);
+ return;
+ case SOLVABLE_CONFLICTS:
+ s->conflicts = repo_addid_dep(repo, s->conflicts, dep, marker);
+ return;
+ case SOLVABLE_REQUIRES:
+ s->requires = repo_addid_dep(repo, s->requires, dep, marker);
+ return;
+ case SOLVABLE_RECOMMENDS:
+ s->recommends = repo_addid_dep(repo, s->recommends, dep, marker);
+ return;
+ case SOLVABLE_SUGGESTS:
+ s->suggests = repo_addid_dep(repo, s->suggests, dep, marker);
+ return;
+ case SOLVABLE_SUPPLEMENTS:
+ s->supplements = repo_addid_dep(repo, s->supplements, dep, marker);
+ return;
+ case SOLVABLE_ENHANCES:
+ s->enhances = repo_addid_dep(repo, s->enhances, dep, marker);
+ return;
+ }
+ }
+ data = repo_last_repodata(repo);
+ repodata_add_idarray(data, p, keyname, dep);
+}
+
+void
+repo_add_idarray(Repo *repo, Id p, Id keyname, Id id)
+{
+ repo_add_deparray(repo, p, keyname, id, 0);
+}
+
+static Offset
+repo_set_idarray_solvable(Repo *repo, Queue *q)
+{
+ Offset o = 0;
+ int i;
+ for (i = 0; i < q->count; i++)
+ repo_addid_dep(repo, o, q->elements[i], 0);
+ return o;
+}
+
+void
+repo_set_deparray(Repo *repo, Id p, Id keyname, Queue *q, Id marker)
+{
+ Repodata *data;
+ if (marker == -1 || marker == 1)
+ marker = solv_depmarker(keyname, marker);
+ if (marker)
+ {
+ /* complex case, splice old and new arrays */
+ int i;
+ Queue q2;
+ queue_init(&q2);
+ repo_lookup_deparray(repo, p, keyname, &q2, -marker);
+ if (marker > 0)
+ {
+ if (q->count)
+ {
+ queue_push(&q2, marker);
+ for (i = 0; i < q->count; i++)
+ queue_push(&q2, q->elements[i]);
+ }
+ }
+ else
+ {
+ if (q2.count)
+ queue_insert(&q2, 0, -marker);
+ queue_insertn(&q2, 0, q->count, q->elements);
+ }
+ repo_set_deparray(repo, p, keyname, &q2, 0);
+ queue_free(&q2);
+ return;
+ }
+ if (p >= 0)
+ {
+ Solvable *s = repo->pool->solvables + p;
+ switch (keyname)
+ {
+ case SOLVABLE_PROVIDES:
+ s->provides = repo_set_idarray_solvable(repo, q);
+ return;
+ case SOLVABLE_OBSOLETES:
+ s->obsoletes = repo_set_idarray_solvable(repo, q);
+ return;
+ case SOLVABLE_CONFLICTS:
+ s->conflicts = repo_set_idarray_solvable(repo, q);
+ return;
+ case SOLVABLE_REQUIRES:
+ s->requires = repo_set_idarray_solvable(repo, q);
+ return;
+ case SOLVABLE_RECOMMENDS:
+ s->recommends = repo_set_idarray_solvable(repo, q);
+ return;
+ case SOLVABLE_SUGGESTS:
+ s->suggests = repo_set_idarray_solvable(repo, q);
+ return;
+ case SOLVABLE_SUPPLEMENTS:
+ s->supplements = repo_set_idarray_solvable(repo, q);
+ return;
+ case SOLVABLE_ENHANCES:
+ s->enhances = repo_set_idarray_solvable(repo, q);
+ return;
+ }
+ }
+ data = repo_last_repodata(repo);
+ repodata_set_idarray(data, p, keyname, q);
+}
+
+void
+repo_set_idarray(Repo *repo, Id p, Id keyname, Queue *q)
+{
+ repo_set_deparray(repo, p, keyname, q, 0);
+}
+
+void
+repo_unset(Repo *repo, Id p, Id keyname)
+{
+ Repodata *data;
+ if (p >= 0)
+ {
+ Solvable *s = repo->pool->solvables + p;
+ switch (keyname)
+ {
+ case SOLVABLE_NAME:
+ s->name = 0;
+ return;
+ case SOLVABLE_ARCH:
+ s->arch = 0;
+ return;
+ case SOLVABLE_EVR:
+ s->evr = 0;
+ return;
+ case SOLVABLE_VENDOR:
+ s->vendor = 0;
+ return;
+ case RPM_RPMDBID:
+ if (repo->rpmdbid)
+ repo->rpmdbid[p - repo->start] = 0;
+ return;
+ case SOLVABLE_PROVIDES:
+ s->provides = 0;
+ return;
+ case SOLVABLE_OBSOLETES:
+ s->obsoletes = 0;
+ return;
+ case SOLVABLE_CONFLICTS:
+ s->conflicts = 0;
+ return;
+ case SOLVABLE_REQUIRES:
+ s->requires = 0;
+ return;
+ case SOLVABLE_RECOMMENDS:
+ s->recommends = 0;
+ return;
+ case SOLVABLE_SUGGESTS:
+ s->suggests = 0;
+ return;
+ case SOLVABLE_SUPPLEMENTS:
+ s->supplements = 0;
+ case SOLVABLE_ENHANCES:
+ s->enhances = 0;
+ return;
+ default:
+ break;
+ }
+ }
+ data = repo_last_repodata(repo);
+ repodata_unset(data, p, keyname);
+}
+
+void
+repo_internalize(Repo *repo)
+{
+ int i;
+ Repodata *data;
+
+ FOR_REPODATAS(repo, i, data)
+ if (data->attrs || data->xattrs)
+ repodata_internalize(data);
+}
+
+void
+repo_disable_paging(Repo *repo)
+{
+ int i;
+ Repodata *data;
+
+ FOR_REPODATAS(repo, i, data)
+ repodata_disable_paging(data);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo.h
+ *
+ */
+
+#ifndef LIBSOLV_REPO_H
+#define LIBSOLV_REPO_H
+
+#include "pooltypes.h"
+#include "pool.h"
+#include "repodata.h"
+#include "dataiterator.h"
+#include "hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _Repo {
+ const char *name; /* name pointer */
+ Id repoid; /* our id */
+ void *appdata; /* application private pointer */
+
+ Pool *pool; /* pool containing this repo */
+
+ int start; /* start of this repo solvables within pool->solvables */
+ int end; /* last solvable + 1 of this repo */
+ int nsolvables; /* number of solvables repo is contributing to pool */
+
+ int disabled; /* ignore the solvables? */
+ int priority; /* priority of this repo */
+ int subpriority; /* sub-priority of this repo, used just for sorting, not pruning */
+
+ Id *idarraydata; /* array of metadata Ids, solvable dependencies are offsets into this array */
+ int idarraysize;
+
+ int nrepodata; /* number of our stores.. */
+
+ Id *rpmdbid; /* solvable side data: rpm database id */
+
+#ifdef LIBSOLV_INTERNAL
+ Repodata *repodata; /* our stores for non-solvable related data */
+ Offset lastoff; /* start of last array in idarraydata */
+
+ Hashtable lastidhash; /* hash to speed up repo_addid_dep */
+ Hashval lastidhash_mask;
+ int lastidhash_idarraysize;
+ int lastmarker;
+ Offset lastmarkerpos;
+#endif /* LIBSOLV_INTERNAL */
+} Repo;
+
+extern Repo *repo_create(Pool *pool, const char *name);
+extern void repo_free(Repo *repo, int reuseids);
+extern void repo_empty(Repo *repo, int reuseids);
+extern void repo_freedata(Repo *repo);
+extern Id repo_add_solvable(Repo *repo);
+extern Id repo_add_solvable_block(Repo *repo, int count);
+extern void repo_free_solvable(Repo *repo, Id p, int reuseids);
+extern void repo_free_solvable_block(Repo *repo, Id start, int count, int reuseids);
+extern void *repo_sidedata_create(Repo *repo, size_t size);
+extern void *repo_sidedata_extend(Repo *repo, void *b, size_t size, Id p, int count);
+extern Id repo_add_solvable_block_before(Repo *repo, int count, Repo *beforerepo);
+
+extern Offset repo_addid(Repo *repo, Offset olddeps, Id id);
+extern Offset repo_addid_dep(Repo *repo, Offset olddeps, Id id, Id marker);
+extern Offset repo_reserve_ids(Repo *repo, Offset olddeps, int num);
+extern Offset repo_fix_supplements(Repo *repo, Offset provides, Offset supplements, Offset freshens);
+extern Offset repo_fix_conflicts(Repo *repo, Offset conflicts);
+
+static inline const char *repo_name(const Repo *repo)
+{
+ return repo->name;
+}
+
+/* those two functions are here because they need the Repo definition */
+
+static inline Repo *pool_id2repo(Pool *pool, Id repoid)
+{
+ return repoid < pool->nrepos ? pool->repos[repoid] : 0;
+}
+
+static inline int pool_disabled_solvable(const Pool *pool, Solvable *s)
+{
+ if (s->repo && s->repo->disabled)
+ return 1;
+ if (pool->considered)
+ {
+ Id id = s - pool->solvables;
+ if (!MAPTST(pool->considered, id))
+ return 1;
+ }
+ return 0;
+}
+
+static inline int pool_installable(const Pool *pool, Solvable *s)
+{
+ if (!s->arch || s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+ return 0;
+ if (s->repo && s->repo->disabled)
+ return 0;
+ if (pool->id2arch && (s->arch > pool->lastarch || !pool->id2arch[s->arch]))
+ return 0;
+ if (pool->considered)
+ {
+ Id id = s - pool->solvables;
+ if (!MAPTST(pool->considered, id))
+ return 0;
+ }
+ return 1;
+}
+
+/* search callback values */
+#define SEARCH_NEXT_KEY 1
+#define SEARCH_NEXT_SOLVABLE 2
+#define SEARCH_STOP 3
+#define SEARCH_ENTERSUB -1
+
+/* standard flags used in the repo_add functions */
+#define REPO_REUSE_REPODATA (1 << 0)
+#define REPO_NO_INTERNALIZE (1 << 1)
+#define REPO_LOCALPOOL (1 << 2)
+#define REPO_USE_LOADING (1 << 3)
+#define REPO_EXTEND_SOLVABLES (1 << 4)
+#define REPO_USE_ROOTDIR (1 << 5)
+#define REPO_NO_LOCATION (1 << 6)
+
+Repodata *repo_add_repodata(Repo *repo, int flags);
+Repodata *repo_id2repodata(Repo *repo, Id id);
+Repodata *repo_last_repodata(Repo *repo);
+
+void repo_search(Repo *repo, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata);
+
+/* returns the string value of the attribute, or NULL if not found */
+Id repo_lookup_type(Repo *repo, Id entry, Id keyname);
+const char *repo_lookup_str(Repo *repo, Id entry, Id keyname);
+/* returns the integer value of the attribute, or notfound if not found */
+unsigned long long repo_lookup_num(Repo *repo, Id entry, Id keyname, unsigned long long notfound);
+Id repo_lookup_id(Repo *repo, Id entry, Id keyname);
+int repo_lookup_idarray(Repo *repo, Id entry, Id keyname, Queue *q);
+int repo_lookup_deparray(Repo *repo, Id entry, Id keyname, Queue *q, Id marker);
+int repo_lookup_void(Repo *repo, Id entry, Id keyname);
+const char *repo_lookup_checksum(Repo *repo, Id entry, Id keyname, Id *typep);
+const unsigned char *repo_lookup_bin_checksum(Repo *repo, Id entry, Id keyname, Id *typep);
+const void *repo_lookup_binary(Repo *repo, Id entry, Id keyname, int *lenp);
+Id solv_depmarker(Id keyname, Id marker);
+
+void repo_set_id(Repo *repo, Id p, Id keyname, Id id);
+void repo_set_num(Repo *repo, Id p, Id keyname, unsigned long long num);
+void repo_set_str(Repo *repo, Id p, Id keyname, const char *str);
+void repo_set_poolstr(Repo *repo, Id p, Id keyname, const char *str);
+void repo_add_poolstr_array(Repo *repo, Id p, Id keyname, const char *str);
+void repo_add_idarray(Repo *repo, Id p, Id keyname, Id id);
+void repo_add_deparray(Repo *repo, Id p, Id keyname, Id dep, Id marker);
+void repo_set_idarray(Repo *repo, Id p, Id keyname, Queue *q);
+void repo_set_deparray(Repo *repo, Id p, Id keyname, Queue *q, Id marker);
+void repo_unset(Repo *repo, Id p, Id keyname);
+
+void repo_internalize(Repo *repo);
+void repo_disable_paging(Repo *repo);
+
+/* iterator macros */
+#define FOR_REPO_SOLVABLES(r, p, s) \
+ for (p = (r)->start, s = (r)->pool->solvables + p; p < (r)->end; p++, s = (r)->pool->solvables + p) \
+ if (s->repo != (r)) \
+ continue; \
+ else
+
+#ifdef LIBSOLV_INTERNAL
+#define FOR_REPODATAS(repo, rdid, data) \
+ for (rdid = 1, data = repo->repodata + rdid; rdid < repo->nrepodata; rdid++, data++)
+#else
+#define FOR_REPODATAS(repo, rdid, data) \
+ for (rdid = 1; rdid < repo->nrepodata && (data = repo_id2repodata(repo, rdid)); rdid++)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_REPO_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_solv.c
+ *
+ * Add a repo in solv format
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "repo_solv.h"
+#include "util.h"
+
+#include "repopack.h"
+#include "repopage.h"
+
+#include "poolid_private.h" /* WHATPROVIDES_BLOCK */
+
+#define INTERESTED_START SOLVABLE_NAME
+#define INTERESTED_END SOLVABLE_ENHANCES
+
+#define SOLV_ERROR_NOT_SOLV 1
+#define SOLV_ERROR_UNSUPPORTED 2
+#define SOLV_ERROR_EOF 3
+#define SOLV_ERROR_ID_RANGE 4
+#define SOLV_ERROR_OVERFLOW 5
+#define SOLV_ERROR_CORRUPT 6
+
+
+
+/*******************************************************************************
+ * functions to extract data from a file handle
+ */
+
+/*
+ * read u32
+ */
+
+static unsigned int
+read_u32(Repodata *data)
+{
+ int c, i;
+ unsigned int x = 0;
+
+ if (data->error)
+ return 0;
+ for (i = 0; i < 4; i++)
+ {
+ c = getc(data->fp);
+ if (c == EOF)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_EOF, "unexpected EOF");
+ return 0;
+ }
+ x = (x << 8) | c;
+ }
+ return x;
+}
+
+
+/*
+ * read u8
+ */
+
+static unsigned int
+read_u8(Repodata *data)
+{
+ int c;
+
+ if (data->error)
+ return 0;
+ c = getc(data->fp);
+ if (c == EOF)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_EOF, "unexpected EOF");
+ return 0;
+ }
+ return c;
+}
+
+
+/*
+ * read Id
+ */
+
+static Id
+read_id(Repodata *data, Id max)
+{
+ unsigned int x = 0;
+ int c, i;
+
+ if (data->error)
+ return 0;
+ for (i = 0; i < 5; i++)
+ {
+ c = getc(data->fp);
+ if (c == EOF)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_EOF, "unexpected EOF");
+ return 0;
+ }
+ if (!(c & 128))
+ {
+ x = (x << 7) | c;
+ if (max && x >= (unsigned int)max)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "read_id: id too large (%u/%u)", x, max);
+ return 0;
+ }
+ return x;
+ }
+ x = (x << 7) ^ c ^ 128;
+ }
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_CORRUPT, "read_id: id too long");
+ return 0;
+}
+
+
+static Id *
+read_idarray(Repodata *data, Id max, Id *map, Id *store, Id *end)
+{
+ unsigned int x = 0;
+ int c;
+
+ if (data->error)
+ return 0;
+ for (;;)
+ {
+ c = getc(data->fp);
+ if (c == EOF)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_EOF, "unexpected EOF");
+ return 0;
+ }
+ if ((c & 128) != 0)
+ {
+ x = (x << 7) ^ c ^ 128;
+ continue;
+ }
+ x = (x << 6) | (c & 63);
+ if (max && x >= (unsigned int)max)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "read_idarray: id too large (%u/%u)", x, max);
+ return 0;
+ }
+ if (map)
+ x = map[x];
+ if (store == end)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_OVERFLOW, "read_idarray: array overflow");
+ return 0;
+ }
+ *store++ = x;
+ if ((c & 64) == 0)
+ {
+ if (x == 0) /* already have trailing zero? */
+ return store;
+ if (store == end)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_OVERFLOW, "read_idarray: array overflow");
+ return 0;
+ }
+ *store++ = 0;
+ return store;
+ }
+ x = 0;
+ }
+}
+
+
+/*******************************************************************************
+ * functions to extract data from memory
+ */
+
+/*
+ * read array of Ids
+ */
+
+static inline unsigned char *
+data_read_id_max(unsigned char *dp, Id *ret, Id *map, int max, Repodata *data)
+{
+ Id x;
+ dp = data_read_id(dp, &x);
+ if (x < 0 || (max && x >= max))
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "data_read_id_max: id too large (%u/%u)", x, max);
+ x = 0;
+ }
+ *ret = map ? map[x] : x;
+ return dp;
+}
+
+static unsigned char *
+data_read_idarray(unsigned char *dp, Id **storep, Id *map, int max, Repodata *data)
+{
+ Id *store = *storep;
+ unsigned int x = 0;
+ int c;
+
+ for (;;)
+ {
+ c = *dp++;
+ if ((c & 128) != 0)
+ {
+ x = (x << 7) ^ c ^ 128;
+ continue;
+ }
+ x = (x << 6) | (c & 63);
+ if (max && x >= (unsigned int)max)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "data_read_idarray: id too large (%u/%u)", x, max);
+ data->error = SOLV_ERROR_ID_RANGE;
+ break;
+ }
+ *store++ = x;
+ if ((c & 64) == 0)
+ break;
+ x = 0;
+ }
+ *store++ = 0;
+ *storep = store;
+ return dp;
+}
+
+static unsigned char *
+data_read_rel_idarray(unsigned char *dp, Id **storep, Id *map, int max, Repodata *data, Id marker)
+{
+ Id *store = *storep;
+ Id old = 0;
+ unsigned int x = 0;
+ int c;
+
+ for (;;)
+ {
+ c = *dp++;
+ if ((c & 128) != 0)
+ {
+ x = (x << 7) ^ c ^ 128;
+ continue;
+ }
+ x = (x << 6) | (c & 63);
+ if (x == 0)
+ {
+ if (!(c & 64))
+ break;
+ if (marker)
+ *store++ = marker;
+ old = 0;
+ continue;
+ }
+ x = old + (x - 1);
+ old = x;
+ if (max && x >= (unsigned int)max)
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "data_read_rel_idarray: id too large (%u/%u)", x, max);
+ break;
+ }
+ *store++ = map ? map[x] : x;
+ if (!(c & 64))
+ break;
+ x = 0;
+ }
+ *store++ = 0;
+ *storep = store;
+ return dp;
+}
+
+
+
+
+/*******************************************************************************
+ * functions to add data to our incore memory space
+ */
+
+#define INCORE_ADD_CHUNK 8192
+#define DATA_READ_CHUNK 8192
+
+static void
+incore_add_id(Repodata *data, Id sx)
+{
+ unsigned int x = (unsigned int)sx;
+ unsigned char *dp;
+ /* make sure we have at least 5 bytes free */
+ if (data->incoredatafree < 5)
+ {
+ data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + INCORE_ADD_CHUNK);
+ data->incoredatafree = INCORE_ADD_CHUNK;
+ }
+ dp = data->incoredata + data->incoredatalen;
+ if (x >= (1 << 14))
+ {
+ if (x >= (1 << 28))
+ *dp++ = (x >> 28) | 128;
+ if (x >= (1 << 21))
+ *dp++ = (x >> 21) | 128;
+ *dp++ = (x >> 14) | 128;
+ }
+ if (x >= (1 << 7))
+ *dp++ = (x >> 7) | 128;
+ *dp++ = x & 127;
+ data->incoredatafree -= dp - (data->incoredata + data->incoredatalen);
+ data->incoredatalen = dp - data->incoredata;
+}
+
+static void
+incore_add_sizek(Repodata *data, unsigned int sx)
+{
+ if (sx < (1 << 22))
+ incore_add_id(data, (Id)(sx << 10));
+ else
+ {
+ if ((sx >> 25) != 0)
+ {
+ incore_add_id(data, (Id)(sx >> 25));
+ data->incoredata[data->incoredatalen - 1] |= 128;
+ }
+ incore_add_id(data, (Id)((sx << 10) | 0x80000000));
+ data->incoredata[data->incoredatalen - 5] = (sx >> 18) | 128;
+ }
+}
+
+static void
+incore_add_ideof(Repodata *data, Id sx, int eof)
+{
+ unsigned int x = (unsigned int)sx;
+ unsigned char *dp;
+ /* make sure we have at least 5 bytes free */
+ if (data->incoredatafree < 5)
+ {
+ data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + INCORE_ADD_CHUNK);
+ data->incoredatafree = INCORE_ADD_CHUNK;
+ }
+ dp = data->incoredata + data->incoredatalen;
+ if (x >= (1 << 13))
+ {
+ if (x >= (1 << 27))
+ *dp++ = (x >> 27) | 128;
+ if (x >= (1 << 20))
+ *dp++ = (x >> 20) | 128;
+ *dp++ = (x >> 13) | 128;
+ }
+ if (x >= (1 << 6))
+ *dp++ = (x >> 6) | 128;
+ *dp++ = eof ? (x & 63) : (x & 63) | 64;
+ data->incoredatafree -= dp - (data->incoredata + data->incoredatalen);
+ data->incoredatalen = dp - data->incoredata;
+}
+
+static void
+incore_add_blob(Repodata *data, unsigned char *buf, int len)
+{
+ if (data->incoredatafree < (unsigned int)len)
+ {
+ data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + INCORE_ADD_CHUNK + len);
+ data->incoredatafree = INCORE_ADD_CHUNK + len;
+ }
+ memcpy(data->incoredata + data->incoredatalen, buf, len);
+ data->incoredatafree -= len;
+ data->incoredatalen += len;
+}
+
+static void
+incore_map_idarray(Repodata *data, unsigned char *dp, Id *map, Id max)
+{
+ /* We have to map the IDs, which might also change
+ the necessary number of bytes, so we can't just copy
+ over the blob and adjust it. */
+ for (;;)
+ {
+ Id id;
+ int eof;
+ dp = data_read_ideof(dp, &id, &eof);
+ if (id < 0 || (max && id >= max))
+ {
+ data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "incore_map_idarray: id too large (%u/%u)", id, max);
+ break;
+ }
+ id = map[id];
+ incore_add_ideof(data, id, eof);
+ if (eof)
+ break;
+ }
+}
+
+#if 0
+static void
+incore_add_u32(Repodata *data, unsigned int x)
+{
+ unsigned char *dp;
+ /* make sure we have at least 4 bytes free */
+ if (data->incoredatafree < 4)
+ {
+ data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + INCORE_ADD_CHUNK);
+ data->incoredatafree = INCORE_ADD_CHUNK;
+ }
+ dp = data->incoredata + data->incoredatalen;
+ *dp++ = x >> 24;
+ *dp++ = x >> 16;
+ *dp++ = x >> 8;
+ *dp++ = x;
+ data->incoredatafree -= 4;
+ data->incoredatalen += 4;
+}
+
+static void
+incore_add_u8(Repodata *data, unsigned int x)
+{
+ unsigned char *dp;
+ /* make sure we have at least 1 byte free */
+ if (data->incoredatafree < 1)
+ {
+ data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + 1024);
+ data->incoredatafree = 1024;
+ }
+ dp = data->incoredata + data->incoredatalen;
+ *dp++ = x;
+ data->incoredatafree--;
+ data->incoredatalen++;
+}
+#endif
+
+
+/*******************************************************************************
+ * our main function
+ */
+
+/*
+ * read repo from .solv file and add it to pool
+ */
+
+int
+repo_add_solv(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ int i, l;
+ int numid, numrel, numdir, numsolv;
+ int numkeys, numschemata;
+
+ Offset sizeid;
+ Offset *str; /* map Id -> Offset into string space */
+ char *strsp; /* repo string space */
+ char *sp; /* pointer into string space */
+ Id *idmap; /* map of repo Ids to pool Ids */
+ Id id, type;
+ Hashval hashmask, h, hh;
+ Hashtable hashtbl;
+ Id name, evr, did;
+ int relflags;
+ Reldep *ran;
+ unsigned int size_idarray;
+ Id *idarraydatap, *idarraydataend;
+ Offset ido;
+ Solvable *s;
+ unsigned int solvflags;
+ unsigned int solvversion;
+ Repokey *keys;
+ Id *schemadata, *schemadatap, *schemadataend;
+ Id *schemata, key, *keyp;
+ int nentries;
+ int have_incoredata;
+ int maxsize, allsize;
+ unsigned char *buf, *bufend, *dp, *dps;
+ Id stack[3 * 5];
+ int keydepth;
+ int needchunk; /* need a new chunk of data */
+ unsigned int now;
+ int oldnstrings = pool->ss.nstrings;
+ int oldnrels = pool->nrels;
+
+ struct _Stringpool *spool;
+
+ Repodata *parent = 0;
+ Repodata data;
+
+ int extendstart = 0, extendend = 0; /* set in case we're extending */
+
+ now = solv_timems(0);
+
+ if ((flags & REPO_USE_LOADING) != 0)
+ {
+ /* this is a stub replace operation */
+ flags |= REPO_EXTEND_SOLVABLES;
+ /* use REPO_REUSE_REPODATA hack so that the old repodata is kept */
+ parent = repo_add_repodata(repo, flags | REPO_REUSE_REPODATA);
+ extendstart = parent->start;
+ extendend = parent->end;
+ }
+ else if (flags & REPO_EXTEND_SOLVABLES)
+ {
+ /* extend all solvables of this repo */
+ extendstart = repo->start;
+ extendend = repo->end;
+ }
+
+ memset(&data, 0, sizeof(data));
+ data.repo = repo;
+ data.fp = fp;
+ repopagestore_init(&data.store);
+
+ if (read_u32(&data) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
+ return pool_error(pool, SOLV_ERROR_NOT_SOLV, "not a SOLV file");
+ solvversion = read_u32(&data);
+ switch (solvversion)
+ {
+ case SOLV_VERSION_8:
+ break;
+ default:
+ return pool_error(pool, SOLV_ERROR_UNSUPPORTED, "unsupported SOLV version");
+ }
+
+ numid = (int)read_u32(&data);
+ numrel = (int)read_u32(&data);
+ numdir = (int)read_u32(&data);
+ numsolv = (int)read_u32(&data);
+ numkeys = (int)read_u32(&data);
+ numschemata = (int)read_u32(&data);
+ solvflags = read_u32(&data);
+
+ if (numid < 0 || numid >= 0x20000000)
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of ids");
+ if (numrel < 0 || numrel >= 0x20000000)
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of rels");
+ if (numdir && (numdir < 2 || numdir >= 0x20000000))
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of dirs");
+ if (numsolv < 0 || numsolv >= 0x20000000)
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of solvables");
+ if (numkeys < 0 || numkeys >= 0x20000000)
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of keys");
+ if (numschemata < 0 || numschemata >= 0x20000000)
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of schematas");
+
+ if (numrel && (flags & REPO_LOCALPOOL) != 0)
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "relations are forbidden in a local pool");
+ if ((flags & REPO_EXTEND_SOLVABLES) && numsolv)
+ {
+ /* make sure that we exactly replace the stub repodata */
+ if (extendend - extendstart != numsolv)
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "sub-repository solvable number does not match main repository (%d - %d)", extendend - extendstart, numsolv);
+ for (i = 0; i < numsolv; i++)
+ if (pool->solvables[extendstart + i].repo != repo)
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "main repository contains holes, cannot extend");
+ }
+
+ /******* Part 1: string IDs *****************************************/
+
+ sizeid = read_u32(&data); /* size of string space */
+
+ /*
+ * read strings and Ids
+ *
+ */
+
+
+ /*
+ * alloc buffers
+ */
+
+ if (!(flags & REPO_LOCALPOOL))
+ {
+ spool = &pool->ss;
+ /* alloc max needed string buffer and string pointers, will shrink again later */
+#if 0
+ spool->stringspace = solv_realloc(spool->stringspace, spool->sstrings + sizeid + 1);
+ spool->strings = solv_realloc2(spool->strings, spool->nstrings + numid, sizeof(Offset));
+#else
+ spool->sstrings += sizeid + 1;
+ spool->nstrings += numid;
+ stringpool_shrink(spool); /* we misuse stringpool_shrink so that the correct BLOCK factor is used */
+ spool->sstrings -= sizeid + 1;
+ spool->nstrings -= numid;
+#endif
+ }
+ else
+ {
+ data.localpool = 1;
+ spool = &data.spool;
+ spool->stringspace = solv_malloc(7 + sizeid + 1);
+ spool->strings = solv_malloc2(numid < 2 ? 2 : numid, sizeof(Offset));
+ strcpy(spool->stringspace, "<NULL>");
+ spool->sstrings = 7;
+ spool->nstrings = 1;
+ spool->strings[0] = 0; /* <NULL> */
+ }
+
+
+ /*
+ * read string data and append to old string space
+ */
+
+ strsp = spool->stringspace + spool->sstrings; /* append new entries */
+ if ((solvflags & SOLV_FLAG_PREFIX_POOL) == 0)
+ {
+ if (sizeid && fread(strsp, sizeid, 1, fp) != 1)
+ {
+ repodata_freedata(&data);
+ return pool_error(pool, SOLV_ERROR_EOF, "read error while reading strings");
+ }
+ }
+ else
+ {
+ unsigned int pfsize = read_u32(&data);
+ char *prefix = solv_malloc(pfsize);
+ char *pp = prefix;
+ char *old_str = 0;
+ char *dest = strsp;
+ int freesp = sizeid;
+
+ if (pfsize && fread(prefix, pfsize, 1, fp) != 1)
+ {
+ solv_free(prefix);
+ repodata_freedata(&data);
+ return pool_error(pool, SOLV_ERROR_EOF, "read error while reading strings");
+ }
+ for (i = 1; i < numid; i++)
+ {
+ int same = (unsigned char)*pp++;
+ size_t len = strlen(pp) + 1;
+ freesp -= same + len;
+ if (freesp < 0)
+ {
+ solv_free(prefix);
+ repodata_freedata(&data);
+ return pool_error(pool, SOLV_ERROR_OVERFLOW, "overflow while expanding strings");
+ }
+ if (same)
+ memcpy(dest, old_str, same);
+ memcpy(dest + same, pp, len);
+ pp += len;
+ old_str = dest;
+ dest += same + len;
+ }
+ solv_free(prefix);
+ if (freesp != 0)
+ {
+ repodata_freedata(&data);
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "expanding strings size mismatch");
+ }
+ }
+ strsp[sizeid] = 0; /* make string space \0 terminated */
+ sp = strsp;
+
+ /* now merge */
+ str = spool->strings; /* array of offsets into strsp, indexed by Id */
+ if ((flags & REPO_LOCALPOOL) != 0)
+ {
+ /* no shared pool, thus no idmap and no unification needed */
+ idmap = 0;
+ spool->nstrings = numid < 2 ? 2 : numid; /* make sure we have at least id 0 and 1 */
+ if (*sp)
+ {
+ /* we need id 1 to be '' for directories */
+ repodata_freedata(&data);
+ return pool_error(pool, SOLV_ERROR_CORRUPT, "store strings don't start with an empty string");
+ }
+ for (i = 1; i < spool->nstrings; i++)
+ {
+ if (sp >= strsp + sizeid && numid >= 2)
+ {
+ repodata_freedata(&data);
+ return pool_error(pool, SOLV_ERROR_OVERFLOW, "not enough strings");
+ }
+ str[i] = sp - spool->stringspace;
+ sp += strlen(sp) + 1;
+ }
+ spool->sstrings = sp - spool->stringspace;
+ }
+ else
+ {
+ Offset oldsstrings = spool->sstrings;
+
+ /* alloc id map for name and rel Ids. this maps ids in the solv files
+ * to the ids in our pool */
+ idmap = solv_calloc(numid + numrel, sizeof(Id));
+
+ /* grow hash if needed, otherwise reuse */
+ hashmask = mkmask(spool->nstrings + numid);
+#if 0
+ POOL_DEBUG(SOLV_DEBUG_STATS, "read %d strings\n", numid);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "string hash buckets: %d, old %d\n", hashmask + 1, spool->stringhashmask + 1);
+#endif
+ if (hashmask > spool->stringhashmask)
+ {
+ spool->stringhashtbl = solv_free(spool->stringhashtbl);
+ spool->stringhashmask = hashmask;
+ spool->stringhashtbl = hashtbl = solv_calloc(hashmask + 1, sizeof(Id));
+ for (i = 1; i < spool->nstrings; i++)
+ {
+ h = strhash(spool->stringspace + spool->strings[i]) & hashmask;
+ hh = HASHCHAIN_START;
+ while (hashtbl[h])
+ h = HASHCHAIN_NEXT(h, hh, hashmask);
+ hashtbl[h] = i;
+ }
+ }
+ else
+ {
+ hashtbl = spool->stringhashtbl;
+ hashmask = spool->stringhashmask;
+ }
+
+ /*
+ * run over strings and merge with pool.
+ * also populate id map (maps solv Id -> pool Id)
+ */
+ for (i = 1; i < numid; i++)
+ {
+ if (sp >= strsp + sizeid)
+ {
+ solv_free(idmap);
+ spool->nstrings = oldnstrings;
+ spool->sstrings = oldsstrings;
+ stringpool_freehash(spool);
+ repodata_freedata(&data);
+ return pool_error(pool, SOLV_ERROR_OVERFLOW, "not enough strings %d %d", i, numid);
+ }
+ 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)
+ break;
+ if (!strcmp(spool->stringspace + spool->strings[id], sp))
+ break; /* already in pool */
+ h = HASHCHAIN_NEXT(h, hh, hashmask);
+ }
+
+ /* length == offset to next string */
+ l = strlen(sp) + 1;
+ if (!id) /* end of hash chain -> new string */
+ {
+ id = spool->nstrings++;
+ hashtbl[h] = id;
+ str[id] = spool->sstrings; /* save offset */
+ if (sp != spool->stringspace + spool->sstrings)
+ memmove(spool->stringspace + spool->sstrings, sp, l);
+ spool->sstrings += l;
+ }
+ idmap[i] = id; /* repo relative -> pool relative */
+ sp += l; /* next string */
+ }
+ if (hashmask > mkmask(spool->nstrings + 8192))
+ {
+ spool->stringhashtbl = solv_free(spool->stringhashtbl);
+ spool->stringhashmask = 0;
+ }
+ stringpool_shrink(spool); /* vacuum */
+ }
+
+
+ /******* Part 2: Relation IDs ***************************************/
+
+ /*
+ * read RelDeps
+ *
+ */
+
+ if (numrel)
+ {
+ /* extend rels */
+ pool->rels = solv_realloc2(pool->rels, pool->nrels + numrel, sizeof(Reldep));
+ ran = pool->rels;
+
+ /* grow hash if needed, otherwise reuse */
+ hashmask = mkmask(pool->nrels + numrel);
+#if 0
+ POOL_DEBUG(SOLV_DEBUG_STATS, "read %d rels\n", numrel);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "rel hash buckets: %d, old %d\n", hashmask + 1, pool->relhashmask + 1);
+#endif
+ if (hashmask > pool->relhashmask)
+ {
+ pool->relhashtbl = solv_free(pool->relhashtbl);
+ pool->relhashmask = hashmask;
+ pool->relhashtbl = hashtbl = solv_calloc(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;
+ }
+ }
+ else
+ {
+ hashtbl = pool->relhashtbl;
+ hashmask = pool->relhashmask;
+ }
+
+ /*
+ * read RelDeps from repo
+ */
+ for (i = 0; i < numrel; i++)
+ {
+ name = read_id(&data, i + numid); /* read (repo relative) Ids */
+ evr = read_id(&data, i + numid);
+ relflags = read_u8(&data);
+ name = idmap[name]; /* map to (pool relative) Ids */
+ evr = idmap[evr];
+ h = relhash(name, evr, relflags) & hashmask;
+ hh = HASHCHAIN_START;
+ for (;;)
+ {
+ id = hashtbl[h];
+ if (!id) /* end of hash chain reached */
+ break;
+ if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == relflags)
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hashmask);
+ }
+ if (!id) /* new RelDep */
+ {
+ id = pool->nrels++;
+ hashtbl[h] = id;
+ ran[id].name = name;
+ ran[id].evr = evr;
+ ran[id].flags = relflags;
+ }
+ idmap[i + numid] = MAKERELDEP(id); /* fill Id map */
+ }
+ if (hashmask > mkmask(pool->nrels + 4096))
+ {
+ pool->relhashtbl = solv_free(pool->relhashtbl);
+ pool->relhashmask = 0;
+ }
+ pool_shrink_rels(pool); /* vacuum */
+ }
+
+ /* if we added ids/rels, make room in our whatprovide arrays */
+ if (!(flags & REPO_LOCALPOOL))
+ {
+ if (pool->whatprovides && oldnstrings != pool->ss.nstrings)
+ {
+ int newlen = (pool->ss.nstrings + WHATPROVIDES_BLOCK) & ~WHATPROVIDES_BLOCK;
+ pool->whatprovides = solv_realloc2(pool->whatprovides, newlen, sizeof(Offset));
+ memset(pool->whatprovides + oldnstrings, 0, (newlen - oldnstrings) * sizeof(Offset));
+ }
+ if (pool->whatprovides_rel && oldnrels != pool->nrels)
+ {
+ int newlen = (pool->nrels + WHATPROVIDES_BLOCK) & ~WHATPROVIDES_BLOCK;
+ pool->whatprovides_rel = solv_realloc2(pool->whatprovides_rel, newlen, sizeof(Offset));
+ memset(pool->whatprovides_rel + oldnrels, 0, (newlen - oldnrels) * sizeof(Offset));
+ }
+ }
+
+ /******* Part 3: Dirs ***********************************************/
+ if (numdir)
+ {
+ data.dirpool.dirs = solv_malloc2(numdir, sizeof(Id));
+ data.dirpool.ndirs = numdir;
+ data.dirpool.dirs[0] = 0; /* dir 0: virtual root */
+ data.dirpool.dirs[1] = 1; /* dir 1: / */
+ for (i = 2; i < numdir; i++)
+ {
+ id = read_id(&data, i + numid);
+ if (id >= numid)
+ data.dirpool.dirs[i] = -(id - numid);
+ else if (idmap)
+ data.dirpool.dirs[i] = idmap[id];
+ else
+ data.dirpool.dirs[i] = id;
+ }
+ }
+
+ /******* Part 4: Keys ***********************************************/
+
+ keys = solv_calloc(numkeys, sizeof(*keys));
+ /* keys start at 1 */
+ for (i = 1; i < numkeys; i++)
+ {
+ id = read_id(&data, numid);
+ if (idmap)
+ id = idmap[id];
+ else if ((flags & REPO_LOCALPOOL) != 0)
+ id = pool_str2id(pool, stringpool_id2str(spool, id), 1);
+ type = read_id(&data, numid);
+ if (idmap)
+ type = idmap[type];
+ else if ((flags & REPO_LOCALPOOL) != 0)
+ type = pool_str2id(pool, stringpool_id2str(spool, type), 1);
+ if (type < REPOKEY_TYPE_VOID || type > REPOKEY_TYPE_FLEXARRAY)
+ {
+ data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "unsupported data type '%s'", pool_id2str(pool, type));
+ type = REPOKEY_TYPE_VOID;
+ }
+ keys[i].name = id;
+ keys[i].type = type;
+ keys[i].size = read_id(&data, keys[i].type == REPOKEY_TYPE_CONSTANTID ? numid + numrel : 0);
+ keys[i].storage = read_id(&data, 0);
+ /* old versions used SOLVABLE for main solvable data */
+ if (keys[i].storage == KEY_STORAGE_SOLVABLE)
+ keys[i].storage = KEY_STORAGE_INCORE;
+ if (keys[i].storage != KEY_STORAGE_INCORE && keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
+ data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "unsupported storage type %d", keys[i].storage);
+ if (id >= SOLVABLE_NAME && id <= RPM_RPMDBID)
+ {
+ if (keys[i].storage != KEY_STORAGE_INCORE)
+ data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "main solvable data must use incore storage %d", keys[i].storage);
+ keys[i].storage = KEY_STORAGE_SOLVABLE;
+ }
+ /* cannot handle rel idarrays in incore/vertical */
+ if (type == REPOKEY_TYPE_REL_IDARRAY && keys[i].storage != KEY_STORAGE_SOLVABLE)
+ data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "type REL_IDARRAY is only supported for STORAGE_SOLVABLE");
+ /* cannot handle mapped ids in vertical */
+ if (!(flags & REPO_LOCALPOOL) && keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET && (type == REPOKEY_TYPE_ID || type == REPOKEY_TYPE_IDARRAY))
+ data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "mapped ids are not supported for STORAGE_VERTICAL_OFFSET");
+
+ if (keys[i].type == REPOKEY_TYPE_CONSTANTID && idmap)
+ keys[i].size = idmap[keys[i].size];
+#if 0
+ fprintf(stderr, "key %d %s %s %d %d\n", i, pool_id2str(pool,id), pool_id2str(pool, keys[i].type),
+ keys[i].size, keys[i].storage);
+#endif
+ }
+
+ have_incoredata = 0;
+ for (i = 1; i < numkeys; i++)
+ if (keys[i].storage == KEY_STORAGE_INCORE || keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
+ have_incoredata = 1;
+
+ data.keys = keys;
+ data.nkeys = numkeys;
+ for (i = 1; i < numkeys; i++)
+ {
+ id = keys[i].name;
+ data.keybits[(id >> 3) & (sizeof(data.keybits) - 1)] |= 1 << (id & 7);
+ }
+
+ /******* Part 5: Schemata ********************************************/
+
+ id = read_id(&data, 0);
+ schemadata = solv_calloc(id + 1, sizeof(Id));
+ schemadatap = schemadata + 1;
+ schemadataend = schemadatap + id;
+ schemata = solv_calloc(numschemata, sizeof(Id));
+ for (i = 1; i < numschemata; i++)
+ {
+ schemata[i] = schemadatap - schemadata;
+ schemadatap = read_idarray(&data, numid, 0, schemadatap, schemadataend);
+#if 0
+ Id *sp = schemadata + schemata[i];
+ fprintf(stderr, "schema %d:", i);
+ for (; *sp; sp++)
+ fprintf(stderr, " %d", *sp);
+ fprintf(stderr, "\n");
+#endif
+ }
+ data.schemata = schemata;
+ data.nschemata = numschemata;
+ data.schemadata = schemadata;
+ data.schemadatalen = schemadataend - data.schemadata;
+
+ /******* Part 6: Data ********************************************/
+
+ idarraydatap = idarraydataend = 0;
+ size_idarray = 0;
+
+ maxsize = read_id(&data, 0);
+ allsize = read_id(&data, 0);
+ maxsize += 5; /* so we can read the next schema of an array */
+ if (maxsize > allsize)
+ maxsize = allsize;
+
+ buf = solv_calloc(maxsize + DATA_READ_CHUNK + 4, 1); /* 4 extra bytes to detect overflows */
+ bufend = buf;
+ dp = buf;
+
+ l = maxsize;
+ if (l < DATA_READ_CHUNK)
+ l = DATA_READ_CHUNK;
+ if (l > allsize)
+ l = allsize;
+ if (!l || fread(buf, l, 1, data.fp) != 1)
+ {
+ data.error = pool_error(pool, SOLV_ERROR_EOF, "unexpected EOF");
+ id = 0;
+ }
+ else
+ {
+ bufend = buf + l;
+ allsize -= l;
+ dp = data_read_id_max(dp, &id, 0, numschemata, &data);
+ }
+
+ incore_add_id(&data, 0); /* so that incoreoffset 0 means schema 0 */
+ incore_add_id(&data, id); /* main schema id */
+ keyp = schemadata + schemata[id];
+ data.mainschema = id;
+ for (i = 0; keyp[i]; i++)
+ ;
+ if (i)
+ data.mainschemaoffsets = solv_calloc(i, sizeof(Id));
+
+ nentries = 0;
+ keydepth = 0;
+ s = 0;
+ needchunk = 1;
+ for(;;)
+ {
+ /* make sure we have enough room */
+ if (keydepth == 0 || needchunk)
+ {
+ int left = bufend - dp;
+ /* read data chunk to dp */
+ if (data.error)
+ break;
+ if (left < 0)
+ {
+ data.error = pool_error(pool, SOLV_ERROR_EOF, "buffer overrun");
+ break;
+ }
+ if (left < maxsize)
+ {
+ if (left)
+ memmove(buf, dp, left);
+ l = maxsize - left;
+ if (l < DATA_READ_CHUNK)
+ l = DATA_READ_CHUNK;
+ if (l > allsize)
+ l = allsize;
+ if (l && fread(buf + left, l, 1, data.fp) != 1)
+ {
+ data.error = pool_error(pool, SOLV_ERROR_EOF, "unexpected EOF");
+ break;
+ }
+ allsize -= l;
+ left += l;
+ bufend = buf + left;
+ if (allsize + left < maxsize)
+ maxsize = allsize + left;
+ dp = buf;
+ }
+ needchunk = 0;
+ }
+
+ key = *keyp++;
+#if 0
+printf("key %d at %d\n", key, (int)(keyp - 1 - schemadata));
+#endif
+ if (!key)
+ {
+ if (keydepth <= 3)
+ needchunk = 1;
+ if (nentries)
+ {
+ if (s && keydepth == 3)
+ {
+ s++; /* next solvable */
+ if (have_incoredata)
+ data.incoreoffset[(s - pool->solvables) - data.start] = data.incoredatalen;
+ }
+ id = stack[keydepth - 1];
+ if (!id)
+ {
+ dp = data_read_id_max(dp, &id, 0, numschemata, &data);
+ incore_add_id(&data, id);
+ }
+ keyp = schemadata + schemata[id];
+ nentries--;
+ continue;
+ }
+ if (!keydepth)
+ break;
+ --keydepth;
+ keyp = schemadata + stack[--keydepth];
+ nentries = stack[--keydepth];
+#if 0
+printf("pop flexarray %d %d\n", keydepth, nentries);
+#endif
+ if (!keydepth && s)
+ s = 0; /* back from solvables */
+ continue;
+ }
+
+ if (keydepth == 0)
+ data.mainschemaoffsets[keyp - 1 - (schemadata + schemata[data.mainschema])] = data.incoredatalen;
+
+#if 0
+printf("=> %s %s %p\n", pool_id2str(pool, keys[key].name), pool_id2str(pool, keys[key].type), s);
+#endif
+ id = keys[key].name;
+ if (keys[key].storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ dps = dp;
+ dp = data_skip(dp, REPOKEY_TYPE_ID);
+ dp = data_skip(dp, REPOKEY_TYPE_ID);
+ incore_add_blob(&data, dps, dp - dps); /* just record offset/size */
+ continue;
+ }
+ switch (keys[key].type)
+ {
+ case REPOKEY_TYPE_ID:
+ dp = data_read_id_max(dp, &did, idmap, numid + numrel, &data);
+ if (s && id == SOLVABLE_NAME)
+ s->name = did;
+ else if (s && id == SOLVABLE_ARCH)
+ s->arch = did;
+ else if (s && id == SOLVABLE_EVR)
+ s->evr = did;
+ else if (s && id == SOLVABLE_VENDOR)
+ s->vendor = did;
+ else if (keys[key].storage == KEY_STORAGE_INCORE)
+ incore_add_id(&data, did);
+#if 0
+ POOL_DEBUG(SOLV_DEBUG_STATS, "%s -> %s\n", pool_id2str(pool, id), pool_id2str(pool, did));
+#endif
+ break;
+ case REPOKEY_TYPE_IDARRAY:
+ case REPOKEY_TYPE_REL_IDARRAY:
+ if (!s || id < INTERESTED_START || id > INTERESTED_END)
+ {
+ dps = dp;
+ dp = data_skip(dp, REPOKEY_TYPE_IDARRAY);
+ if (keys[key].storage != KEY_STORAGE_INCORE)
+ break;
+ if (idmap)
+ incore_map_idarray(&data, dps, idmap, numid + numrel);
+ else
+ incore_add_blob(&data, dps, dp - dps);
+ break;
+ }
+ ido = idarraydatap - repo->idarraydata;
+ if (keys[key].type == REPOKEY_TYPE_IDARRAY)
+ dp = data_read_idarray(dp, &idarraydatap, idmap, numid + numrel, &data);
+ else if (id == SOLVABLE_REQUIRES)
+ dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data, SOLVABLE_PREREQMARKER);
+ else if (id == SOLVABLE_PROVIDES)
+ dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data, SOLVABLE_FILEMARKER);
+ else
+ dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data, 0);
+ if (idarraydatap > idarraydataend)
+ {
+ data.error = pool_error(pool, SOLV_ERROR_OVERFLOW, "idarray overflow");
+ break;
+ }
+ if (id == SOLVABLE_PROVIDES)
+ s->provides = ido;
+ else if (id == SOLVABLE_OBSOLETES)
+ s->obsoletes = ido;
+ else if (id == SOLVABLE_CONFLICTS)
+ s->conflicts = ido;
+ else if (id == SOLVABLE_REQUIRES)
+ s->requires = ido;
+ else if (id == SOLVABLE_RECOMMENDS)
+ s->recommends= ido;
+ else if (id == SOLVABLE_SUPPLEMENTS)
+ s->supplements = ido;
+ else if (id == SOLVABLE_SUGGESTS)
+ s->suggests = ido;
+ else if (id == SOLVABLE_ENHANCES)
+ s->enhances = ido;
+#if 0
+ POOL_DEBUG(SOLV_DEBUG_STATS, "%s ->\n", pool_id2str(pool, id));
+ for (; repo->idarraydata[ido]; ido++)
+ POOL_DEBUG(SOLV_DEBUG_STATS," %s\n", pool_dep2str(pool, repo->idarraydata[ido]));
+#endif
+ break;
+ case REPOKEY_TYPE_FIXARRAY:
+ case REPOKEY_TYPE_FLEXARRAY:
+ if (!keydepth)
+ needchunk = 1;
+ if (keydepth == sizeof(stack)/sizeof(*stack))
+ {
+ data.error = pool_error(pool, SOLV_ERROR_OVERFLOW, "array stack overflow");
+ break;
+ }
+ stack[keydepth++] = nentries;
+ stack[keydepth++] = keyp - schemadata;
+ stack[keydepth++] = 0;
+ dp = data_read_id_max(dp, &nentries, 0, 0, &data);
+ incore_add_id(&data, nentries);
+ if (!nentries)
+ {
+ /* zero size array? */
+ keydepth -= 2;
+ nentries = stack[--keydepth];
+ break;
+ }
+ if (keydepth == 3 && id == REPOSITORY_SOLVABLES)
+ {
+ /* horray! here come the solvables */
+ if (nentries != numsolv)
+ {
+ data.error = pool_error(pool, SOLV_ERROR_CORRUPT, "inconsistent number of solvables: %d %d", nentries, numsolv);
+ break;
+ }
+ if (idarraydatap)
+ {
+ data.error = pool_error(pool, SOLV_ERROR_CORRUPT, "more than one solvable block");
+ break;
+ }
+ if ((flags & REPO_EXTEND_SOLVABLES) != 0)
+ s = pool_id2solvable(pool, extendstart);
+ else
+ s = pool_id2solvable(pool, repo_add_solvable_block(repo, numsolv));
+ data.start = s - pool->solvables;
+ data.end = data.start + numsolv;
+ repodata_extend_block(&data, data.start, numsolv);
+ for (i = 1; i < numkeys; i++)
+ {
+ id = keys[i].name;
+ if ((keys[i].type == REPOKEY_TYPE_IDARRAY || keys[i].type == REPOKEY_TYPE_REL_IDARRAY)
+ && id >= INTERESTED_START && id <= INTERESTED_END)
+ size_idarray += keys[i].size;
+ }
+ /* allocate needed space in repo */
+ /* we add maxsize because it is an upper limit for all idarrays, thus we can't overflow */
+ repo_reserve_ids(repo, 0, size_idarray + maxsize + 1);
+ idarraydatap = repo->idarraydata + repo->idarraysize;
+ repo->idarraysize += size_idarray;
+ idarraydataend = idarraydatap + size_idarray;
+ repo->lastoff = 0;
+ if (have_incoredata)
+ data.incoreoffset[(s - pool->solvables) - data.start] = data.incoredatalen;
+ }
+ nentries--;
+ dp = data_read_id_max(dp, &id, 0, numschemata, &data);
+ incore_add_id(&data, id);
+ if (keys[key].type == REPOKEY_TYPE_FIXARRAY)
+ {
+ if (!id)
+ data.error = pool_error(pool, SOLV_ERROR_CORRUPT, "illegal fixarray");
+ stack[keydepth - 1] = id;
+ }
+ keyp = schemadata + schemata[id];
+ break;
+ case REPOKEY_TYPE_NUM:
+ if (!(solvflags & SOLV_FLAG_SIZE_BYTES) && keys[key].storage == KEY_STORAGE_INCORE &&
+ (id == SOLVABLE_INSTALLSIZE || id == SOLVABLE_DOWNLOADSIZE || id == DELTA_DOWNLOADSIZE))
+ {
+ /* old solv file with sizes in kilos. transcode. */
+ dp = data_read_id(dp, &id);
+ incore_add_sizek(&data, (unsigned int)id);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (id == RPM_RPMDBID && s && (keys[key].type == REPOKEY_TYPE_U32 || keys[key].type == REPOKEY_TYPE_NUM))
+ {
+ if (keys[key].type == REPOKEY_TYPE_U32)
+ dp = data_read_u32(dp, (unsigned int *)&id);
+ else
+ dp = data_read_id_max(dp, &id, 0, 0, &data);
+ if (!repo->rpmdbid)
+ repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
+ repo->rpmdbid[(s - pool->solvables) - repo->start] = id;
+ break;
+ }
+ dps = dp;
+ dp = data_skip(dp, keys[key].type);
+ if (keys[key].storage == KEY_STORAGE_INCORE)
+ incore_add_blob(&data, dps, dp - dps);
+ break;
+ }
+ }
+ /* should shrink idarraydata again */
+
+ if (keydepth)
+ data.error = pool_error(pool, SOLV_ERROR_EOF, "unexpected EOF, depth = %d", keydepth);
+ if (!data.error)
+ {
+ if (dp > bufend)
+ data.error = pool_error(pool, SOLV_ERROR_EOF, "buffer overrun");
+ }
+ solv_free(buf);
+
+ if (data.error)
+ {
+ /* free solvables */
+ repo_free_solvable_block(repo, data.start, data.end - data.start, 1);
+ /* free id array */
+ repo->idarraysize -= size_idarray;
+ /* free incore data */
+ data.incoredata = solv_free(data.incoredata);
+ data.incoredatalen = data.incoredatafree = 0;
+ }
+
+ if (data.incoredatafree)
+ {
+ /* shrink excess size */
+ data.incoredata = solv_realloc(data.incoredata, data.incoredatalen);
+ data.incoredatafree = 0;
+ }
+ solv_free(idmap);
+
+ /* fixup the special idarray type */
+ for (i = 1; i < numkeys; i++)
+ if (keys[i].type == REPOKEY_TYPE_REL_IDARRAY)
+ keys[i].type = REPOKEY_TYPE_IDARRAY;
+
+ for (i = 1; i < numkeys; i++)
+ if (keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
+ break;
+ if (i < numkeys && !data.error)
+ {
+ Id fileoffset = 0;
+ unsigned int pagesize;
+
+ /* we have vertical data, make it available */
+ data.verticaloffset = solv_calloc(numkeys, sizeof(Id));
+ for (i = 1; i < numkeys; i++)
+ if (keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ data.verticaloffset[i] = fileoffset;
+ fileoffset += keys[i].size;
+ }
+ data.lastverticaloffset = fileoffset;
+ pagesize = read_u32(&data);
+ if (!data.error)
+ {
+ data.error = repopagestore_read_or_setup_pages(&data.store, data.fp, pagesize, fileoffset);
+ if (data.error == SOLV_ERROR_EOF)
+ pool_error(pool, data.error, "repopagestore setup: unexpected EOF");
+ else if (data.error)
+ pool_error(pool, data.error, "repopagestore setup failed");
+ }
+ }
+ data.fp = 0; /* no longer needed */
+
+ if (data.error)
+ {
+ i = data.error;
+ repodata_freedata(&data);
+ return i;
+ }
+
+ if (parent)
+ {
+ /* overwrite stub repodata */
+ repodata_freedata(parent);
+ data.repodataid = parent->repodataid;
+ *parent = data;
+ }
+ else
+ {
+ /* make it available as new repodata */
+ if (!repo->nrepodata)
+ {
+ repo->nrepodata = 1;
+ repo->repodata = solv_calloc(2, sizeof(data));
+ }
+ else
+ repo->repodata = solv_realloc2(repo->repodata, repo->nrepodata + 1, sizeof(data));
+ data.repodataid = repo->nrepodata;
+ repo->repodata[repo->nrepodata++] = data;
+ }
+
+ /* create stub repodata entries for all external */
+ if (!(flags & SOLV_ADD_NO_STUBS) && !parent)
+ {
+ for (key = 1 ; key < data.nkeys; key++)
+ if (data.keys[key].name == REPOSITORY_EXTERNAL && data.keys[key].type == REPOKEY_TYPE_FLEXARRAY)
+ break;
+ if (key < data.nkeys)
+ repodata_create_stubs(repo->repodata + (repo->nrepodata - 1));
+ }
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_solv took %d ms\n", solv_timems(now));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", data.incoredatalen/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_solv.h
+ *
+ */
+
+#ifndef LIBSOLV_REPO_SOLVE_H
+#define LIBSOLV_REPO_SOLVE_H
+
+#include <stdio.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int repo_add_solv(Repo *repo, FILE *fp, int flags);
+
+#define SOLV_ADD_NO_STUBS (1 << 8)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_REPO_SOLVE_H */
--- /dev/null
+/*
+ * Copyright (c) 2007-2011, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_write.c
+ *
+ * Write Repo data out to a file in solv format
+ *
+ * See doc/README.format for a description
+ * of the binary file format
+ *
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "util.h"
+#include "repo_write.h"
+#include "repopage.h"
+
+/*------------------------------------------------------------------*/
+/* Id map optimizations */
+
+typedef struct needid {
+ Id need;
+ Id map;
+} NeedId;
+
+
+#define RELOFF(id) (needid[0].map + GETRELID(id))
+
+/*
+ * increment need Id
+ * idarray: array of Ids, ID_NULL terminated
+ * needid: array of Id->NeedId
+ *
+ * return size of array (including trailing zero)
+ *
+ */
+
+static void
+incneedid(Pool *pool, Id id, NeedId *needid)
+{
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ needid[RELOFF(id)].need++;
+ if (ISRELDEP(rd->evr))
+ incneedid(pool, rd->evr, needid);
+ else
+ needid[rd->evr].need++;
+ id = rd->name;
+ }
+ needid[id].need++;
+}
+
+static int
+incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
+{
+ Id id;
+ int n = 0;
+
+ if (!idarray)
+ return 0;
+ while ((id = *idarray++) != 0)
+ {
+ n++;
+ while (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ needid[RELOFF(id)].need++;
+ if (ISRELDEP(rd->evr))
+ incneedid(pool, rd->evr, needid);
+ else
+ needid[rd->evr].need++;
+ id = rd->name;
+ }
+ needid[id].need++;
+ }
+ return n + 1;
+}
+
+
+/*
+ *
+ */
+
+static int
+needid_cmp_need(const void *ap, const void *bp, void *dp)
+{
+ const NeedId *a = ap;
+ const NeedId *b = bp;
+ int r;
+ r = b->need - a->need;
+ if (r)
+ return r;
+ return a->map - b->map;
+}
+
+static int
+needid_cmp_need_s(const void *ap, const void *bp, void *dp)
+{
+ const NeedId *a = ap;
+ const NeedId *b = bp;
+ Stringpool *spool = dp;
+ const char *as;
+ const char *bs;
+
+ int r;
+ r = b->need - a->need;
+ if (r)
+ return r;
+ as = spool->stringspace + spool->strings[a->map];
+ bs = spool->stringspace + spool->strings[b->map];
+ return strcmp(as, bs);
+}
+
+
+/*------------------------------------------------------------------*/
+/* output helper routines, used for writing the header */
+/* (the data itself is accumulated in memory and written with
+ * write_blob) */
+
+/*
+ * unsigned 32-bit
+ */
+
+static void
+write_u32(Repodata *data, unsigned int x)
+{
+ FILE *fp = data->fp;
+ if (data->error)
+ return;
+ if (putc(x >> 24, fp) == EOF ||
+ putc(x >> 16, fp) == EOF ||
+ putc(x >> 8, fp) == EOF ||
+ putc(x, fp) == EOF)
+ {
+ data->error = pool_error(data->repo->pool, -1, "write error u32: %s", strerror(errno));
+ }
+}
+
+
+/*
+ * unsigned 8-bit
+ */
+
+static void
+write_u8(Repodata *data, unsigned int x)
+{
+ if (data->error)
+ return;
+ if (putc(x, data->fp) == EOF)
+ {
+ data->error = pool_error(data->repo->pool, -1, "write error u8: %s", strerror(errno));
+ }
+}
+
+/*
+ * data blob
+ */
+
+static void
+write_blob(Repodata *data, void *blob, int len)
+{
+ if (data->error)
+ return;
+ if (len && fwrite(blob, len, 1, data->fp) != 1)
+ {
+ data->error = pool_error(data->repo->pool, -1, "write error blob: %s", strerror(errno));
+ }
+}
+
+/*
+ * Id
+ */
+
+static void
+write_id(Repodata *data, Id x)
+{
+ FILE *fp = data->fp;
+ if (data->error)
+ return;
+ 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)
+ {
+ data->error = pool_error(data->repo->pool, -1, "write error id: %s", strerror(errno));
+ }
+}
+
+static inline void
+write_id_eof(Repodata *data, Id x, int eof)
+{
+ if (x >= 64)
+ x = (x & 63) | ((x & ~63) << 1);
+ write_id(data, x | (eof ? 0 : 64));
+}
+
+
+
+static inline void
+write_str(Repodata *data, const char *str)
+{
+ if (data->error)
+ return;
+ if (fputs(str, data->fp) == EOF || putc(0, data->fp) == EOF)
+ {
+ data->error = pool_error(data->repo->pool, -1, "write error str: %s", strerror(errno));
+ }
+}
+
+/*
+ * Array of Ids
+ */
+
+static void
+write_idarray(Repodata *data, Pool *pool, NeedId *needid, Id *ids)
+{
+ Id id;
+ if (!ids)
+ return;
+ if (!*ids)
+ {
+ write_u8(data, 0);
+ return;
+ }
+ for (;;)
+ {
+ id = *ids++;
+ if (needid)
+ id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
+ if (id >= 64)
+ id = (id & 63) | ((id & ~63) << 1);
+ if (!*ids)
+ {
+ write_id(data, id);
+ return;
+ }
+ write_id(data, id | 64);
+ }
+}
+
+static int
+cmp_ids(const void *pa, const void *pb, void *dp)
+{
+ Id a = *(Id *)pa;
+ Id b = *(Id *)pb;
+ return a - b;
+}
+
+#if 0
+static void
+write_idarray_sort(Repodata *data, Pool *pool, NeedId *needid, Id *ids, Id marker)
+{
+ int len, i;
+ Id lids[64], *sids;
+
+ if (!ids)
+ return;
+ if (!*ids)
+ {
+ write_u8(data, 0);
+ return;
+ }
+ for (len = 0; len < 64 && ids[len]; len++)
+ {
+ Id id = ids[len];
+ if (needid)
+ id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
+ lids[len] = id;
+ }
+ if (ids[len])
+ {
+ for (i = len + 1; ids[i]; i++)
+ ;
+ sids = solv_malloc2(i, sizeof(Id));
+ memcpy(sids, lids, 64 * sizeof(Id));
+ for (; ids[len]; len++)
+ {
+ Id id = ids[len];
+ if (needid)
+ id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
+ sids[len] = id;
+ }
+ }
+ else
+ sids = lids;
+
+ /* That bloody solvable:prereqmarker needs to stay in position :-( */
+ if (needid)
+ marker = needid[marker].need;
+ for (i = 0; i < len; i++)
+ if (sids[i] == marker)
+ break;
+ if (i > 1)
+ solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
+ if ((len - i) > 2)
+ solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
+
+ Id id, old = 0;
+
+ /* The differencing above produces many runs of ones and twos. I tried
+ fairly elaborate schemes to RLE those, but they give only very mediocre
+ improvements in compression, as coding the escapes costs quite some
+ space. Even if they are coded only as bits in IDs. The best improvement
+ was about 2.7% for the whole .solv file. It's probably better to
+ invest some complexity into sharing idarrays, than RLEing. */
+ for (i = 0; i < len - 1; i++)
+ {
+ id = sids[i];
+ /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
+ hence all real differences are offsetted by 1. Otherwise we would
+ have to handle negative differences, which would cost code space for
+ the encoding of the sign. We loose the exact mapping of prereq here,
+ but we know the result, so we can recover from that in the reader. */
+ if (id == marker)
+ id = old = 0;
+ else
+ {
+ id = id - old + 1;
+ old = sids[i];
+ }
+ /* XXX If difference is zero we have multiple equal elements,
+ we might want to skip writing them out. */
+ if (id >= 64)
+ id = (id & 63) | ((id & ~63) << 1);
+ write_id(data, id | 64);
+ }
+ id = sids[i];
+ if (id == marker)
+ id = 0;
+ else
+ id = id - old + 1;
+ if (id >= 64)
+ id = (id & 63) | ((id & ~63) << 1);
+ write_id(data, id);
+ if (sids != lids)
+ solv_free(sids);
+}
+#endif
+
+
+struct extdata {
+ unsigned char *buf;
+ int len;
+};
+
+struct cbdata {
+ Repo *repo;
+ Repodata *target;
+
+ Stringpool *ownspool;
+ Dirpool *owndirpool;
+
+ Id *keymap;
+ int nkeymap;
+ Id *keymapstart;
+
+ NeedId *needid;
+
+ Id *schema; /* schema construction space */
+ Id *sp; /* pointer in above */
+ Id *oldschema, *oldsp;
+
+ Id *solvschemata;
+ Id *subschemata;
+ int nsubschemata;
+ int current_sub;
+
+ struct extdata *extdata;
+
+ Id *dirused;
+
+ Id vstart;
+
+ Id maxdata;
+ Id lastlen;
+
+ int doingsolvables; /* working on solvables data */
+ int filelistmode;
+};
+
+#define NEEDED_BLOCK 1023
+#define SCHEMATA_BLOCK 31
+#define SCHEMATADATA_BLOCK 255
+#define EXTDATA_BLOCK 4095
+
+static inline void
+data_addid(struct extdata *xd, Id sx)
+{
+ unsigned int x = (unsigned int)sx;
+ unsigned char *dp;
+
+ xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
+ dp = xd->buf + xd->len;
+
+ if (x >= (1 << 14))
+ {
+ if (x >= (1 << 28))
+ *dp++ = (x >> 28) | 128;
+ if (x >= (1 << 21))
+ *dp++ = (x >> 21) | 128;
+ *dp++ = (x >> 14) | 128;
+ }
+ if (x >= (1 << 7))
+ *dp++ = (x >> 7) | 128;
+ *dp++ = x & 127;
+ xd->len = dp - xd->buf;
+}
+
+static inline void
+data_addideof(struct extdata *xd, Id sx, int eof)
+{
+ unsigned int x = (unsigned int)sx;
+ unsigned char *dp;
+
+ xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
+ dp = xd->buf + xd->len;
+
+ if (x >= (1 << 13))
+ {
+ if (x >= (1 << 27))
+ *dp++ = (x >> 27) | 128;
+ if (x >= (1 << 20))
+ *dp++ = (x >> 20) | 128;
+ *dp++ = (x >> 13) | 128;
+ }
+ if (x >= (1 << 6))
+ *dp++ = (x >> 6) | 128;
+ *dp++ = eof ? (x & 63) : (x & 63) | 64;
+ xd->len = dp - xd->buf;
+}
+
+static inline int
+data_addideof_len(Id sx)
+{
+ unsigned int x = (unsigned int)sx;
+ if (x >= (1 << 13))
+ {
+ if (x >= (1 << 27))
+ return 5;
+ return x >= (1 << 20) ? 4 : 3;
+ }
+ return x >= (1 << 6) ? 2 : 1;
+}
+
+static void
+data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
+{
+ if (hx)
+ {
+ if (hx > 7)
+ {
+ data_addid(xd, (Id)(hx >> 3));
+ xd->buf[xd->len - 1] |= 128;
+ hx &= 7;
+ }
+ data_addid(xd, (Id)(x | 0x80000000));
+ xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
+ }
+ else
+ data_addid(xd, (Id)x);
+}
+
+static void
+data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
+{
+ int len, i;
+ Id lids[64], *sids;
+ Id id, old;
+
+ if (!ids)
+ return;
+ if (!*ids)
+ {
+ data_addid(xd, 0);
+ return;
+ }
+ for (len = 0; len < 64 && ids[len]; len++)
+ {
+ Id id = ids[len];
+ if (needid)
+ id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
+ lids[len] = id;
+ }
+ if (ids[len])
+ {
+ for (i = len + 1; ids[i]; i++)
+ ;
+ sids = solv_malloc2(i, sizeof(Id));
+ memcpy(sids, lids, 64 * sizeof(Id));
+ for (; ids[len]; len++)
+ {
+ Id id = ids[len];
+ if (needid)
+ id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
+ sids[len] = id;
+ }
+ }
+ else
+ sids = lids;
+
+ /* That bloody solvable:prereqmarker needs to stay in position :-( */
+ if (needid)
+ marker = needid[marker].need;
+ for (i = 0; i < len; i++)
+ if (sids[i] == marker)
+ break;
+ if (i > 1)
+ solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
+ if ((len - i) > 2)
+ solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
+
+ old = 0;
+
+ /* The differencing above produces many runs of ones and twos. I tried
+ fairly elaborate schemes to RLE those, but they give only very mediocre
+ improvements in compression, as coding the escapes costs quite some
+ space. Even if they are coded only as bits in IDs. The best improvement
+ was about 2.7% for the whole .solv file. It's probably better to
+ invest some complexity into sharing idarrays, than RLEing. */
+ for (i = 0; i < len - 1; i++)
+ {
+ id = sids[i];
+ /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
+ hence all real differences are offsetted by 1. Otherwise we would
+ have to handle negative differences, which would cost code space for
+ the encoding of the sign. We loose the exact mapping of prereq here,
+ but we know the result, so we can recover from that in the reader. */
+ if (id == marker)
+ id = old = 0;
+ else
+ {
+ id = id - old + 1;
+ old = sids[i];
+ }
+ /* XXX If difference is zero we have multiple equal elements,
+ we might want to skip writing them out. */
+ data_addideof(xd, id, 0);
+ }
+ id = sids[i];
+ if (id == marker)
+ id = 0;
+ else
+ id = id - old + 1;
+ data_addideof(xd, id, 1);
+ if (sids != lids)
+ solv_free(sids);
+}
+
+static inline void
+data_addblob(struct extdata *xd, unsigned char *blob, int len)
+{
+ xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
+ memcpy(xd->buf + xd->len, blob, len);
+ xd->len += len;
+}
+
+static inline void
+data_addu32(struct extdata *xd, unsigned int num)
+{
+ unsigned char d[4];
+ d[0] = num >> 24;
+ d[1] = num >> 16;
+ d[2] = num >> 8;
+ d[3] = num;
+ data_addblob(xd, d, 4);
+}
+
+static Id
+putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
+{
+ const char *str = stringpool_id2str(ss, id);
+ id = stringpool_str2id(cbdata->ownspool, str, 1);
+ if (id >= cbdata->needid[0].map)
+ {
+ int oldoff = cbdata->needid[0].map;
+ int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
+ int nrels = cbdata->repo->pool->nrels;
+ cbdata->needid = solv_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
+ if (nrels)
+ memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
+ memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
+ cbdata->needid[0].map = newoff;
+ }
+ return id;
+}
+
+static Id
+putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
+{
+ Id compid, parent;
+
+ parent = dirpool_parent(dp, dir);
+ if (parent)
+ parent = putinowndirpool(cbdata, data, dp, parent);
+ compid = dp->dirs[dir];
+ if (cbdata->ownspool && compid > 1)
+ compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
+ return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
+}
+
+/*
+ * collect usage information about the dirs
+ * 1: dir used, no child of dir used
+ * 2: dir used as parent of another used dir
+ */
+static inline void
+setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
+{
+ if (cbdata->dirused[dir])
+ return;
+ cbdata->dirused[dir] = 1;
+ while ((dir = dirpool_parent(dp, dir)) != 0)
+ {
+ if (cbdata->dirused[dir] == 2)
+ return;
+ if (cbdata->dirused[dir])
+ {
+ cbdata->dirused[dir] = 2;
+ return;
+ }
+ cbdata->dirused[dir] = 2;
+ }
+ cbdata->dirused[0] = 2;
+}
+
+/*
+ * pass 1 callback:
+ * collect key/id/dirid usage information, create needed schemas
+ */
+static int
+repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
+{
+ Id id;
+ int rm;
+
+ if (key->name == REPOSITORY_SOLVABLES)
+ return SEARCH_NEXT_KEY; /* we do not want this one */
+
+ /* hack: ignore some keys, see BUGS */
+ if (data->repodataid != data->repo->nrepodata - 1)
+ if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
+ return SEARCH_NEXT_KEY;
+
+ rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
+ if (!rm)
+ return SEARCH_NEXT_KEY; /* we do not want this one */
+
+ /* record key in schema */
+ if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
+ && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
+ *cbdata->sp++ = rm;
+
+ switch(key->type)
+ {
+ case REPOKEY_TYPE_ID:
+ case REPOKEY_TYPE_IDARRAY:
+ id = kv->id;
+ if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
+ id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
+ incneedid(repo->pool, id, cbdata->needid);
+ break;
+ case REPOKEY_TYPE_DIR:
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ id = kv->id;
+ if (cbdata->owndirpool)
+ putinowndirpool(cbdata, data, &data->dirpool, id);
+ else
+ setdirused(cbdata, &data->dirpool, id);
+ break;
+ case REPOKEY_TYPE_FIXARRAY:
+ if (kv->eof == 0)
+ {
+ if (cbdata->oldschema)
+ {
+ cbdata->target->error = pool_error(cbdata->repo->pool, -1, "nested fixarray structs not yet implemented");
+ return SEARCH_NEXT_KEY;
+ }
+ cbdata->oldschema = cbdata->schema;
+ cbdata->oldsp = cbdata->sp;
+ cbdata->schema = solv_calloc(cbdata->target->nkeys, sizeof(Id));
+ cbdata->sp = cbdata->schema;
+ }
+ else if (kv->eof == 1)
+ {
+ cbdata->current_sub++;
+ *cbdata->sp = 0;
+ cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
+ cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, cbdata->schema, 1);
+#if 0
+ fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
+#endif
+ cbdata->sp = cbdata->schema;
+ }
+ else
+ {
+ solv_free(cbdata->schema);
+ cbdata->schema = cbdata->oldschema;
+ cbdata->sp = cbdata->oldsp;
+ cbdata->oldsp = cbdata->oldschema = 0;
+ }
+ break;
+ case REPOKEY_TYPE_FLEXARRAY:
+ if (kv->entry == 0)
+ {
+ if (kv->eof != 2)
+ *cbdata->sp++ = 0; /* mark start */
+ }
+ else
+ {
+ /* just finished a schema, rewind */
+ Id *sp = cbdata->sp - 1;
+ *sp = 0;
+ while (sp[-1])
+ sp--;
+ cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
+ cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, sp, 1);
+ cbdata->sp = kv->eof == 2 ? sp - 1: sp;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int
+repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
+{
+ struct cbdata *cbdata = vcbdata;
+ Repo *repo = data->repo;
+
+#if 0
+ if (s)
+ fprintf(stderr, "solvable %d (%s): key (%d)%s %d\n", s ? s - repo->pool->solvables : 0, s ? pool_id2str(repo->pool, s->name) : "", key->name, pool_id2str(repo->pool, key->name), key->type);
+#endif
+ return repo_write_collect_needed(cbdata, repo, data, key, kv);
+}
+
+
+/*
+ * pass 2 callback:
+ * encode all of the data into the correct buffers
+ */
+
+static int
+repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
+{
+ int rm;
+ Id id;
+ unsigned int u32;
+ unsigned char v[4];
+ struct extdata *xd;
+ NeedId *needid;
+
+ if (key->name == REPOSITORY_SOLVABLES)
+ return SEARCH_NEXT_KEY;
+
+ /* hack: ignore some keys, see BUGS */
+ if (data->repodataid != data->repo->nrepodata - 1)
+ if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
+ return SEARCH_NEXT_KEY;
+
+ rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
+ if (!rm)
+ return SEARCH_NEXT_KEY; /* we do not want this one */
+
+ if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ xd = cbdata->extdata + rm; /* vertical buffer */
+ if (cbdata->vstart == -1)
+ cbdata->vstart = xd->len;
+ }
+ else
+ xd = cbdata->extdata + 0; /* incore buffer */
+ switch(key->type)
+ {
+ case REPOKEY_TYPE_VOID:
+ case REPOKEY_TYPE_CONSTANT:
+ case REPOKEY_TYPE_CONSTANTID:
+ break;
+ case REPOKEY_TYPE_ID:
+ id = kv->id;
+ if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
+ id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
+ needid = cbdata->needid;
+ id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
+ data_addid(xd, id);
+ break;
+ case REPOKEY_TYPE_IDARRAY:
+ id = kv->id;
+ if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
+ id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
+ needid = cbdata->needid;
+ id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
+ data_addideof(xd, id, kv->eof);
+ break;
+ case REPOKEY_TYPE_STR:
+ data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
+ break;
+ case REPOKEY_TYPE_MD5:
+ data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
+ break;
+ case REPOKEY_TYPE_SHA1:
+ data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
+ break;
+ case REPOKEY_TYPE_SHA224:
+ data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA224);
+ break;
+ case REPOKEY_TYPE_SHA256:
+ data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
+ break;
+ case REPOKEY_TYPE_SHA384:
+ data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA384);
+ break;
+ case REPOKEY_TYPE_SHA512:
+ data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA512);
+ break;
+ case REPOKEY_TYPE_U32:
+ u32 = kv->num;
+ v[0] = u32 >> 24;
+ v[1] = u32 >> 16;
+ v[2] = u32 >> 8;
+ v[3] = u32;
+ data_addblob(xd, v, 4);
+ break;
+ case REPOKEY_TYPE_NUM:
+ data_addid64(xd, kv->num, kv->num2);
+ break;
+ case REPOKEY_TYPE_DIR:
+ id = kv->id;
+ if (cbdata->owndirpool)
+ id = putinowndirpool(cbdata, data, &data->dirpool, id);
+ id = cbdata->dirused[id];
+ data_addid(xd, id);
+ break;
+ case REPOKEY_TYPE_BINARY:
+ data_addid(xd, kv->num);
+ if (kv->num)
+ data_addblob(xd, (unsigned char *)kv->str, kv->num);
+ break;
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ id = kv->id;
+ if (cbdata->owndirpool)
+ id = putinowndirpool(cbdata, data, &data->dirpool, id);
+ id = cbdata->dirused[id];
+ data_addid(xd, id);
+ data_addid(xd, kv->num);
+ data_addideof(xd, kv->num2, kv->eof);
+ break;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ id = kv->id;
+ if (cbdata->owndirpool)
+ id = putinowndirpool(cbdata, data, &data->dirpool, id);
+ id = cbdata->dirused[id];
+ if (cbdata->filelistmode > 0)
+ {
+ xd->len += data_addideof_len(id) + strlen(kv->str) + 1;
+ break;
+ }
+ data_addideof(xd, id, kv->eof);
+ data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
+ if (cbdata->filelistmode < 0)
+ return 0;
+ break;
+ case REPOKEY_TYPE_FIXARRAY:
+ if (kv->eof == 0)
+ {
+ if (kv->num)
+ {
+ data_addid(xd, kv->num);
+ data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
+#if 0
+ fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
+#endif
+ }
+ }
+ else if (kv->eof == 1)
+ {
+ cbdata->current_sub++;
+ }
+ break;
+ case REPOKEY_TYPE_FLEXARRAY:
+ if (!kv->entry)
+ data_addid(xd, kv->num);
+ if (kv->eof != 2)
+ data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
+ if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
+ {
+ if (xd->len - cbdata->lastlen > cbdata->maxdata)
+ cbdata->maxdata = xd->len - cbdata->lastlen;
+ cbdata->lastlen = xd->len;
+ }
+ break;
+ default:
+ cbdata->target->error = pool_error(cbdata->repo->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
+ break;
+ }
+ if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
+ {
+ /* we can re-use old data in the blob here! */
+ data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
+ data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
+ cbdata->vstart = -1;
+ }
+ return 0;
+}
+
+static int
+repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
+{
+ struct cbdata *cbdata = vcbdata;
+ return repo_write_adddata(cbdata, data, key, kv);
+}
+
+/* traverse through directory with first child "dir" */
+static int
+traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
+{
+ Id sib, child;
+ Id parent, lastn;
+
+ parent = n;
+ /* special case for '/', which has to come first */
+ if (parent == 1)
+ dirmap[n++] = 1;
+ for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
+ {
+ if (used && !used[sib])
+ continue;
+ if (sib == 1 && parent == 1)
+ continue; /* already did that one above */
+ dirmap[n++] = sib;
+ }
+
+ /* now go through all the siblings we just added and
+ * do recursive calls on them */
+ lastn = n;
+ for (; parent < lastn; parent++)
+ {
+ sib = dirmap[parent];
+ if (used && used[sib] != 2) /* 2: used as parent */
+ continue;
+ child = dirpool_child(dp, sib);
+ if (child)
+ {
+ dirmap[n++] = -parent; /* start new block */
+ n = traverse_dirs(dp, dirmap, n, child, used);
+ }
+ }
+ return n;
+}
+
+static void
+write_compressed_page(Repodata *data, unsigned char *page, int len)
+{
+ int clen;
+ unsigned char cpage[REPOPAGE_BLOBSIZE];
+
+ clen = repopagestore_compress_page(page, len, cpage, len - 1);
+ if (!clen)
+ {
+ write_u32(data, len * 2);
+ write_blob(data, page, len);
+ }
+ else
+ {
+ write_u32(data, clen * 2 + 1);
+ write_blob(data, cpage, clen);
+ }
+}
+
+static Id verticals[] = {
+ SOLVABLE_AUTHORS,
+ SOLVABLE_DESCRIPTION,
+ SOLVABLE_MESSAGEDEL,
+ SOLVABLE_MESSAGEINS,
+ SOLVABLE_EULA,
+ SOLVABLE_DISKUSAGE,
+ SOLVABLE_FILELIST,
+ SOLVABLE_CHECKSUM,
+ DELTA_CHECKSUM,
+ DELTA_SEQ_NUM,
+ SOLVABLE_PKGID,
+ SOLVABLE_HDRID,
+ SOLVABLE_LEADSIGID,
+ SOLVABLE_CHANGELOG_AUTHOR,
+ SOLVABLE_CHANGELOG_TEXT,
+ 0
+};
+
+static char *languagetags[] = {
+ "solvable:summary:",
+ "solvable:description:",
+ "solvable:messageins:",
+ "solvable:messagedel:",
+ "solvable:eula:",
+ 0
+};
+
+int
+repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
+{
+ const char *keyname;
+ int i;
+
+ for (i = 0; verticals[i]; i++)
+ if (key->name == verticals[i])
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ keyname = pool_id2str(repo->pool, key->name);
+ for (i = 0; languagetags[i] != 0; i++)
+ if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ return KEY_STORAGE_INCORE;
+}
+
+/*
+ * return true if the repodata contains the filelist (and just
+ * the filelist). The same code is used in the dataiterator. The way
+ * it is used is completely wrong, of course, as having the filelist
+ * key does not mean it is used for a specific solvable. Nevertheless
+ * it is better to have it than to write broken solv files.
+ */
+static inline int
+is_filelist_extension(Repodata *data)
+{
+ int j;
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ return 0;
+ return 1;
+}
+
+
+static int
+write_compressed_extdata(Repodata *target, struct extdata *xd, unsigned char *vpage, int lpage)
+{
+ unsigned char *dp = xd->buf;
+ int l = xd->len;
+ while (l)
+ {
+ int ll = REPOPAGE_BLOBSIZE - lpage;
+ if (l < ll)
+ ll = l;
+ memcpy(vpage + lpage, dp, ll);
+ dp += ll;
+ lpage += ll;
+ l -= ll;
+ if (lpage == REPOPAGE_BLOBSIZE)
+ {
+ write_compressed_page(target, vpage, lpage);
+ lpage = 0;
+ }
+ }
+ return lpage;
+}
+
+/*
+ * Repo
+ */
+
+/*
+ * the code works the following way:
+ *
+ * 1) find which keys should be written
+ * 2) collect usage information for keys/ids/dirids, create schema
+ * data
+ * 3) use usage information to create mapping tables, so that often
+ * used ids get a lower number
+ * 4) encode data into buffers using the mapping tables
+ * 5) write everything to disk
+ */
+int
+repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
+{
+ Pool *pool = repo->pool;
+ int i, j, n, lastfilelistn;
+ Solvable *s;
+ NeedId *needid;
+ int nstrings, nrels;
+ unsigned int sizeid;
+ unsigned int solv_flags;
+ Reldep *ran;
+ Id *idarraydata;
+
+ Id id, *sp;
+
+ Id *dirmap;
+ int ndirmap;
+ Id *keyused;
+ unsigned char *repodataused;
+ int anyrepodataused = 0;
+ int anysolvableused = 0;
+
+ struct cbdata cbdata;
+ int clonepool;
+ Repokey *key;
+ int poolusage, dirpoolusage, idused, dirused;
+ int reloff;
+
+ Repodata *data, *dirpooldata;
+
+ Repodata target;
+
+ Stringpool *spool;
+ Dirpool *dirpool;
+
+ Id mainschema;
+
+ struct extdata *xd;
+
+ Id type_constantid = REPOKEY_TYPE_CONSTANTID;
+
+
+ memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.repo = repo;
+ cbdata.target = ⌖
+
+ repodata_initdata(&target, repo, 1);
+
+ /* go through all repodata and find the keys we need */
+ /* also unify keys */
+ /* keymapstart - maps repo number to keymap offset */
+ /* keymap - maps repo key to my key, 0 -> not used */
+
+ /* start with all KEY_STORAGE_SOLVABLE ids */
+
+ n = ID_NUM_INTERNAL;
+ FOR_REPODATAS(repo, i, data)
+ n += data->nkeys;
+ cbdata.keymap = solv_calloc(n, sizeof(Id));
+ cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
+ repodataused = solv_calloc(repo->nrepodata, 1);
+
+ clonepool = 0;
+ poolusage = 0;
+
+ /* add keys for STORAGE_SOLVABLE */
+ for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
+ {
+ Repokey keyd;
+ keyd.name = i;
+ if (i < SOLVABLE_PROVIDES)
+ keyd.type = REPOKEY_TYPE_ID;
+ else if (i < RPM_RPMDBID)
+ keyd.type = REPOKEY_TYPE_REL_IDARRAY;
+ else
+ keyd.type = REPOKEY_TYPE_NUM;
+ keyd.size = 0;
+ keyd.storage = KEY_STORAGE_SOLVABLE;
+ if (keyfilter)
+ {
+ keyd.storage = keyfilter(repo, &keyd, kfdata);
+ if (keyd.storage == KEY_STORAGE_DROPPED)
+ continue;
+ keyd.storage = KEY_STORAGE_SOLVABLE;
+ }
+ poolusage = 1;
+ clonepool = 1;
+ cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
+ }
+
+ if (repo->nsolvables)
+ {
+ Repokey keyd;
+ keyd.name = REPOSITORY_SOLVABLES;
+ keyd.type = REPOKEY_TYPE_FLEXARRAY;
+ keyd.size = 0;
+ keyd.storage = KEY_STORAGE_INCORE;
+ cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
+ }
+
+ dirpoolusage = 0;
+
+ spool = 0;
+ dirpool = 0;
+ dirpooldata = 0;
+ n = ID_NUM_INTERNAL;
+ lastfilelistn = 0;
+ FOR_REPODATAS(repo, i, data)
+ {
+ cbdata.keymapstart[i] = n;
+ cbdata.keymap[n++] = 0; /* key 0 */
+ idused = 0;
+ dirused = 0;
+ if (keyfilter)
+ {
+ Repokey keyd;
+ /* check if we want this repodata */
+ memset(&keyd, 0, sizeof(keyd));
+ keyd.name = 1;
+ keyd.type = 1;
+ keyd.size = i;
+ if (keyfilter(repo, &keyd, kfdata) == -1)
+ continue;
+ }
+ for (j = 1; j < data->nkeys; j++, n++)
+ {
+ key = data->keys + j;
+ if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
+ {
+ cbdata.keymap[n] = cbdata.keymap[key->name];
+ continue;
+ }
+ if (key->type == REPOKEY_TYPE_DELETED)
+ {
+ cbdata.keymap[n] = 0;
+ continue;
+ }
+ if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
+ {
+ Repokey keyd = *key;
+ keyd.size = repodata_globalize_id(data, key->size, 1);
+ id = repodata_key2id(&target, &keyd, 0);
+ }
+ else
+ id = repodata_key2id(&target, key, 0);
+ if (!id)
+ {
+ Repokey keyd = *key;
+ keyd.storage = KEY_STORAGE_INCORE;
+ if (keyd.type == REPOKEY_TYPE_CONSTANTID)
+ keyd.size = repodata_globalize_id(data, key->size, 1);
+ else if (keyd.type != REPOKEY_TYPE_CONSTANT)
+ keyd.size = 0;
+ if (keyfilter)
+ {
+ keyd.storage = keyfilter(repo, &keyd, kfdata);
+ if (keyd.storage == KEY_STORAGE_DROPPED)
+ {
+ cbdata.keymap[n] = 0;
+ continue;
+ }
+ }
+ id = repodata_key2id(&target, &keyd, 1);
+ }
+ cbdata.keymap[n] = id;
+ /* load repodata if not already loaded */
+ if (data->state == REPODATA_STUB)
+ {
+ if (data->loadcallback)
+ data->loadcallback(data);
+ else
+ data->state = REPODATA_ERROR;
+ if (data->state != REPODATA_ERROR)
+ {
+ /* redo this repodata! */
+ j = 0;
+ n = cbdata.keymapstart[i];
+ continue;
+ }
+ }
+ if (data->state == REPODATA_ERROR)
+ {
+ /* too bad! */
+ cbdata.keymap[n] = 0;
+ continue;
+ }
+
+ repodataused[i] = 1;
+ anyrepodataused = 1;
+ if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
+ key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
+ idused = 1;
+ else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
+ {
+ idused = 1; /* dirs also use ids */
+ dirused = 1;
+ }
+ if (key->type == REPOKEY_TYPE_DIRSTRARRAY && key->name == SOLVABLE_FILELIST)
+ {
+ /* is this a file list extension */
+ if (is_filelist_extension(data))
+ {
+ /* hmm, we have a file list extension. Kill filelist of other repodata.
+ * XXX: this is wrong, as the extension does not need to cover all
+ * solvables of the other repodata */
+ if (lastfilelistn)
+ cbdata.keymap[lastfilelistn] = 0;
+ }
+ else
+ lastfilelistn = n;
+ }
+ }
+ if (idused)
+ {
+ if (data->localpool)
+ {
+ if (poolusage)
+ poolusage = 3; /* need own pool */
+ else
+ {
+ poolusage = 2;
+ spool = &data->spool;
+ }
+ }
+ else
+ {
+ if (poolusage == 0)
+ poolusage = 1;
+ else if (poolusage != 1)
+ poolusage = 3; /* need own pool */
+ }
+ }
+ if (dirused)
+ {
+ if (dirpoolusage)
+ dirpoolusage = 3; /* need own dirpool */
+ else
+ {
+ dirpoolusage = 2;
+ dirpool = &data->dirpool;
+ dirpooldata = data;
+ }
+ }
+ }
+ cbdata.nkeymap = n;
+
+ /* 0: no pool needed at all */
+ /* 1: use global pool */
+ /* 2: use repodata local pool */
+ /* 3: need own pool */
+ if (poolusage == 3)
+ {
+ spool = &target.spool;
+ /* hack: reuse global pool data so we don't have to map pool ids */
+ if (clonepool)
+ {
+ stringpool_free(spool);
+ stringpool_clone(spool, &pool->ss);
+ }
+ cbdata.ownspool = spool;
+ }
+ else if (poolusage == 0 || poolusage == 1)
+ {
+ poolusage = 1;
+ spool = &pool->ss;
+ }
+
+ if (dirpoolusage == 3)
+ {
+ dirpool = &target.dirpool;
+ dirpooldata = 0;
+ cbdata.owndirpool = dirpool;
+ }
+ else if (dirpool)
+ cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
+
+
+/********************************************************************/
+#if 0
+fprintf(stderr, "poolusage: %d\n", poolusage);
+fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
+fprintf(stderr, "nkeys: %d\n", target.nkeys);
+for (i = 1; i < target.nkeys; i++)
+ fprintf(stderr, " %2d: %s[%d] %d %d %d\n", i, pool_id2str(pool, target.keys[i].name), target.keys[i].name, target.keys[i].type, target.keys[i].size, target.keys[i].storage);
+#endif
+
+ /* copy keys if requested */
+ if (keyq)
+ {
+ queue_empty(keyq);
+ for (i = 1; i < target.nkeys; i++)
+ queue_push2(keyq, target.keys[i].name, target.keys[i].type);
+ }
+
+ if (poolusage > 1)
+ {
+ /* put all the keys we need in our string pool */
+ /* put mapped ids right into target.keys */
+ for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
+ {
+ key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
+ if (key->type == REPOKEY_TYPE_CONSTANTID)
+ {
+ key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
+ type_constantid = key->type;
+ key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
+ }
+ else
+ key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
+ }
+ if (poolusage == 2)
+ stringpool_freehash(spool); /* free some mem */
+ }
+
+
+/********************************************************************/
+
+ /* set needed count of all strings and rels,
+ * find which keys are used in the solvables
+ * put all strings in own spool
+ */
+
+ reloff = spool->nstrings;
+ if (poolusage == 3)
+ reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
+
+ needid = calloc(reloff + pool->nrels, sizeof(*needid));
+ needid[0].map = reloff;
+
+ cbdata.needid = needid;
+ cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
+ cbdata.sp = cbdata.schema;
+ cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
+
+ /* create main schema */
+ cbdata.sp = cbdata.schema;
+ /* collect all other data from all repodatas */
+ /* XXX: merge arrays of equal keys? */
+ FOR_REPODATAS(repo, j, data)
+ {
+ if (!repodataused[j])
+ continue;
+ repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
+ }
+ sp = cbdata.sp;
+ /* add solvables if needed (may revert later) */
+ if (repo->nsolvables)
+ {
+ *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
+ target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
+ }
+ *sp = 0;
+ mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
+
+ idarraydata = repo->idarraydata;
+
+ anysolvableused = 0;
+ cbdata.doingsolvables = 1;
+ for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
+ {
+ if (s->repo != repo)
+ continue;
+
+ /* set schema info, keep in sync with further down */
+ sp = cbdata.schema;
+ if (cbdata.keymap[SOLVABLE_NAME])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_NAME];
+ needid[s->name].need++;
+ }
+ if (cbdata.keymap[SOLVABLE_ARCH])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_ARCH];
+ needid[s->arch].need++;
+ }
+ if (cbdata.keymap[SOLVABLE_EVR])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_EVR];
+ needid[s->evr].need++;
+ }
+ if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
+ needid[s->vendor].need++;
+ }
+ if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
+ target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
+ }
+ if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
+ target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
+ }
+ if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
+ target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
+ }
+ if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
+ target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
+ }
+ if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
+ target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
+ }
+ if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
+ target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
+ }
+ if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
+ target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
+ }
+ if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
+ {
+ *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
+ target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
+ }
+ if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
+ {
+ *sp++ = cbdata.keymap[RPM_RPMDBID];
+ target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
+ }
+ cbdata.sp = sp;
+
+ if (anyrepodataused)
+ {
+ FOR_REPODATAS(repo, j, data)
+ {
+ if (!repodataused[j])
+ continue;
+ if (i < data->start || i >= data->end)
+ continue;
+ repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
+ needid = cbdata.needid;
+ }
+ }
+ *cbdata.sp = 0;
+ cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
+ if (cbdata.solvschemata[n])
+ anysolvableused = 1;
+ n++;
+ }
+ cbdata.doingsolvables = 0;
+ assert(n == repo->nsolvables);
+
+ if (repo->nsolvables && !anysolvableused)
+ {
+ /* strip off solvable from the main schema */
+ target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
+ sp = cbdata.schema;
+ for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
+ {
+ *sp = target.schemadata[target.schemata[mainschema] + i];
+ if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
+ sp++;
+ }
+ assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
+ *sp = 0;
+ target.schemadatalen = target.schemata[mainschema];
+ target.nschemata--;
+ repodata_free_schemahash(&target);
+ mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
+ }
+
+/********************************************************************/
+
+ /* remove unused keys */
+ keyused = solv_calloc(target.nkeys, sizeof(Id));
+ for (i = 1; i < (int)target.schemadatalen; i++)
+ keyused[target.schemadata[i]] = 1;
+ keyused[0] = 0;
+ for (n = i = 1; i < target.nkeys; i++)
+ {
+ if (!keyused[i])
+ continue;
+ keyused[i] = n;
+ if (i != n)
+ {
+ target.keys[n] = target.keys[i];
+ if (keyq)
+ {
+ keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
+ keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
+ }
+ }
+ n++;
+ }
+ target.nkeys = n;
+ if (keyq)
+ queue_truncate(keyq, 2 * n - 2);
+
+ /* update schema data to the new key ids */
+ for (i = 1; i < (int)target.schemadatalen; i++)
+ target.schemadata[i] = keyused[target.schemadata[i]];
+ /* update keymap to the new key ids */
+ for (i = 0; i < cbdata.nkeymap; i++)
+ cbdata.keymap[i] = keyused[cbdata.keymap[i]];
+ keyused = solv_free(keyused);
+
+ /* increment needid of the used keys, they are already mapped to
+ * the correct string pool */
+ for (i = 1; i < target.nkeys; i++)
+ {
+ if (target.keys[i].type == type_constantid)
+ needid[target.keys[i].size].need++;
+ needid[target.keys[i].name].need++;
+ needid[target.keys[i].type].need++;
+ }
+
+/********************************************************************/
+
+ if (dirpool && cbdata.dirused && !cbdata.dirused[0])
+ {
+ /* no dirs used at all */
+ cbdata.dirused = solv_free(cbdata.dirused);
+ dirpool = 0;
+ }
+
+ /* increment need id for used dir components */
+ if (dirpool)
+ {
+ /* if we have own dirpool, all entries in it are used.
+ also, all comp ids are already mapped by putinowndirpool(),
+ so we can simply increment needid.
+ (owndirpool != 0, dirused == 0, dirpooldata == 0) */
+ /* else we re-use a dirpool of repodata "dirpooldata".
+ dirused tells us which of the ids are used.
+ we need to map comp ids if we generate a new pool.
+ (owndirpool == 0, dirused != 0, dirpooldata != 0) */
+ for (i = 1; i < dirpool->ndirs; i++)
+ {
+#if 0
+fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
+#endif
+ if (cbdata.dirused && !cbdata.dirused[i])
+ continue;
+ id = dirpool->dirs[i];
+ if (id <= 0)
+ continue;
+ if (dirpooldata && cbdata.ownspool && id > 1)
+ {
+ id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
+ needid = cbdata.needid;
+ }
+ needid[id].need++;
+ }
+ }
+
+
+/********************************************************************/
+
+ /*
+ * create mapping table, new keys are sorted by needid[].need
+ *
+ * needid[key].need : old key -> new key
+ * needid[key].map : new key -> old key
+ */
+
+ /* zero out id 0 and rel 0 just in case */
+ reloff = needid[0].map;
+ needid[0].need = 0;
+ needid[reloff].need = 0;
+
+ for (i = 1; i < reloff + pool->nrels; i++)
+ needid[i].map = i;
+
+#if 0
+ solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
+#else
+ /* make first entry '' */
+ needid[1].need = 1;
+ solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
+#endif
+ solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
+ /* now needid is in new order, needid[newid].map -> oldid */
+
+ /* calculate string space size, also zero out needid[].need */
+ sizeid = 0;
+ for (i = 1; i < reloff; i++)
+ {
+ if (!needid[i].need)
+ break; /* as we have sorted, every entry after this also has need == 0 */
+ needid[i].need = 0;
+ sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
+ }
+ nstrings = i; /* our new string id end */
+
+ /* make needid[oldid].need point to newid */
+ for (i = 1; i < nstrings; i++)
+ needid[needid[i].map].need = i;
+
+ /* same as above for relations */
+ for (i = 0; i < pool->nrels; i++)
+ {
+ if (!needid[reloff + i].need)
+ break;
+ needid[reloff + i].need = 0;
+ }
+ nrels = i; /* our new rel id end */
+
+ for (i = 0; i < nrels; i++)
+ needid[needid[reloff + i].map].need = nstrings + i;
+
+ /* now we have: needid[oldid].need -> newid
+ needid[newid].map -> oldid
+ both for strings and relations */
+
+
+/********************************************************************/
+
+ ndirmap = 0;
+ dirmap = 0;
+ if (dirpool)
+ {
+ /* create our new target directory structure by traversing through all
+ * used dirs. This will concatenate blocks with the same parent
+ * directory into single blocks.
+ * Instead of components, traverse_dirs stores the old dirids,
+ * we will change this in the second step below */
+ /* (dirpooldata and dirused are 0 if we have our own dirpool) */
+ if (cbdata.dirused && !cbdata.dirused[1])
+ cbdata.dirused[1] = 1; /* always want / entry */
+ dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
+ dirmap[0] = 0;
+ ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
+
+ /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
+ /* change dirmap so that it maps from "new dirid" to "new compid" */
+ if (!cbdata.dirused)
+ cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
+ memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
+ for (i = 1; i < ndirmap; i++)
+ {
+ if (dirmap[i] <= 0)
+ continue;
+ cbdata.dirused[dirmap[i]] = i;
+ id = dirpool->dirs[dirmap[i]];
+ if (dirpooldata && cbdata.ownspool && id > 1)
+ id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
+ dirmap[i] = needid[id].need;
+ }
+ /* now the new target directory structure is complete (dirmap), and we have
+ * dirused[olddirid] -> newdirid */
+ }
+
+/********************************************************************/
+
+ /* collect all data
+ * we use extdata[0] for incore data and extdata[keyid] for vertical data
+ */
+
+ cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
+
+ xd = cbdata.extdata;
+ cbdata.current_sub = 0;
+ /* add main schema */
+ cbdata.lastlen = 0;
+ data_addid(xd, mainschema);
+
+#if 1
+ FOR_REPODATAS(repo, j, data)
+ {
+ if (!repodataused[j])
+ continue;
+ repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
+ }
+#endif
+
+ if (xd->len - cbdata.lastlen > cbdata.maxdata)
+ cbdata.maxdata = xd->len - cbdata.lastlen;
+ cbdata.lastlen = xd->len;
+
+ if (anysolvableused)
+ {
+ data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
+ cbdata.doingsolvables = 1;
+
+ /* check if we can do the special filelist memory optimization */
+ if (anyrepodataused)
+ {
+ for (i = 1; i < target.nkeys; i++)
+ if (target.keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
+ cbdata.filelistmode |= cbdata.filelistmode == 0 && target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY ? 1 : 2;
+ else if (target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY)
+ cbdata.filelistmode = 2;
+ if (cbdata.filelistmode != 1)
+ cbdata.filelistmode = 0;
+ }
+
+ for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
+ {
+ if (s->repo != repo)
+ continue;
+ data_addid(xd, cbdata.solvschemata[n]);
+ if (cbdata.keymap[SOLVABLE_NAME])
+ data_addid(xd, needid[s->name].need);
+ if (cbdata.keymap[SOLVABLE_ARCH])
+ data_addid(xd, needid[s->arch].need);
+ if (cbdata.keymap[SOLVABLE_EVR])
+ data_addid(xd, needid[s->evr].need);
+ if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
+ data_addid(xd, needid[s->vendor].need);
+ if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
+ data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
+ if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
+ data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
+ if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
+ data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
+ if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
+ data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
+ if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
+ data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
+ if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
+ data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
+ if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
+ data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
+ if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
+ data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
+ if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
+ data_addid(xd, repo->rpmdbid[i - repo->start]);
+ if (anyrepodataused)
+ {
+ cbdata.vstart = -1;
+ FOR_REPODATAS(repo, j, data)
+ {
+ if (!repodataused[j])
+ continue;
+ if (i < data->start || i >= data->end)
+ continue;
+ repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
+ }
+ }
+ if (xd->len - cbdata.lastlen > cbdata.maxdata)
+ cbdata.maxdata = xd->len - cbdata.lastlen;
+ cbdata.lastlen = xd->len;
+ n++;
+ }
+ cbdata.doingsolvables = 0;
+ }
+
+ assert(cbdata.current_sub == cbdata.nsubschemata);
+ if (cbdata.subschemata)
+ {
+ cbdata.subschemata = solv_free(cbdata.subschemata);
+ cbdata.nsubschemata = 0;
+ }
+
+/********************************************************************/
+
+ target.fp = fp;
+
+ /* write header */
+
+ /* write file header */
+ write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
+ write_u32(&target, SOLV_VERSION_8);
+
+
+ /* write counts */
+ write_u32(&target, nstrings);
+ write_u32(&target, nrels);
+ write_u32(&target, ndirmap);
+ write_u32(&target, anysolvableused ? repo->nsolvables : 0);
+ write_u32(&target, target.nkeys);
+ write_u32(&target, target.nschemata);
+ solv_flags = 0;
+ solv_flags |= SOLV_FLAG_PREFIX_POOL;
+ solv_flags |= SOLV_FLAG_SIZE_BYTES;
+ write_u32(&target, solv_flags);
+
+ if (nstrings)
+ {
+ /*
+ * calculate prefix encoding of the strings
+ */
+ unsigned char *prefixcomp = solv_malloc(nstrings);
+ unsigned int compsum = 0;
+ char *old_str = "";
+
+ prefixcomp[0] = 0;
+ for (i = 1; i < nstrings; i++)
+ {
+ char *str = spool->stringspace + spool->strings[needid[i].map];
+ int same;
+ for (same = 0; same < 255; same++)
+ if (!old_str[same] || old_str[same] != str[same])
+ break;
+ prefixcomp[i] = same;
+ compsum += same;
+ old_str = str;
+ }
+
+ /*
+ * write strings
+ */
+ write_u32(&target, sizeid);
+ /* we save compsum bytes but need 1 extra byte for every string */
+ write_u32(&target, sizeid + nstrings - 1 - compsum);
+ for (i = 1; i < nstrings; i++)
+ {
+ char *str = spool->stringspace + spool->strings[needid[i].map];
+ write_u8(&target, prefixcomp[i]);
+ write_str(&target, str + prefixcomp[i]);
+ }
+ solv_free(prefixcomp);
+ }
+ else
+ {
+ write_u32(&target, 0);
+ write_u32(&target, 0);
+ }
+
+ /*
+ * write RelDeps
+ */
+ for (i = 0; i < nrels; i++)
+ {
+ ran = pool->rels + (needid[reloff + i].map - reloff);
+ write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
+ write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
+ write_u8(&target, ran->flags);
+ }
+
+ /*
+ * write dirs (skip both root and / entry)
+ */
+ for (i = 2; i < ndirmap; i++)
+ {
+ if (dirmap[i] > 0)
+ write_id(&target, dirmap[i]);
+ else
+ write_id(&target, nstrings - dirmap[i]);
+ }
+ solv_free(dirmap);
+
+ /*
+ * write keys
+ */
+ for (i = 1; i < target.nkeys; i++)
+ {
+ write_id(&target, needid[target.keys[i].name].need);
+ write_id(&target, needid[target.keys[i].type].need);
+ if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ if (target.keys[i].type == type_constantid)
+ write_id(&target, needid[target.keys[i].size].need);
+ else
+ write_id(&target, target.keys[i].size);
+ }
+ else
+ write_id(&target, cbdata.extdata[i].len);
+ write_id(&target, target.keys[i].storage);
+ }
+
+ /*
+ * write schemata
+ */
+ write_id(&target, target.schemadatalen); /* XXX -1? */
+ for (i = 1; i < target.nschemata; i++)
+ write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
+
+/********************************************************************/
+
+ write_id(&target, cbdata.maxdata);
+ write_id(&target, cbdata.extdata[0].len);
+ if (cbdata.extdata[0].len)
+ write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
+ solv_free(cbdata.extdata[0].buf);
+
+ /* do we have vertical data? */
+ for (i = 1; i < target.nkeys; i++)
+ if (cbdata.extdata[i].len)
+ break;
+ if (i < target.nkeys)
+ {
+ /* yes, write it in pages */
+ unsigned char vpage[REPOPAGE_BLOBSIZE];
+ int lpage = 0;
+
+ write_u32(&target, REPOPAGE_BLOBSIZE);
+ for (i = 1; i < target.nkeys; i++)
+ if (cbdata.extdata[i].len)
+ {
+ if (cbdata.filelistmode)
+ break;
+ lpage = write_compressed_extdata(&target, cbdata.extdata + i, vpage, lpage);
+ }
+ if (cbdata.filelistmode && i < target.nkeys)
+ {
+ /* ok, just this single extdata, which is a filelist */
+ xd = cbdata.extdata + i;
+ xd->len = 0;
+ cbdata.filelistmode = -1;
+ for (j = 0; j < cbdata.nkeymap; j++)
+ if (cbdata.keymap[j] != i)
+ cbdata.keymap[j] = 0;
+ for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
+ {
+ if (s->repo != repo)
+ continue;
+ FOR_REPODATAS(repo, j, data)
+ {
+ if (!repodataused[j])
+ continue;
+ if (i < data->start || i >= data->end)
+ continue;
+ repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
+ }
+ if (xd->len > 1024 * 1024)
+ {
+ lpage = write_compressed_extdata(&target, xd, vpage, lpage);
+ xd->len = 0;
+ }
+ }
+ if (xd->len)
+ lpage = write_compressed_extdata(&target, xd, vpage, lpage);
+ }
+ if (lpage)
+ write_compressed_page(&target, vpage, lpage);
+ }
+
+ for (i = 1; i < target.nkeys; i++)
+ solv_free(cbdata.extdata[i].buf);
+ solv_free(cbdata.extdata);
+
+ target.fp = 0;
+ repodata_freedata(&target);
+
+ solv_free(needid);
+ solv_free(cbdata.solvschemata);
+ solv_free(cbdata.schema);
+
+ solv_free(cbdata.keymap);
+ solv_free(cbdata.keymapstart);
+ solv_free(cbdata.dirused);
+ solv_free(repodataused);
+ return target.error;
+}
+
+struct repodata_write_data {
+ int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
+ void *kfdata;
+ int repodataid;
+};
+
+static int
+repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
+{
+ struct repodata_write_data *wd = kfdata;
+
+ /* XXX: special repodata selection hack */
+ if (key->name == 1 && key->size != wd->repodataid)
+ return -1;
+ if (key->storage == KEY_STORAGE_SOLVABLE)
+ return KEY_STORAGE_DROPPED; /* not part of this repodata */
+ if (wd->keyfilter)
+ return (*wd->keyfilter)(repo, key, wd->kfdata);
+ return key->storage;
+}
+
+int
+repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
+{
+ struct repodata_write_data wd;
+
+ wd.keyfilter = keyfilter;
+ wd.kfdata = kfdata;
+ wd.repodataid = data->repodataid;
+ return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
+}
+
+int
+repodata_write(Repodata *data, FILE *fp)
+{
+ return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
+}
+
+int
+repo_write(Repo *repo, FILE *fp)
+{
+ return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_write.h
+ *
+ */
+
+#ifndef REPO_WRITE_H
+#define REPO_WRITE_H
+
+#include <stdio.h>
+
+#include "repo.h"
+#include "queue.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int repo_write(Repo *repo, FILE *fp);
+extern int repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq);
+
+extern int repodata_write(Repodata *data , FILE *fp);
+extern int repodata_write_filtered(Repodata *data , FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq);
+
+extern int repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repodata.c
+ *
+ * Manage data coming from one repository
+ *
+ * a repository can contain multiple repodata entries, consisting of
+ * different sets of keys and different sets of solvables
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <fnmatch.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <regex.h>
+
+#include "repo.h"
+#include "pool.h"
+#include "poolid_private.h"
+#include "util.h"
+#include "hash.h"
+#include "chksum.h"
+
+#include "repopack.h"
+#include "repopage.h"
+
+#define REPODATA_BLOCK 255
+
+static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
+
+void
+repodata_initdata(Repodata *data, Repo *repo, int localpool)
+{
+ memset(data, 0, sizeof (*data));
+ data->repodataid = data - repo->repodata;
+ data->repo = repo;
+ data->localpool = localpool;
+ if (localpool)
+ stringpool_init_empty(&data->spool);
+ /* dirpool_init(&data->dirpool); just zeros out again */
+ data->keys = solv_calloc(1, sizeof(Repokey));
+ data->nkeys = 1;
+ data->schemata = solv_calloc(1, sizeof(Id));
+ data->schemadata = solv_calloc(1, sizeof(Id));
+ data->nschemata = 1;
+ data->schemadatalen = 1;
+ repopagestore_init(&data->store);
+}
+
+void
+repodata_freedata(Repodata *data)
+{
+ int i;
+
+ solv_free(data->keys);
+
+ solv_free(data->schemata);
+ solv_free(data->schemadata);
+ solv_free(data->schematahash);
+
+ stringpool_free(&data->spool);
+ dirpool_free(&data->dirpool);
+
+ solv_free(data->mainschemaoffsets);
+ solv_free(data->incoredata);
+ solv_free(data->incoreoffset);
+ solv_free(data->verticaloffset);
+
+ repopagestore_free(&data->store);
+
+ solv_free(data->vincore);
+
+ if (data->attrs)
+ for (i = 0; i < data->end - data->start; i++)
+ solv_free(data->attrs[i]);
+ solv_free(data->attrs);
+ if (data->xattrs)
+ for (i = 0; i < data->nxattrs; i++)
+ solv_free(data->xattrs[i]);
+ solv_free(data->xattrs);
+
+ solv_free(data->attrdata);
+ solv_free(data->attriddata);
+ solv_free(data->attrnum64data);
+
+ solv_free(data->dircache);
+}
+
+void
+repodata_free(Repodata *data)
+{
+ Repo *repo = data->repo;
+ int i = data - repo->repodata;
+ if (i == 0)
+ return;
+ repodata_freedata(data);
+ if (i < repo->nrepodata - 1)
+ {
+ /* whoa! this changes the repodataids! */
+ memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
+ for (; i < repo->nrepodata - 1; i++)
+ repo->repodata[i].repodataid = i;
+ }
+ repo->nrepodata--;
+ if (repo->nrepodata == 1)
+ {
+ repo->repodata = solv_free(repo->repodata);
+ repo->nrepodata = 0;
+ }
+}
+
+void
+repodata_empty(Repodata *data, int localpool)
+{
+ void (*loadcallback)(Repodata *) = data->loadcallback;
+ int state = data->state;
+ repodata_freedata(data);
+ repodata_initdata(data, data->repo, localpool);
+ data->state = state;
+ data->loadcallback = loadcallback;
+}
+
+
+/***************************************************************
+ * key pool management
+ */
+
+/* this is not so time critical that we need a hash, so we do a simple
+ * linear search */
+Id
+repodata_key2id(Repodata *data, Repokey *key, int create)
+{
+ Id keyid;
+
+ for (keyid = 1; keyid < data->nkeys; keyid++)
+ if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
+ {
+ if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
+ continue;
+ break;
+ }
+ if (keyid == data->nkeys)
+ {
+ if (!create)
+ return 0;
+ /* allocate new key */
+ data->keys = solv_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
+ data->keys[data->nkeys++] = *key;
+ if (data->verticaloffset)
+ {
+ data->verticaloffset = solv_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
+ data->verticaloffset[data->nkeys - 1] = 0;
+ }
+ data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
+ }
+ return keyid;
+}
+
+
+/***************************************************************
+ * schema pool management
+ */
+
+#define SCHEMATA_BLOCK 31
+#define SCHEMATADATA_BLOCK 255
+
+Id
+repodata_schema2id(Repodata *data, Id *schema, int create)
+{
+ int h, len, i;
+ Id *sp, cid;
+ Id *schematahash;
+
+ if (!*schema)
+ return 0; /* XXX: allow empty schema? */
+ if ((schematahash = data->schematahash) == 0)
+ {
+ data->schematahash = schematahash = solv_calloc(256, sizeof(Id));
+ for (i = 1; i < data->nschemata; i++)
+ {
+ for (sp = data->schemadata + data->schemata[i], h = 0; *sp;)
+ h = h * 7 + *sp++;
+ h &= 255;
+ schematahash[h] = i;
+ }
+ data->schemadata = solv_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
+ data->schemata = solv_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
+ }
+
+ for (sp = schema, len = 0, h = 0; *sp; len++)
+ h = h * 7 + *sp++;
+ h &= 255;
+ len++;
+
+ cid = schematahash[h];
+ if (cid)
+ {
+ if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
+ return cid;
+ /* cache conflict, do a slow search */
+ for (cid = 1; cid < data->nschemata; cid++)
+ if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
+ return cid;
+ }
+ /* a new one */
+ if (!create)
+ return 0;
+ data->schemadata = solv_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
+ data->schemata = solv_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
+ /* add schema */
+ memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
+ data->schemata[data->nschemata] = data->schemadatalen;
+ data->schemadatalen += len;
+ schematahash[h] = data->nschemata;
+#if 0
+fprintf(stderr, "schema2id: new schema\n");
+#endif
+ return data->nschemata++;
+}
+
+void
+repodata_free_schemahash(Repodata *data)
+{
+ data->schematahash = solv_free(data->schematahash);
+ /* shrink arrays */
+ data->schemata = solv_realloc2(data->schemata, data->nschemata, sizeof(Id));
+ data->schemadata = solv_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
+}
+
+
+/***************************************************************
+ * dir pool management
+ */
+
+#ifndef HAVE_STRCHRNUL
+static inline const char *strchrnul(const char *str, char x)
+{
+ const char *p = strchr(str, x);
+ return p ? p : str + strlen(str);
+}
+#endif
+
+#define DIRCACHE_SIZE 41 /* < 1k */
+
+#ifdef DIRCACHE_SIZE
+struct dircache {
+ Id ids[DIRCACHE_SIZE];
+ char str[(DIRCACHE_SIZE * (DIRCACHE_SIZE - 1)) / 2];
+};
+#endif
+
+Id
+repodata_str2dir(Repodata *data, const char *dir, int create)
+{
+ Id id, parent;
+#ifdef DIRCACHE_SIZE
+ const char *dirs;
+#endif
+ const char *dire;
+
+ parent = 0;
+ if (!*dir)
+ return 0;
+ while (*dir == '/' && dir[1] == '/')
+ dir++;
+ if (*dir == '/' && !dir[1])
+ {
+ if (data->dirpool.ndirs)
+ return 1;
+ return dirpool_add_dir(&data->dirpool, 0, 1, create);
+ }
+#ifdef DIRCACHE_SIZE
+ dirs = dir;
+ if (data->dircache)
+ {
+ int l;
+ struct dircache *dircache = data->dircache;
+ l = strlen(dir);
+ while (l > 0)
+ {
+ if (l < DIRCACHE_SIZE && dircache->ids[l] && !memcmp(dircache->str + l * (l - 1) / 2, dir, l))
+ {
+ parent = dircache->ids[l];
+ dir += l;
+ if (!*dir)
+ return parent;
+ while (*dir == '/')
+ dir++;
+ break;
+ }
+ while (--l)
+ if (dir[l] == '/')
+ break;
+ }
+ }
+#endif
+ while (*dir)
+ {
+ dire = strchrnul(dir, '/');
+ if (data->localpool)
+ id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
+ else
+ id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
+ if (!id)
+ return 0;
+ parent = dirpool_add_dir(&data->dirpool, parent, id, create);
+ if (!parent)
+ return 0;
+#ifdef DIRCACHE_SIZE
+ if (!data->dircache)
+ data->dircache = solv_calloc(1, sizeof(struct dircache));
+ if (data->dircache)
+ {
+ int l = dire - dirs;
+ if (l < DIRCACHE_SIZE)
+ {
+ data->dircache->ids[l] = parent;
+ memcpy(data->dircache->str + l * (l - 1) / 2, dirs, l);
+ }
+ }
+#endif
+ if (!*dire)
+ break;
+ dir = dire + 1;
+ while (*dir == '/')
+ dir++;
+ }
+ return parent;
+}
+
+void
+repodata_free_dircache(Repodata *data)
+{
+ data->dircache = solv_free(data->dircache);
+}
+
+const char *
+repodata_dir2str(Repodata *data, Id did, const char *suf)
+{
+ Pool *pool = data->repo->pool;
+ int l = 0;
+ Id parent, comp;
+ const char *comps;
+ char *p;
+
+ if (!did)
+ return suf ? suf : "";
+ parent = did;
+ while (parent)
+ {
+ comp = dirpool_compid(&data->dirpool, parent);
+ comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
+ l += strlen(comps);
+ parent = dirpool_parent(&data->dirpool, parent);
+ if (parent)
+ l++;
+ }
+ if (suf)
+ l += strlen(suf) + 1;
+ p = pool_alloctmpspace(pool, l + 1) + l;
+ *p = 0;
+ if (suf)
+ {
+ p -= strlen(suf);
+ strcpy(p, suf);
+ *--p = '/';
+ }
+ parent = did;
+ while (parent)
+ {
+ comp = dirpool_compid(&data->dirpool, parent);
+ comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
+ l = strlen(comps);
+ p -= l;
+ strncpy(p, comps, l);
+ parent = dirpool_parent(&data->dirpool, parent);
+ if (parent)
+ *--p = '/';
+ }
+ return p;
+}
+
+
+/***************************************************************
+ * data management
+ */
+
+static inline unsigned char *
+data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
+{
+ Id *keyp = data->schemadata + data->schemata[schema];
+ for (; *keyp; keyp++)
+ dp = data_skip_key(data, dp, data->keys + *keyp);
+ return dp;
+}
+
+static unsigned char *
+data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
+{
+ int nentries, schema;
+ switch(key->type)
+ {
+ case REPOKEY_TYPE_FIXARRAY:
+ dp = data_read_id(dp, &nentries);
+ if (!nentries)
+ return dp;
+ dp = data_read_id(dp, &schema);
+ while (nentries--)
+ dp = data_skip_schema(data, dp, schema);
+ return dp;
+ case REPOKEY_TYPE_FLEXARRAY:
+ dp = data_read_id(dp, &nentries);
+ while (nentries--)
+ {
+ dp = data_read_id(dp, &schema);
+ dp = data_skip_schema(data, dp, schema);
+ }
+ return dp;
+ default:
+ if (key->storage == KEY_STORAGE_INCORE)
+ dp = data_skip(dp, key->type);
+ else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ dp = data_skip(dp, REPOKEY_TYPE_ID);
+ dp = data_skip(dp, REPOKEY_TYPE_ID);
+ }
+ return dp;
+ }
+}
+
+static unsigned char *
+forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
+{
+ Id k;
+
+ if (!keyid)
+ return 0;
+ if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
+ {
+ int i;
+ for (i = 0; (k = *keyp++) != 0; i++)
+ if (k == keyid)
+ return data->incoredata + data->mainschemaoffsets[i];
+ return 0;
+ }
+ while ((k = *keyp++) != 0)
+ {
+ if (k == keyid)
+ return dp;
+ if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip offset */
+ dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip length */
+ continue;
+ }
+ if (data->keys[k].storage != KEY_STORAGE_INCORE)
+ continue;
+ dp = data_skip_key(data, dp, data->keys + k);
+ }
+ return 0;
+}
+
+static unsigned char *
+get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
+{
+ unsigned char *dp;
+ if (len <= 0)
+ return 0;
+ if (off >= data->lastverticaloffset)
+ {
+ off -= data->lastverticaloffset;
+ if ((unsigned int)off + len > data->vincorelen)
+ return 0;
+ return data->vincore + off;
+ }
+ if ((unsigned int)off + len > key->size)
+ return 0;
+ /* we now have the offset, go into vertical */
+ off += data->verticaloffset[key - data->keys];
+ /* fprintf(stderr, "key %d page %d\n", key->name, off / REPOPAGE_BLOBSIZE); */
+ dp = repopagestore_load_page_range(&data->store, off / REPOPAGE_BLOBSIZE, (off + len - 1) / REPOPAGE_BLOBSIZE);
+ data->storestate++;
+ if (dp)
+ dp += off % REPOPAGE_BLOBSIZE;
+ return dp;
+}
+
+static inline unsigned char *
+get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
+{
+ unsigned char *dp = *dpp;
+
+ if (!dp)
+ return 0;
+ if (key->storage == KEY_STORAGE_INCORE)
+ {
+ if (advance)
+ *dpp = data_skip_key(data, dp, key);
+ return dp;
+ }
+ else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ Id off, len;
+ dp = data_read_id(dp, &off);
+ dp = data_read_id(dp, &len);
+ if (advance)
+ *dpp = dp;
+ return get_vertical_data(data, key, off, len);
+ }
+ return 0;
+}
+
+static int
+load_repodata(Repodata *data)
+{
+ if (data->loadcallback)
+ {
+ data->loadcallback(data);
+ if (data->state == REPODATA_AVAILABLE)
+ return 1;
+ }
+ data->state = REPODATA_ERROR;
+ return 0;
+}
+
+static inline int
+maybe_load_repodata(Repodata *data, Id keyname)
+{
+ if (keyname && !repodata_precheck_keyname(data, keyname))
+ return 0; /* do not bother... */
+ switch(data->state)
+ {
+ case REPODATA_STUB:
+ if (keyname)
+ {
+ int i;
+ for (i = 1; i < data->nkeys; i++)
+ if (keyname == data->keys[i].name)
+ break;
+ if (i == data->nkeys)
+ return 0;
+ }
+ return load_repodata(data);
+ case REPODATA_ERROR:
+ return 0;
+ case REPODATA_AVAILABLE:
+ case REPODATA_LOADING:
+ return 1;
+ default:
+ data->state = REPODATA_ERROR;
+ return 0;
+ }
+}
+
+static inline unsigned char *
+solvid2data(Repodata *data, Id solvid, Id *schemap)
+{
+ unsigned char *dp = data->incoredata;
+ if (!dp)
+ return 0;
+ if (solvid == SOLVID_META)
+ dp += 1; /* offset of "meta" solvable */
+ else if (solvid == SOLVID_POS)
+ {
+ Pool *pool = data->repo->pool;
+ if (data->repo != pool->pos.repo)
+ return 0;
+ if (data != data->repo->repodata + pool->pos.repodataid)
+ return 0;
+ dp += pool->pos.dp;
+ if (pool->pos.dp != 1)
+ {
+ *schemap = pool->pos.schema;
+ return dp;
+ }
+ }
+ else
+ {
+ if (solvid < data->start || solvid >= data->end)
+ return 0;
+ dp += data->incoreoffset[solvid - data->start];
+ }
+ return data_read_id(dp, schemap);
+}
+
+/************************************************************************
+ * data lookup
+ */
+
+static unsigned char *
+find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
+{
+ unsigned char *dp;
+ Id schema, *keyp, *kp;
+ Repokey *key;
+
+ if (!maybe_load_repodata(data, keyname))
+ return 0;
+ dp = solvid2data(data, solvid, &schema);
+ if (!dp)
+ return 0;
+ keyp = data->schemadata + data->schemata[schema];
+ for (kp = keyp; *kp; kp++)
+ if (data->keys[*kp].name == keyname)
+ break;
+ if (!*kp)
+ return 0;
+ *keypp = key = data->keys + *kp;
+ if (key->type == REPOKEY_TYPE_DELETED)
+ return 0;
+ if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
+ return dp; /* no need to forward... */
+ if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
+ return 0; /* get_data will not work, no need to forward */
+ dp = forward_to_key(data, *kp, keyp, dp);
+ if (!dp)
+ return 0;
+ return get_data(data, key, &dp, 0);
+}
+
+Id
+repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
+{
+ Id schema, *keyp, *kp;
+ if (!maybe_load_repodata(data, keyname))
+ return 0;
+ if (!solvid2data(data, solvid, &schema))
+ return 0;
+ keyp = data->schemadata + data->schemata[schema];
+ for (kp = keyp; *kp; kp++)
+ if (data->keys[*kp].name == keyname)
+ return data->keys[*kp].type;
+ return 0;
+}
+
+Id
+repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
+{
+ unsigned char *dp;
+ Repokey *key;
+ Id id;
+
+ dp = find_key_data(data, solvid, keyname, &key);
+ if (!dp)
+ return 0;
+ if (key->type == REPOKEY_TYPE_CONSTANTID)
+ return key->size;
+ if (key->type != REPOKEY_TYPE_ID)
+ return 0;
+ dp = data_read_id(dp, &id);
+ return id;
+}
+
+const char *
+repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
+{
+ unsigned char *dp;
+ Repokey *key;
+ Id id;
+
+ dp = find_key_data(data, solvid, keyname, &key);
+ if (!dp)
+ return 0;
+ if (key->type == REPOKEY_TYPE_STR)
+ return (const char *)dp;
+ if (key->type == REPOKEY_TYPE_CONSTANTID)
+ id = key->size;
+ else if (key->type == REPOKEY_TYPE_ID)
+ dp = data_read_id(dp, &id);
+ else
+ return 0;
+ if (data->localpool)
+ return stringpool_id2str(&data->spool, id);
+ return pool_id2str(data->repo->pool, id);
+}
+
+int
+repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value)
+{
+ unsigned char *dp;
+ Repokey *key;
+ unsigned int high, low;
+
+ *value = 0;
+ dp = find_key_data(data, solvid, keyname, &key);
+ if (!dp)
+ return 0;
+ switch (key->type)
+ {
+ case REPOKEY_TYPE_NUM:
+ data_read_num64(dp, &low, &high);
+ *value = (unsigned long long)high << 32 | low;
+ return 1;
+ case REPOKEY_TYPE_U32:
+ data_read_u32(dp, &low);
+ *value = low;
+ return 1;
+ case REPOKEY_TYPE_CONSTANT:
+ *value = key->size;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int
+repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
+{
+ Id schema;
+ Id *keyp;
+ unsigned char *dp;
+
+ if (!maybe_load_repodata(data, keyname))
+ return 0;
+ dp = solvid2data(data, solvid, &schema);
+ if (!dp)
+ return 0;
+ /* can't use find_key_data as we need to test the type */
+ for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
+ if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
+ return 1;
+ return 0;
+}
+
+const unsigned char *
+repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
+{
+ unsigned char *dp;
+ Repokey *key;
+
+ dp = find_key_data(data, solvid, keyname, &key);
+ if (!dp)
+ return 0;
+ switch (key->type)
+ {
+ case_CHKSUM_TYPES:
+ break;
+ default:
+ return 0;
+ }
+ *typep = key->type;
+ return dp;
+}
+
+int
+repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
+{
+ unsigned char *dp;
+ Repokey *key;
+ Id id;
+ int eof = 0;
+
+ queue_empty(q);
+ dp = find_key_data(data, solvid, keyname, &key);
+ if (!dp)
+ return 0;
+ if (key->type != REPOKEY_TYPE_IDARRAY)
+ return 0;
+ for (;;)
+ {
+ dp = data_read_ideof(dp, &id, &eof);
+ queue_push(q, id);
+ if (eof)
+ break;
+ }
+ return 1;
+}
+
+const void *
+repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp)
+{
+ unsigned char *dp;
+ Repokey *key;
+ Id len;
+
+ dp = find_key_data(data, solvid, keyname, &key);
+ if (!dp || key->type != REPOKEY_TYPE_BINARY)
+ {
+ *lenp = 0;
+ return 0;
+ }
+ dp = data_read_id(dp, &len);
+ *lenp = len;
+ return dp;
+}
+
+Id
+repodata_globalize_id(Repodata *data, Id id, int create)
+{
+ if (!id || !data || !data->localpool)
+ return id;
+ return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
+}
+
+Id
+repodata_localize_id(Repodata *data, Id id, int create)
+{
+ if (!id || !data || !data->localpool)
+ return id;
+ return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
+}
+
+Id
+repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
+{
+ if (!id || !data || !fromdata)
+ return id;
+ if (!data->localpool || !fromdata->localpool)
+ {
+ if (fromdata->localpool)
+ id = repodata_globalize_id(fromdata, id, create);
+ if (data->localpool)
+ id = repodata_localize_id(data, id, create);
+ return id;
+ }
+ /* localpool is set in both data and fromdata */
+ return stringpool_str2id(&data->spool, stringpool_id2str(&fromdata->spool, id), create);
+}
+
+Id
+repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
+{
+ Id *ap;
+ if (!data->attrs)
+ return 0;
+ ap = data->attrs[solvid - data->start];
+ if (!ap)
+ return 0;
+ for (; *ap; ap += 2)
+ {
+ if (data->keys[*ap].name != keyname)
+ continue;
+ if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
+ return voidid;
+ if (data->keys[*ap].type == REPOKEY_TYPE_ID)
+ return ap[1];
+ return 0;
+ }
+ return 0;
+}
+
+const char *
+repodata_lookup_dirstrarray_uninternalized(Repodata *data, Id solvid, Id keyname, Id *didp, Id *iterp)
+{
+ Id *ap, did;
+ Id iter = *iterp;
+ if (iter == 0) /* find key data */
+ {
+ if (!data->attrs)
+ return 0;
+ ap = data->attrs[solvid - data->start];
+ if (!ap)
+ return 0;
+ for (; *ap; ap += 2)
+ if (data->keys[*ap].name == keyname && data->keys[*ap].type == REPOKEY_TYPE_DIRSTRARRAY)
+ break;
+ if (!*ap)
+ return 0;
+ iter = ap[1];
+ }
+ did = *didp;
+ for (ap = data->attriddata + iter; *ap; ap += 2)
+ {
+ if (did && ap[0] != did)
+ continue;
+ *didp = ap[0];
+ *iterp = ap - data->attriddata + 2;
+ return (const char *)data->attrdata + ap[1];
+ }
+ *iterp = 0;
+ return 0;
+}
+
+/************************************************************************
+ * data search
+ */
+
+
+const char *
+repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
+{
+ switch (key->type)
+ {
+ case REPOKEY_TYPE_ID:
+ case REPOKEY_TYPE_CONSTANTID:
+ case REPOKEY_TYPE_IDARRAY:
+ if (data && data->localpool)
+ kv->str = stringpool_id2str(&data->spool, kv->id);
+ else
+ kv->str = pool_id2str(pool, kv->id);
+ if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
+ {
+ const char *s;
+ for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
+ ;
+ if (*s == ':' && s > kv->str)
+ kv->str = s + 1;
+ }
+ return kv->str;
+ case REPOKEY_TYPE_STR:
+ return kv->str;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ if (!(flags & SEARCH_FILES))
+ return kv->str; /* match just the basename */
+ if (kv->num)
+ return kv->str; /* already stringified */
+ /* Put the full filename into kv->str. */
+ kv->str = repodata_dir2str(data, kv->id, kv->str);
+ kv->num = 1; /* mark stringification */
+ return kv->str;
+ case_CHKSUM_TYPES:
+ if (!(flags & SEARCH_CHECKSUMS))
+ return 0; /* skip em */
+ if (kv->num)
+ return kv->str; /* already stringified */
+ kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
+ kv->num = 1; /* mark stringification */
+ return kv->str;
+ default:
+ return 0;
+ }
+}
+
+
+struct subschema_data {
+ Solvable *s;
+ void *cbdata;
+ KeyValue *parent;
+};
+
+/* search a specific repodata */
+void
+repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
+{
+ Id schema;
+ Repokey *key;
+ Id keyid, *kp, *keyp;
+ unsigned char *dp, *ddp;
+ int onekey = 0;
+ int stop;
+ KeyValue kv;
+ Solvable *s;
+
+ if (!maybe_load_repodata(data, keyname))
+ return;
+ if (solvid == SOLVID_SUBSCHEMA)
+ {
+ struct subschema_data *subd = cbdata;
+ cbdata = subd->cbdata;
+ s = subd->s;
+ schema = subd->parent->id;
+ dp = (unsigned char *)subd->parent->str;
+ kv.parent = subd->parent;
+ }
+ else
+ {
+ schema = 0;
+ dp = solvid2data(data, solvid, &schema);
+ if (!dp)
+ return;
+ s = data->repo->pool->solvables + solvid;
+ kv.parent = 0;
+ }
+ keyp = data->schemadata + data->schemata[schema];
+ if (keyname)
+ {
+ /* search for a specific key */
+ for (kp = keyp; *kp; kp++)
+ if (data->keys[*kp].name == keyname)
+ break;
+ if (!*kp)
+ return;
+ dp = forward_to_key(data, *kp, keyp, dp);
+ if (!dp)
+ return;
+ keyp = kp;
+ onekey = 1;
+ }
+ while ((keyid = *keyp++) != 0)
+ {
+ stop = 0;
+ key = data->keys + keyid;
+ ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
+
+ if (key->type == REPOKEY_TYPE_DELETED)
+ continue;
+ if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
+ {
+ struct subschema_data subd;
+ int nentries;
+ Id schema = 0;
+
+ subd.cbdata = cbdata;
+ subd.s = s;
+ subd.parent = &kv;
+ ddp = data_read_id(ddp, &nentries);
+ kv.num = nentries;
+ kv.entry = 0;
+ kv.eof = 0;
+ while (ddp && nentries > 0)
+ {
+ if (!--nentries)
+ kv.eof = 1;
+ if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
+ ddp = data_read_id(ddp, &schema);
+ kv.id = schema;
+ kv.str = (char *)ddp;
+ stop = callback(cbdata, s, data, key, &kv);
+ if (stop > SEARCH_NEXT_KEY)
+ return;
+ if (stop && stop != SEARCH_ENTERSUB)
+ break;
+ if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
+ repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
+ ddp = data_skip_schema(data, ddp, schema);
+ kv.entry++;
+ }
+ if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
+ {
+ /* sentinel */
+ kv.eof = 2;
+ kv.str = (char *)ddp;
+ stop = callback(cbdata, s, data, key, &kv);
+ if (stop > SEARCH_NEXT_KEY)
+ return;
+ }
+ if (onekey)
+ return;
+ continue;
+ }
+ kv.entry = 0;
+ do
+ {
+ ddp = data_fetch(ddp, &kv, key);
+ if (!ddp)
+ break;
+ stop = callback(cbdata, s, data, key, &kv);
+ kv.entry++;
+ }
+ while (!kv.eof && !stop);
+ if (onekey || stop > SEARCH_NEXT_KEY)
+ return;
+ }
+}
+
+void
+repodata_setpos_kv(Repodata *data, KeyValue *kv)
+{
+ Pool *pool = data->repo->pool;
+ if (!kv)
+ pool_clear_pos(pool);
+ else
+ {
+ pool->pos.repo = data->repo;
+ pool->pos.repodataid = data - data->repo->repodata;
+ pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
+ pool->pos.schema = kv->id;
+ }
+}
+
+/************************************************************************
+ * data iterator functions
+ */
+
+static inline Id *
+solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
+{
+ kv->id = keyname;
+ switch (keyname)
+ {
+ case SOLVABLE_NAME:
+ kv->eof = 1;
+ return &s->name;
+ case SOLVABLE_ARCH:
+ kv->eof = 1;
+ return &s->arch;
+ case SOLVABLE_EVR:
+ kv->eof = 1;
+ return &s->evr;
+ case SOLVABLE_VENDOR:
+ kv->eof = 1;
+ return &s->vendor;
+ case SOLVABLE_PROVIDES:
+ kv->eof = 0;
+ return s->provides ? s->repo->idarraydata + s->provides : 0;
+ case SOLVABLE_OBSOLETES:
+ kv->eof = 0;
+ return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
+ case SOLVABLE_CONFLICTS:
+ kv->eof = 0;
+ return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
+ case SOLVABLE_REQUIRES:
+ kv->eof = 0;
+ return s->requires ? s->repo->idarraydata + s->requires : 0;
+ case SOLVABLE_RECOMMENDS:
+ kv->eof = 0;
+ return s->recommends ? s->repo->idarraydata + s->recommends : 0;
+ case SOLVABLE_SUPPLEMENTS:
+ kv->eof = 0;
+ return s->supplements ? s->repo->idarraydata + s->supplements : 0;
+ case SOLVABLE_SUGGESTS:
+ kv->eof = 0;
+ return s->suggests ? s->repo->idarraydata + s->suggests : 0;
+ case SOLVABLE_ENHANCES:
+ kv->eof = 0;
+ return s->enhances ? s->repo->idarraydata + s->enhances : 0;
+ case RPM_RPMDBID:
+ kv->eof = 1;
+ return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
+ default:
+ return 0;
+ }
+}
+
+int
+datamatcher_init(Datamatcher *ma, const char *match, int flags)
+{
+ match = match ? solv_strdup(match) : 0;
+ ma->match = match;
+ ma->flags = flags;
+ ma->error = 0;
+ ma->matchdata = 0;
+ if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
+ {
+ ma->matchdata = solv_calloc(1, sizeof(regex_t));
+ ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
+ if (ma->error)
+ {
+ solv_free(ma->matchdata);
+ ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
+ }
+ }
+ if ((flags & SEARCH_FILES) != 0 && match)
+ {
+ /* prepare basename check */
+ if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
+ {
+ const char *p = strrchr(match, '/');
+ ma->matchdata = (void *)(p ? p + 1 : match);
+ }
+ else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
+ {
+ const char *p;
+ for (p = match + strlen(match) - 1; p >= match; p--)
+ if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
+ break;
+ ma->matchdata = (void *)(p + 1);
+ }
+ }
+ return ma->error;
+}
+
+void
+datamatcher_free(Datamatcher *ma)
+{
+ if (ma->match)
+ ma->match = solv_free((char *)ma->match);
+ if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
+ {
+ regfree(ma->matchdata);
+ solv_free(ma->matchdata);
+ }
+ ma->matchdata = 0;
+}
+
+int
+datamatcher_match(Datamatcher *ma, const char *str)
+{
+ int l;
+ switch ((ma->flags & SEARCH_STRINGMASK))
+ {
+ case SEARCH_SUBSTRING:
+ if (ma->flags & SEARCH_NOCASE)
+ return strcasestr(str, ma->match) != 0;
+ else
+ return strstr(str, ma->match) != 0;
+ case SEARCH_STRING:
+ if (ma->flags & SEARCH_NOCASE)
+ return !strcasecmp(ma->match, str);
+ else
+ return !strcmp(ma->match, str);
+ case SEARCH_STRINGSTART:
+ if (ma->flags & SEARCH_NOCASE)
+ return !strncasecmp(ma->match, str, strlen(ma->match));
+ else
+ return !strncmp(ma->match, str, strlen(ma->match));
+ case SEARCH_STRINGEND:
+ l = strlen(str) - strlen(ma->match);
+ if (l < 0)
+ return 0;
+ if (ma->flags & SEARCH_NOCASE)
+ return !strcasecmp(ma->match, str + l);
+ else
+ return !strcmp(ma->match, str + l);
+ case SEARCH_GLOB:
+ return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
+ case SEARCH_REGEX:
+ return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
+ default:
+ return 0;
+ }
+}
+
+/* check if the matcher can match the provides basename */
+
+int
+datamatcher_checkbasename(Datamatcher *ma, const char *basename)
+{
+ int l;
+ const char *match = ma->matchdata;
+ if (!match)
+ return 1;
+ switch (ma->flags & SEARCH_STRINGMASK)
+ {
+ case SEARCH_STRING:
+ break;
+ case SEARCH_STRINGEND:
+ if (match != ma->match)
+ break; /* had slash, do exact match on basename */
+ /* FALLTHROUGH */
+ case SEARCH_GLOB:
+ /* check if the basename ends with match */
+ l = strlen(basename) - strlen(match);
+ if (l < 0)
+ return 0;
+ basename += l;
+ break;
+ default:
+ return 1; /* maybe matches */
+ }
+ if ((ma->flags & SEARCH_NOCASE) != 0)
+ return !strcasecmp(match, basename);
+ else
+ return !strcmp(match, basename);
+}
+
+int
+repodata_filelistfilter_matches(Repodata *data, const char *str)
+{
+ /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
+ /* for now hardcoded */
+ if (strstr(str, "bin/"))
+ return 1;
+ if (!strncmp(str, "/etc/", 5))
+ return 1;
+ if (!strcmp(str, "/usr/lib/sendmail"))
+ return 1;
+ return 0;
+}
+
+
+enum {
+ di_bye,
+
+ di_enterrepo,
+ di_entersolvable,
+ di_enterrepodata,
+ di_enterschema,
+ di_enterkey,
+
+ di_nextattr,
+ di_nextkey,
+ di_nextrepodata,
+ di_nextsolvable,
+ di_nextrepo,
+
+ di_enterarray,
+ di_nextarrayelement,
+
+ di_entersub,
+ di_leavesub,
+
+ di_nextsolvablekey,
+ di_entersolvablekey,
+ di_nextsolvableattr
+};
+
+/* see dataiterator.h for documentation */
+int
+dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
+{
+ memset(di, 0, sizeof(*di));
+ di->pool = pool;
+ di->flags = flags & ~SEARCH_THISSOLVID;
+ if (!pool || (repo && repo->pool != pool))
+ {
+ di->state = di_bye;
+ return -1;
+ }
+ if (match)
+ {
+ int error;
+ if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
+ {
+ di->state = di_bye;
+ return error;
+ }
+ }
+ di->keyname = keyname;
+ di->keynames[0] = keyname;
+ dataiterator_set_search(di, repo, p);
+ return 0;
+}
+
+void
+dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
+{
+ *di = *from;
+ if (di->dupstr)
+ {
+ if (di->dupstr == di->kv.str)
+ di->dupstr = solv_memdup(di->dupstr, di->dupstrn);
+ else
+ {
+ di->dupstr = 0;
+ di->dupstrn = 0;
+ }
+ }
+ memset(&di->matcher, 0, sizeof(di->matcher));
+ if (from->matcher.match)
+ datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
+ if (di->nparents)
+ {
+ /* fix pointers */
+ int i;
+ for (i = 1; i < di->nparents; i++)
+ di->parents[i].kv.parent = &di->parents[i - 1].kv;
+ di->kv.parent = &di->parents[di->nparents - 1].kv;
+ }
+}
+
+int
+dataiterator_set_match(Dataiterator *di, const char *match, int flags)
+{
+ di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
+ datamatcher_free(&di->matcher);
+ memset(&di->matcher, 0, sizeof(di->matcher));
+ if (match)
+ {
+ int error;
+ if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
+ {
+ di->state = di_bye;
+ return error;
+ }
+ }
+ return 0;
+}
+
+void
+dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
+{
+ di->repo = repo;
+ di->repoid = 0;
+ di->flags &= ~SEARCH_THISSOLVID;
+ di->nparents = 0;
+ di->rootlevel = 0;
+ di->repodataid = 1;
+ if (!di->pool->urepos)
+ {
+ di->state = di_bye;
+ return;
+ }
+ if (!repo)
+ {
+ di->repoid = 1;
+ di->repo = di->pool->repos[di->repoid];
+ }
+ di->state = di_enterrepo;
+ if (p)
+ dataiterator_jump_to_solvid(di, p);
+}
+
+void
+dataiterator_set_keyname(Dataiterator *di, Id keyname)
+{
+ di->nkeynames = 0;
+ di->keyname = keyname;
+ di->keynames[0] = keyname;
+}
+
+void
+dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
+{
+ int i;
+
+ if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
+ {
+ di->state = di_bye; /* sorry */
+ return;
+ }
+ for (i = di->nkeynames + 1; i > 0; i--)
+ di->keynames[i] = di->keynames[i - 1];
+ di->keynames[0] = di->keyname = keyname;
+ di->nkeynames++;
+}
+
+void
+dataiterator_free(Dataiterator *di)
+{
+ if (di->matcher.match)
+ datamatcher_free(&di->matcher);
+ if (di->dupstr)
+ solv_free(di->dupstr);
+}
+
+static unsigned char *
+dataiterator_find_keyname(Dataiterator *di, Id keyname)
+{
+ Id *keyp;
+ Repokey *keys = di->data->keys, *key;
+ unsigned char *dp;
+
+ for (keyp = di->keyp; *keyp; keyp++)
+ if (keys[*keyp].name == keyname)
+ break;
+ if (!*keyp)
+ return 0;
+ key = keys + *keyp;
+ if (key->type == REPOKEY_TYPE_DELETED)
+ return 0;
+ if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
+ return 0; /* get_data will not work, no need to forward */
+ dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
+ if (!dp)
+ return 0;
+ di->keyp = keyp;
+ return dp;
+}
+
+static inline int
+is_filelist_extension(Repodata *data)
+{
+ int j;
+ if (!repodata_precheck_keyname(data, SOLVABLE_FILELIST))
+ return 0;
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name == SOLVABLE_FILELIST)
+ break;
+ if (j == data->nkeys)
+ return 0;
+ if (data->state != REPODATA_AVAILABLE)
+ return 1;
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ return 0;
+ return 1;
+}
+
+static int
+dataiterator_filelistcheck(Dataiterator *di)
+{
+ int j;
+ int needcomplete = 0;
+ Repodata *data = di->data;
+
+ if ((di->flags & SEARCH_COMPLETE_FILELIST) != 0)
+ if (!di->matcher.match
+ || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
+ && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
+ || !repodata_filelistfilter_matches(data, di->matcher.match))
+ needcomplete = 1;
+ if (data->state != REPODATA_AVAILABLE)
+ return needcomplete ? 1 : 0;
+ if (!needcomplete)
+ {
+ /* we don't need the complete filelist, so ignore all stubs */
+ if (data->repo->nrepodata == 2)
+ return 1;
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ return 1;
+ return 0;
+ }
+ else
+ {
+ /* we need the complete filelist. check if we habe a filtered filelist and there's
+ * a extension with the complete filelist later on */
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name == SOLVABLE_FILELIST)
+ break;
+ if (j == data->nkeys)
+ return 0; /* does not have filelist */
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ break;
+ if (j == data->nkeys)
+ return 1; /* this is the externsion */
+ while (data - data->repo->repodata + 1 < data->repo->nrepodata)
+ {
+ data++;
+ if (is_filelist_extension(data))
+ return 0;
+ }
+ return 1;
+ }
+}
+
+int
+dataiterator_step(Dataiterator *di)
+{
+ Id schema;
+
+ if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
+ unsigned int ddpoff = di->ddp - di->vert_ddp;
+ di->vert_off += ddpoff;
+ di->vert_len -= ddpoff;
+ di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
+ di->vert_storestate = di->data->storestate;
+ if (!di->ddp)
+ di->state = di_nextkey;
+ }
+ for (;;)
+ {
+ switch (di->state)
+ {
+ case di_enterrepo: di_enterrepo:
+ if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
+ goto di_nextrepo;
+ if (!(di->flags & SEARCH_THISSOLVID))
+ {
+ di->solvid = di->repo->start - 1; /* reset solvid iterator */
+ goto di_nextsolvable;
+ }
+ /* FALLTHROUGH */
+
+ case di_entersolvable: di_entersolvable:
+ if (di->repodataid)
+ {
+ di->repodataid = 1; /* reset repodata iterator */
+ if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents - di->rootlevel == di->nkeynames)
+ {
+ extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
+
+ di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
+ di->data = 0;
+ goto di_entersolvablekey;
+ }
+ }
+ /* FALLTHROUGH */
+
+ case di_enterrepodata: di_enterrepodata:
+ if (di->repodataid)
+ {
+ if (di->repodataid >= di->repo->nrepodata)
+ goto di_nextsolvable;
+ di->data = di->repo->repodata + di->repodataid;
+ }
+ if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
+ goto di_nextrepodata;
+ if (!maybe_load_repodata(di->data, di->keyname))
+ goto di_nextrepodata;
+ di->dp = solvid2data(di->data, di->solvid, &schema);
+ if (!di->dp)
+ goto di_nextrepodata;
+ if (di->solvid == SOLVID_POS)
+ di->solvid = di->pool->pos.solvid;
+ /* reset key iterator */
+ di->keyp = di->data->schemadata + di->data->schemata[schema];
+ /* FALLTHROUGH */
+
+ case di_enterschema: di_enterschema:
+ if (di->keyname)
+ di->dp = dataiterator_find_keyname(di, di->keyname);
+ if (!di->dp || !*di->keyp)
+ {
+ if (di->kv.parent)
+ goto di_leavesub;
+ goto di_nextrepodata;
+ }
+ /* FALLTHROUGH */
+
+ case di_enterkey: di_enterkey:
+ di->kv.entry = -1;
+ di->key = di->data->keys + *di->keyp;
+ if (!di->dp)
+ goto di_nextkey;
+ /* this is get_data() modified to store vert_ data */
+ if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ Id off, len;
+ di->dp = data_read_id(di->dp, &off);
+ di->dp = data_read_id(di->dp, &len);
+ di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
+ di->vert_off = off;
+ di->vert_len = len;
+ di->vert_storestate = di->data->storestate;
+ }
+ else if (di->key->storage == KEY_STORAGE_INCORE)
+ {
+ di->ddp = di->dp;
+ if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
+ di->dp = data_skip_key(di->data, di->dp, di->key);
+ }
+ else
+ di->ddp = 0;
+ if (!di->ddp)
+ goto di_nextkey;
+ if (di->key->type == REPOKEY_TYPE_DELETED)
+ goto di_nextkey;
+ if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
+ goto di_enterarray;
+ if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+ goto di_nextkey;
+ /* FALLTHROUGH */
+
+ case di_nextattr:
+ di->kv.entry++;
+ di->ddp = data_fetch(di->ddp, &di->kv, di->key);
+ if (di->kv.eof)
+ di->state = di_nextkey;
+ else
+ di->state = di_nextattr;
+ break;
+
+ case di_nextkey: di_nextkey:
+ if (!di->keyname && *++di->keyp)
+ goto di_enterkey;
+ if (di->kv.parent)
+ goto di_leavesub;
+ /* FALLTHROUGH */
+
+ case di_nextrepodata: di_nextrepodata:
+ if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
+ goto di_enterrepodata;
+ /* FALLTHROUGH */
+
+ case di_nextsolvable: di_nextsolvable:
+ if (!(di->flags & SEARCH_THISSOLVID))
+ {
+ if (di->solvid < 0)
+ di->solvid = di->repo->start;
+ else
+ di->solvid++;
+ for (; di->solvid < di->repo->end; di->solvid++)
+ {
+ if (di->pool->solvables[di->solvid].repo == di->repo)
+ goto di_entersolvable;
+ }
+ }
+ /* FALLTHROUGH */
+
+ case di_nextrepo: di_nextrepo:
+ if (di->repoid > 0)
+ {
+ di->repoid++;
+ di->repodataid = 1;
+ if (di->repoid < di->pool->nrepos)
+ {
+ di->repo = di->pool->repos[di->repoid];
+ goto di_enterrepo;
+ }
+ }
+ /* FALLTHROUGH */
+
+ case di_bye: di_bye:
+ di->state = di_bye;
+ return 0;
+
+ case di_enterarray: di_enterarray:
+ if (di->key->name == REPOSITORY_SOLVABLES)
+ goto di_nextkey;
+ di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
+ di->kv.eof = 0;
+ di->kv.entry = -1;
+ /* FALLTHROUGH */
+
+ case di_nextarrayelement: di_nextarrayelement:
+ di->kv.entry++;
+ if (di->kv.entry)
+ di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
+ if (di->kv.entry == di->kv.num)
+ {
+ if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+ goto di_nextkey;
+ if (!(di->flags & SEARCH_ARRAYSENTINEL))
+ goto di_nextkey;
+ di->kv.str = (char *)di->ddp;
+ di->kv.eof = 2;
+ di->state = di_nextkey;
+ break;
+ }
+ if (di->kv.entry == di->kv.num - 1)
+ di->kv.eof = 1;
+ if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
+ di->ddp = data_read_id(di->ddp, &di->kv.id);
+ di->kv.str = (char *)di->ddp;
+ if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+ goto di_entersub;
+ if ((di->flags & SEARCH_SUB) != 0)
+ di->state = di_entersub;
+ else
+ di->state = di_nextarrayelement;
+ break;
+
+ case di_entersub: di_entersub:
+ if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
+ goto di_nextarrayelement; /* sorry, full */
+ di->parents[di->nparents].kv = di->kv;
+ di->parents[di->nparents].dp = di->dp;
+ di->parents[di->nparents].keyp = di->keyp;
+ di->dp = (unsigned char *)di->kv.str;
+ di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
+ memset(&di->kv, 0, sizeof(di->kv));
+ di->kv.parent = &di->parents[di->nparents].kv;
+ di->nparents++;
+ di->keyname = di->keynames[di->nparents - di->rootlevel];
+ goto di_enterschema;
+
+ case di_leavesub: di_leavesub:
+ if (di->nparents - 1 < di->rootlevel)
+ goto di_bye;
+ di->nparents--;
+ di->dp = di->parents[di->nparents].dp;
+ di->kv = di->parents[di->nparents].kv;
+ di->keyp = di->parents[di->nparents].keyp;
+ di->key = di->data->keys + *di->keyp;
+ di->ddp = (unsigned char *)di->kv.str;
+ di->keyname = di->keynames[di->nparents - di->rootlevel];
+ goto di_nextarrayelement;
+
+ /* special solvable attr handling follows */
+
+ case di_nextsolvablekey: di_nextsolvablekey:
+ if (di->keyname || di->key->name == RPM_RPMDBID)
+ goto di_enterrepodata;
+ di->key++;
+ /* FALLTHROUGH */
+
+ case di_entersolvablekey: di_entersolvablekey:
+ di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
+ if (!di->idp || !*di->idp)
+ goto di_nextsolvablekey;
+ if (di->kv.eof)
+ {
+ /* not an array */
+ di->kv.id = *di->idp;
+ di->kv.num = *di->idp; /* for rpmdbid */
+ di->kv.num2 = 0; /* for rpmdbid */
+ di->kv.entry = 0;
+ di->state = di_nextsolvablekey;
+ break;
+ }
+ di->kv.entry = -1;
+ /* FALLTHROUGH */
+
+ case di_nextsolvableattr:
+ di->state = di_nextsolvableattr;
+ di->kv.id = *di->idp++;
+ di->kv.entry++;
+ if (!*di->idp)
+ {
+ di->kv.eof = 1;
+ di->state = di_nextsolvablekey;
+ }
+ break;
+
+ }
+
+ if (di->matcher.match)
+ {
+ const char *str;
+ /* simple pre-check so that we don't need to stringify */
+ if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
+ if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
+ continue;
+ if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
+ {
+ if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
+ return 1;
+ continue;
+ }
+ if (!datamatcher_match(&di->matcher, str))
+ continue;
+ }
+ else
+ {
+ if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
+ repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
+ }
+ /* found something! */
+ return 1;
+ }
+}
+
+void
+dataiterator_entersub(Dataiterator *di)
+{
+ if (di->state == di_nextarrayelement)
+ di->state = di_entersub;
+}
+
+void
+dataiterator_setpos(Dataiterator *di)
+{
+ if (di->kv.eof == 2)
+ {
+ pool_clear_pos(di->pool);
+ return;
+ }
+ di->pool->pos.solvid = di->solvid;
+ di->pool->pos.repo = di->repo;
+ di->pool->pos.repodataid = di->data - di->repo->repodata;
+ di->pool->pos.schema = di->kv.id;
+ di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
+}
+
+void
+dataiterator_setpos_parent(Dataiterator *di)
+{
+ if (!di->kv.parent || di->kv.parent->eof == 2)
+ {
+ pool_clear_pos(di->pool);
+ return;
+ }
+ di->pool->pos.solvid = di->solvid;
+ di->pool->pos.repo = di->repo;
+ di->pool->pos.repodataid = di->data - di->repo->repodata;
+ di->pool->pos.schema = di->kv.parent->id;
+ di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
+}
+
+/* clones just the position, not the search keys/matcher */
+void
+dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
+{
+ di->state = from->state;
+ di->flags &= ~SEARCH_THISSOLVID;
+ di->flags |= (from->flags & SEARCH_THISSOLVID);
+ di->repo = from->repo;
+ di->data = from->data;
+ di->dp = from->dp;
+ di->ddp = from->ddp;
+ di->idp = from->idp;
+ di->keyp = from->keyp;
+ di->key = from->key;
+ di->kv = from->kv;
+ di->repodataid = from->repodataid;
+ di->solvid = from->solvid;
+ di->repoid = from->repoid;
+ di->rootlevel = from->rootlevel;
+ memcpy(di->parents, from->parents, sizeof(from->parents));
+ di->nparents = from->nparents;
+ if (di->nparents)
+ {
+ int i;
+ for (i = 1; i < di->nparents; i++)
+ di->parents[i].kv.parent = &di->parents[i - 1].kv;
+ di->kv.parent = &di->parents[di->nparents - 1].kv;
+ }
+ di->dupstr = 0;
+ di->dupstrn = 0;
+ if (from->dupstr && from->dupstr == from->kv.str)
+ {
+ di->dupstrn = from->dupstrn;
+ di->dupstr = solv_memdup(from->dupstr, from->dupstrn);
+ }
+}
+
+void
+dataiterator_seek(Dataiterator *di, int whence)
+{
+ if ((whence & DI_SEEK_STAY) != 0)
+ di->rootlevel = di->nparents;
+ switch (whence & ~DI_SEEK_STAY)
+ {
+ case DI_SEEK_CHILD:
+ if (di->state != di_nextarrayelement)
+ break;
+ if ((whence & DI_SEEK_STAY) != 0)
+ di->rootlevel = di->nparents + 1; /* XXX: dangerous! */
+ di->state = di_entersub;
+ break;
+ case DI_SEEK_PARENT:
+ if (!di->nparents)
+ {
+ di->state = di_bye;
+ break;
+ }
+ di->nparents--;
+ if (di->rootlevel > di->nparents)
+ di->rootlevel = di->nparents;
+ di->dp = di->parents[di->nparents].dp;
+ di->kv = di->parents[di->nparents].kv;
+ di->keyp = di->parents[di->nparents].keyp;
+ di->key = di->data->keys + *di->keyp;
+ di->ddp = (unsigned char *)di->kv.str;
+ di->keyname = di->keynames[di->nparents - di->rootlevel];
+ di->state = di_nextarrayelement;
+ break;
+ case DI_SEEK_REWIND:
+ if (!di->nparents)
+ {
+ di->state = di_bye;
+ break;
+ }
+ di->dp = (unsigned char *)di->kv.parent->str;
+ di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
+ di->state = di_enterschema;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+dataiterator_skip_attribute(Dataiterator *di)
+{
+ if (di->state == di_nextsolvableattr)
+ di->state = di_nextsolvablekey;
+ else
+ di->state = di_nextkey;
+}
+
+void
+dataiterator_skip_solvable(Dataiterator *di)
+{
+ di->nparents = 0;
+ di->kv.parent = 0;
+ di->rootlevel = 0;
+ di->keyname = di->keynames[0];
+ di->state = di_nextsolvable;
+}
+
+void
+dataiterator_skip_repo(Dataiterator *di)
+{
+ di->nparents = 0;
+ di->kv.parent = 0;
+ di->rootlevel = 0;
+ di->keyname = di->keynames[0];
+ di->state = di_nextrepo;
+}
+
+void
+dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
+{
+ di->nparents = 0;
+ di->kv.parent = 0;
+ di->rootlevel = 0;
+ di->keyname = di->keynames[0];
+ if (solvid == SOLVID_POS)
+ {
+ di->repo = di->pool->pos.repo;
+ if (!di->repo)
+ {
+ di->state = di_bye;
+ return;
+ }
+ di->repoid = 0;
+ if (!di->pool->pos.repodataid && di->pool->pos.solvid == SOLVID_META) {
+ solvid = SOLVID_META; /* META pos hack */
+ } else {
+ di->data = di->repo->repodata + di->pool->pos.repodataid;
+ di->repodataid = 0;
+ }
+ }
+ else if (solvid > 0)
+ {
+ di->repo = di->pool->solvables[solvid].repo;
+ di->repoid = 0;
+ }
+ if (di->repoid > 0)
+ {
+ if (!di->pool->urepos)
+ {
+ di->state = di_bye;
+ return;
+ }
+ di->repoid = 1;
+ di->repo = di->pool->repos[di->repoid];
+ }
+ if (solvid != SOLVID_POS)
+ di->repodataid = 1;
+ di->solvid = solvid;
+ if (solvid)
+ di->flags |= SEARCH_THISSOLVID;
+ di->state = di_enterrepo;
+}
+
+void
+dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
+{
+ di->nparents = 0;
+ di->kv.parent = 0;
+ di->rootlevel = 0;
+ di->repo = repo;
+ di->repoid = 0; /* 0 means stay at repo */
+ di->repodataid = 1;
+ di->solvid = 0;
+ di->flags &= ~SEARCH_THISSOLVID;
+ di->state = di_enterrepo;
+}
+
+int
+dataiterator_match(Dataiterator *di, Datamatcher *ma)
+{
+ const char *str;
+ if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
+ return 0;
+ return ma ? datamatcher_match(ma, str) : 1;
+}
+
+void
+dataiterator_strdup(Dataiterator *di)
+{
+ int l = -1;
+
+ if (!di->kv.str || di->kv.str == di->dupstr)
+ return;
+ switch (di->key->type)
+ {
+ case_CHKSUM_TYPES:
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ if (di->kv.num) /* was it stringified into tmp space? */
+ l = strlen(di->kv.str) + 1;
+ break;
+ default:
+ break;
+ }
+ if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ switch (di->key->type)
+ {
+ case REPOKEY_TYPE_STR:
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ l = strlen(di->kv.str) + 1;
+ break;
+ case_CHKSUM_TYPES:
+ l = solv_chksum_len(di->key->type);
+ break;
+ case REPOKEY_TYPE_BINARY:
+ l = di->kv.num;
+ break;
+ }
+ }
+ if (l >= 0)
+ {
+ if (!di->dupstrn || di->dupstrn < l)
+ {
+ di->dupstrn = l + 16;
+ di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
+ }
+ if (l)
+ memcpy(di->dupstr, di->kv.str, l);
+ di->kv.str = di->dupstr;
+ }
+}
+
+/************************************************************************
+ * data modify functions
+ */
+
+/* extend repodata so that it includes solvables p */
+void
+repodata_extend(Repodata *data, Id p)
+{
+ if (data->start == data->end)
+ data->start = data->end = p;
+ if (p >= data->end)
+ {
+ int old = data->end - data->start;
+ int new = p - data->end + 1;
+ if (data->attrs)
+ {
+ data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
+ memset(data->attrs + old, 0, new * sizeof(Id *));
+ }
+ data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
+ memset(data->incoreoffset + old, 0, new * sizeof(Id));
+ data->end = p + 1;
+ }
+ if (p < data->start)
+ {
+ int old = data->end - data->start;
+ int new = data->start - p;
+ if (data->attrs)
+ {
+ data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
+ memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
+ memset(data->attrs, 0, new * sizeof(Id *));
+ }
+ data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
+ memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
+ memset(data->incoreoffset, 0, new * sizeof(Id));
+ data->start = p;
+ }
+}
+
+/* shrink end of repodata */
+void
+repodata_shrink(Repodata *data, int end)
+{
+ int i;
+
+ if (data->end <= end)
+ return;
+ if (data->start >= end)
+ {
+ if (data->attrs)
+ {
+ for (i = 0; i < data->end - data->start; i++)
+ solv_free(data->attrs[i]);
+ data->attrs = solv_free(data->attrs);
+ }
+ data->incoreoffset = solv_free(data->incoreoffset);
+ data->start = data->end = 0;
+ return;
+ }
+ if (data->attrs)
+ {
+ for (i = end; i < data->end; i++)
+ solv_free(data->attrs[i - data->start]);
+ data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
+ }
+ if (data->incoreoffset)
+ data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
+ data->end = end;
+}
+
+/* extend repodata so that it includes solvables from start to start + num - 1 */
+void
+repodata_extend_block(Repodata *data, Id start, Id num)
+{
+ if (!num)
+ return;
+ if (!data->incoreoffset)
+ {
+ /* this also means that data->attrs is NULL */
+ data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
+ data->start = start;
+ data->end = start + num;
+ return;
+ }
+ repodata_extend(data, start);
+ if (num > 1)
+ repodata_extend(data, start + num - 1);
+}
+
+/**********************************************************************/
+
+
+#define REPODATA_ATTRS_BLOCK 31
+#define REPODATA_ATTRDATA_BLOCK 1023
+#define REPODATA_ATTRIDDATA_BLOCK 63
+#define REPODATA_ATTRNUM64DATA_BLOCK 15
+
+
+Id
+repodata_new_handle(Repodata *data)
+{
+ if (!data->nxattrs)
+ {
+ data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
+ data->nxattrs = 2; /* -1: SOLVID_META */
+ }
+ data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
+ data->xattrs[data->nxattrs] = 0;
+ return -(data->nxattrs++);
+}
+
+static inline Id **
+repodata_get_attrp(Repodata *data, Id handle)
+{
+ if (handle < 0)
+ {
+ if (handle == SOLVID_META && !data->xattrs)
+ {
+ data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
+ data->nxattrs = 2;
+ }
+ return data->xattrs - handle;
+ }
+ if (handle < data->start || handle >= data->end)
+ repodata_extend(data, handle);
+ if (!data->attrs)
+ data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
+ return data->attrs + (handle - data->start);
+}
+
+static void
+repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
+{
+ Id *pp;
+ Id *ap, **app;
+ int i;
+
+ app = repodata_get_attrp(data, handle);
+ ap = *app;
+ i = 0;
+ if (ap)
+ {
+ /* Determine equality based on the name only, allows us to change
+ type (when overwrite is set), and makes TYPE_CONSTANT work. */
+ for (pp = ap; *pp; pp += 2)
+ if (data->keys[*pp].name == data->keys[keyid].name)
+ break;
+ if (*pp)
+ {
+ if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
+ {
+ pp[0] = keyid;
+ pp[1] = val;
+ }
+ return;
+ }
+ i = pp - ap;
+ }
+ ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
+ *app = ap;
+ pp = ap + i;
+ *pp++ = keyid;
+ *pp++ = val;
+ *pp = 0;
+}
+
+
+static void
+repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
+{
+ Id keyid;
+
+ keyid = repodata_key2id(data, key, 1);
+ repodata_insert_keyid(data, solvid, keyid, val, 1);
+}
+
+void
+repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
+{
+ Repokey key;
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_ID;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ repodata_set(data, solvid, &key, id);
+}
+
+void
+repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
+{
+ Repokey key;
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_NUM;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ if (num >= 0x80000000)
+ {
+ data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
+ data->attrnum64data[data->attrnum64datalen] = num;
+ num = 0x80000000 | data->attrnum64datalen++;
+ }
+ repodata_set(data, solvid, &key, (Id)num);
+}
+
+void
+repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
+{
+ Repokey key;
+ Id id;
+ if (data->localpool)
+ id = stringpool_str2id(&data->spool, str, 1);
+ else
+ id = pool_str2id(data->repo->pool, str, 1);
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_ID;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ repodata_set(data, solvid, &key, id);
+}
+
+void
+repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
+{
+ Repokey key;
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_CONSTANT;
+ key.size = constant;
+ key.storage = KEY_STORAGE_INCORE;
+ repodata_set(data, solvid, &key, 0);
+}
+
+void
+repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
+{
+ Repokey key;
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_CONSTANTID;
+ key.size = id;
+ key.storage = KEY_STORAGE_INCORE;
+ repodata_set(data, solvid, &key, 0);
+}
+
+void
+repodata_set_void(Repodata *data, Id solvid, Id keyname)
+{
+ Repokey key;
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_VOID;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ repodata_set(data, solvid, &key, 0);
+}
+
+void
+repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
+{
+ Repokey key;
+ int l;
+
+ l = strlen(str) + 1;
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_STR;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
+ memcpy(data->attrdata + data->attrdatalen, str, l);
+ repodata_set(data, solvid, &key, data->attrdatalen);
+ data->attrdatalen += l;
+}
+
+void
+repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
+{
+ Repokey key;
+ unsigned char *dp;
+
+ if (len < 0)
+ return;
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_BINARY;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
+ dp = data->attrdata + data->attrdatalen;
+ if (len >= (1 << 14))
+ {
+ if (len >= (1 << 28))
+ *dp++ = (len >> 28) | 128;
+ if (len >= (1 << 21))
+ *dp++ = (len >> 21) | 128;
+ *dp++ = (len >> 14) | 128;
+ }
+ if (len >= (1 << 7))
+ *dp++ = (len >> 7) | 128;
+ *dp++ = len & 127;
+ if (len)
+ memcpy(dp, buf, len);
+ repodata_set(data, solvid, &key, data->attrdatalen);
+ data->attrdatalen = dp + len - data->attrdata;
+}
+
+/* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
+ * so that the caller can append entrysize new elements plus the termination zero there */
+static void
+repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
+{
+ int oldsize;
+ Id *ida, *pp, **ppp;
+
+ /* check if it is the same as last time, this speeds things up a lot */
+ if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
+ {
+ /* great! just append the new data */
+ data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+ data->attriddatalen--; /* overwrite terminating 0 */
+ data->lastdatalen += entrysize;
+ return;
+ }
+
+ ppp = repodata_get_attrp(data, handle);
+ pp = *ppp;
+ if (pp)
+ {
+ for (; *pp; pp += 2)
+ if (data->keys[*pp].name == keyname)
+ break;
+ }
+ if (!pp || !*pp || data->keys[*pp].type != keytype)
+ {
+ /* not found. allocate new key */
+ Repokey key;
+ Id keyid;
+ key.name = keyname;
+ key.type = keytype;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+ keyid = repodata_key2id(data, &key, 1);
+ repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
+ data->lasthandle = handle;
+ data->lastkey = keyid;
+ data->lastdatalen = data->attriddatalen + entrysize + 1;
+ return;
+ }
+ oldsize = 0;
+ for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
+ oldsize += entrysize;
+ if (ida + 1 == data->attriddata + data->attriddatalen)
+ {
+ /* this was the last entry, just append it */
+ data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+ data->attriddatalen--; /* overwrite terminating 0 */
+ }
+ else
+ {
+ /* too bad. move to back. */
+ data->attriddata = solv_extend(data->attriddata, data->attriddatalen, oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+ memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
+ pp[1] = data->attriddatalen;
+ data->attriddatalen += oldsize;
+ }
+ data->lasthandle = handle;
+ data->lastkey = *pp;
+ data->lastdatalen = data->attriddatalen + entrysize + 1;
+}
+
+void
+repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
+ const unsigned char *str)
+{
+ Repokey key;
+ int l;
+
+ if (!(l = solv_chksum_len(type)))
+ return;
+ key.name = keyname;
+ key.type = type;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
+ memcpy(data->attrdata + data->attrdatalen, str, l);
+ repodata_set(data, solvid, &key, data->attrdatalen);
+ data->attrdatalen += l;
+}
+
+void
+repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
+ const char *str)
+{
+ unsigned char buf[64];
+ int l;
+
+ if (!(l = solv_chksum_len(type)))
+ return;
+ if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
+ return;
+ repodata_set_bin_checksum(data, solvid, keyname, type, buf);
+}
+
+const char *
+repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
+{
+ int l;
+
+ if (!(l = solv_chksum_len(type)))
+ return "";
+ return pool_bin2hex(data->repo->pool, buf, l);
+}
+
+/* rpm filenames don't contain the epoch, so strip it */
+static inline const char *
+evrid2vrstr(Pool *pool, Id evrid)
+{
+ const char *p, *evr = pool_id2str(pool, evrid);
+ if (!evr)
+ return evr;
+ for (p = evr; *p >= '0' && *p <= '9'; p++)
+ ;
+ return p != evr && *p == ':' && p[1] ? p + 1 : evr;
+}
+
+static inline void
+repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
+{
+ Id id;
+ if (data->localpool)
+ id = stringpool_strn2id(&data->spool, str, l, 1);
+ else
+ id = pool_strn2id(data->repo->pool, str, l, 1);
+ repodata_set_id(data, solvid, keyname, id);
+}
+
+static inline void
+repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
+{
+ if (!str[l])
+ repodata_set_str(data, solvid, keyname, str);
+ else
+ {
+ char *s = solv_strdup(str);
+ s[l] = 0;
+ repodata_set_str(data, solvid, keyname, s);
+ free(s);
+ }
+}
+
+void
+repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
+{
+ Pool *pool = data->repo->pool;
+ Solvable *s;
+ const char *str, *fp;
+ int l = 0;
+
+ if (medianr)
+ repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
+ if (!dir)
+ {
+ if ((dir = strrchr(file, '/')) != 0)
+ {
+ l = dir - file;
+ dir = file;
+ file = dir + l + 1;
+ if (!l)
+ l++;
+ }
+ }
+ else
+ l = strlen(dir);
+ if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
+ {
+ dir += 2;
+ l -= 2;
+ }
+ if (l == 1 && dir[0] == '.')
+ l = 0;
+ s = pool->solvables + solvid;
+ if (dir && l)
+ {
+ str = pool_id2str(pool, s->arch);
+ if (!strncmp(dir, str, l) && !str[l])
+ repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
+ else
+ repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
+ }
+ fp = file;
+ str = pool_id2str(pool, s->name);
+ l = strlen(str);
+ if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
+ {
+ fp += l + 1;
+ str = evrid2vrstr(pool, s->evr);
+ l = strlen(str);
+ if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
+ {
+ fp += l + 1;
+ str = pool_id2str(pool, s->arch);
+ l = strlen(str);
+ if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
+ {
+ repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
+ return;
+ }
+ }
+ }
+ repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
+}
+
+/* XXX: medianr is currently not stored */
+void
+repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
+{
+ int l = 0;
+ const char *evr, *suf, *s;
+
+ if (!dir)
+ {
+ if ((dir = strrchr(file, '/')) != 0)
+ {
+ l = dir - file;
+ dir = file;
+ file = dir + l + 1;
+ if (!l)
+ l++;
+ }
+ }
+ else
+ l = strlen(dir);
+ if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
+ {
+ dir += 2;
+ l -= 2;
+ }
+ if (l == 1 && dir[0] == '.')
+ l = 0;
+ if (dir && l)
+ repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
+ evr = strchr(file, '-');
+ if (evr)
+ {
+ for (s = evr - 1; s > file; s--)
+ if (*s == '-')
+ {
+ evr = s;
+ break;
+ }
+ }
+ suf = strrchr(file, '.');
+ if (suf)
+ {
+ for (s = suf - 1; s > file; s--)
+ if (*s == '.')
+ {
+ suf = s;
+ break;
+ }
+ if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
+ {
+ /* We accept one more item as suffix. */
+ for (s = suf - 1; s > file; s--)
+ if (*s == '.')
+ {
+ suf = s;
+ break;
+ }
+ }
+ }
+ if (!evr)
+ suf = 0;
+ if (suf && evr && suf < evr)
+ suf = 0;
+ repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
+ if (evr)
+ repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
+ if (suf)
+ repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
+}
+
+void
+repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
+{
+ Pool *pool = data->repo->pool;
+ Solvable *s = pool->solvables + solvid;
+ const char *p, *sevr, *sarch, *name, *evr;
+
+ p = strrchr(sourcepkg, '.');
+ if (!p || strcmp(p, ".rpm") != 0)
+ {
+ if (*sourcepkg)
+ repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
+ return;
+ }
+ p--;
+ while (p > sourcepkg && *p != '.')
+ p--;
+ if (*p != '.' || p == sourcepkg)
+ return;
+ sarch = p-- + 1;
+ while (p > sourcepkg && *p != '-')
+ p--;
+ if (*p != '-' || p == sourcepkg)
+ return;
+ p--;
+ while (p > sourcepkg && *p != '-')
+ p--;
+ if (*p != '-' || p == sourcepkg)
+ return;
+ sevr = p + 1;
+ pool = s->repo->pool;
+
+ name = pool_id2str(pool, s->name);
+ if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
+ repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
+ else
+ repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
+
+ evr = evrid2vrstr(pool, s->evr);
+ if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
+ repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
+ else
+ repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
+
+ if (!strcmp(sarch, "src.rpm"))
+ repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
+ else if (!strcmp(sarch, "nosrc.rpm"))
+ repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
+ else
+ repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
+}
+
+void
+repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
+{
+ Repokey key;
+ int i;
+
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_IDARRAY;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ repodata_set(data, solvid, &key, data->attriddatalen);
+ data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+ for (i = 0; i < q->count; i++)
+ data->attriddata[data->attriddatalen++] = q->elements[i];
+ data->attriddata[data->attriddatalen++] = 0;
+}
+
+void
+repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
+{
+ assert(dir);
+#if 0
+fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
+#endif
+ repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
+ data->attriddata[data->attriddatalen++] = dir;
+ data->attriddata[data->attriddatalen++] = num;
+ data->attriddata[data->attriddatalen++] = num2;
+ data->attriddata[data->attriddatalen++] = 0;
+}
+
+void
+repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
+{
+ Id stroff;
+ int l;
+
+ assert(dir);
+ l = strlen(str) + 1;
+ data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
+ memcpy(data->attrdata + data->attrdatalen, str, l);
+ stroff = data->attrdatalen;
+ data->attrdatalen += l;
+
+#if 0
+fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str, data->attriddatalen);
+#endif
+ repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
+ data->attriddata[data->attriddatalen++] = dir;
+ data->attriddata[data->attriddatalen++] = stroff;
+ data->attriddata[data->attriddatalen++] = 0;
+}
+
+void
+repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
+{
+#if 0
+fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
+#endif
+ repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
+ data->attriddata[data->attriddatalen++] = id;
+ data->attriddata[data->attriddatalen++] = 0;
+}
+
+void
+repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
+ const char *str)
+{
+ Id id;
+ if (data->localpool)
+ id = stringpool_str2id(&data->spool, str, 1);
+ else
+ id = pool_str2id(data->repo->pool, str, 1);
+ repodata_add_idarray(data, solvid, keyname, id);
+}
+
+void
+repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
+{
+ repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
+ data->attriddata[data->attriddatalen++] = ghandle;
+ data->attriddata[data->attriddatalen++] = 0;
+}
+
+void
+repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
+{
+ repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
+ data->attriddata[data->attriddatalen++] = ghandle;
+ data->attriddata[data->attriddatalen++] = 0;
+}
+
+void
+repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
+{
+ Id *pp, *ap, **app;
+ app = repodata_get_attrp(data, solvid);
+ ap = *app;
+ if (!ap)
+ return;
+ for (; *ap; ap += 2)
+ if (data->keys[*ap].name == keyname)
+ break;
+ if (!*ap)
+ return;
+ pp = ap;
+ ap += 2;
+ for (; *ap; ap += 2)
+ {
+ if (data->keys[*ap].name == keyname)
+ continue;
+ *pp++ = ap[0];
+ *pp++ = ap[1];
+ }
+ *pp = 0;
+}
+
+/* XXX: does not work correctly, needs fix in iterators! */
+void
+repodata_unset(Repodata *data, Id solvid, Id keyname)
+{
+ Repokey key;
+ key.name = keyname;
+ key.type = REPOKEY_TYPE_DELETED;
+ key.size = 0;
+ key.storage = KEY_STORAGE_INCORE;
+ repodata_set(data, solvid, &key, 0);
+}
+
+/* add all (uninternalized) attrs from src to dest */
+void
+repodata_merge_attrs(Repodata *data, Id dest, Id src)
+{
+ Id *keyp;
+ if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
+ return;
+ for (; *keyp; keyp += 2)
+ repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
+}
+
+/* add some (uninternalized) attrs from src to dest */
+void
+repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
+{
+ Id *keyp;
+ if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
+ return;
+ for (; *keyp; keyp += 2)
+ if (!keyidmap || MAPTST(keyidmap, keyp[0]))
+ repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
+}
+
+/* swap (uninternalized) attrs from src and dest */
+void
+repodata_swap_attrs(Repodata *data, Id dest, Id src)
+{
+ Id *tmpattrs;
+ if (!data->attrs || dest == src)
+ return;
+ if (dest < data->start || dest >= data->end)
+ repodata_extend(data, dest);
+ if (src < data->start || src >= data->end)
+ repodata_extend(data, src);
+ tmpattrs = data->attrs[dest - data->start];
+ data->attrs[dest - data->start] = data->attrs[src - data->start];
+ data->attrs[src - data->start] = tmpattrs;
+}
+
+
+/**********************************************************************/
+
+/* TODO: unify with repo_write and repo_solv! */
+
+#define EXTDATA_BLOCK 1023
+
+struct extdata {
+ unsigned char *buf;
+ int len;
+};
+
+static void
+data_addid(struct extdata *xd, Id sx)
+{
+ unsigned int x = (unsigned int)sx;
+ unsigned char *dp;
+
+ xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
+ dp = xd->buf + xd->len;
+
+ if (x >= (1 << 14))
+ {
+ if (x >= (1 << 28))
+ *dp++ = (x >> 28) | 128;
+ if (x >= (1 << 21))
+ *dp++ = (x >> 21) | 128;
+ *dp++ = (x >> 14) | 128;
+ }
+ if (x >= (1 << 7))
+ *dp++ = (x >> 7) | 128;
+ *dp++ = x & 127;
+ xd->len = dp - xd->buf;
+}
+
+static void
+data_addid64(struct extdata *xd, unsigned long long x)
+{
+ if (x >= 0x100000000)
+ {
+ if ((x >> 35) != 0)
+ {
+ data_addid(xd, (Id)(x >> 35));
+ xd->buf[xd->len - 1] |= 128;
+ }
+ data_addid(xd, (Id)((unsigned int)x | 0x80000000));
+ xd->buf[xd->len - 5] = (x >> 28) | 128;
+ }
+ else
+ data_addid(xd, (Id)x);
+}
+
+static void
+data_addideof(struct extdata *xd, Id sx, int eof)
+{
+ unsigned int x = (unsigned int)sx;
+ unsigned char *dp;
+
+ xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
+ dp = xd->buf + xd->len;
+
+ if (x >= (1 << 13))
+ {
+ if (x >= (1 << 27))
+ *dp++ = (x >> 27) | 128;
+ if (x >= (1 << 20))
+ *dp++ = (x >> 20) | 128;
+ *dp++ = (x >> 13) | 128;
+ }
+ if (x >= (1 << 6))
+ *dp++ = (x >> 6) | 128;
+ *dp++ = eof ? (x & 63) : (x & 63) | 64;
+ xd->len = dp - xd->buf;
+}
+
+static void
+data_addblob(struct extdata *xd, unsigned char *blob, int len)
+{
+ xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
+ memcpy(xd->buf + xd->len, blob, len);
+ xd->len += len;
+}
+
+/*********************************/
+
+/* this is to reduct memory usage when internalizing oversized repos */
+static void
+compact_attrdata(Repodata *data, int entry, int nentry)
+{
+ int i;
+ unsigned int attrdatastart = data->attrdatalen;
+ unsigned int attriddatastart = data->attriddatalen;
+ if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
+ return;
+ for (i = entry; i < nentry; i++)
+ {
+ Id v, *attrs = data->attrs[i];
+ if (!attrs)
+ continue;
+ for (; *attrs; attrs += 2)
+ {
+ switch (data->keys[*attrs].type)
+ {
+ case REPOKEY_TYPE_STR:
+ case REPOKEY_TYPE_BINARY:
+ case_CHKSUM_TYPES:
+ if ((unsigned int)attrs[1] < attrdatastart)
+ attrdatastart = attrs[1];
+ break;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ for (v = attrs[1]; data->attriddata[v] ; v += 2)
+ if (data->attriddata[v + 1] < attrdatastart)
+ attrdatastart = data->attriddata[v + 1];
+ /* FALLTHROUGH */
+ case REPOKEY_TYPE_IDARRAY:
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ if ((unsigned int)attrs[1] < attriddatastart)
+ attriddatastart = attrs[1];
+ break;
+ case REPOKEY_TYPE_FIXARRAY:
+ case REPOKEY_TYPE_FLEXARRAY:
+ return;
+ default:
+ break;
+ }
+ }
+ }
+#if 0
+ printf("compact_attrdata %d %d\n", entry, nentry);
+ printf("attrdatastart: %d\n", attrdatastart);
+ printf("attriddatastart: %d\n", attriddatastart);
+#endif
+ if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
+ return;
+ for (i = entry; i < nentry; i++)
+ {
+ Id v, *attrs = data->attrs[i];
+ if (!attrs)
+ continue;
+ for (; *attrs; attrs += 2)
+ {
+ switch (data->keys[*attrs].type)
+ {
+ case REPOKEY_TYPE_STR:
+ case REPOKEY_TYPE_BINARY:
+ case_CHKSUM_TYPES:
+ attrs[1] -= attrdatastart;
+ break;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ for (v = attrs[1]; data->attriddata[v] ; v += 2)
+ data->attriddata[v + 1] -= attrdatastart;
+ /* FALLTHROUGH */
+ case REPOKEY_TYPE_IDARRAY:
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ attrs[1] -= attriddatastart;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (attrdatastart)
+ {
+ data->attrdatalen -= attrdatastart;
+ memmove(data->attrdata, data->attrdata + attrdatastart, data->attrdatalen);
+ data->attrdata = solv_extend_resize(data->attrdata, data->attrdatalen, 1, REPODATA_ATTRDATA_BLOCK);
+ }
+ if (attriddatastart)
+ {
+ data->attriddatalen -= attriddatastart;
+ memmove(data->attriddata, data->attriddata + attriddatastart, data->attriddatalen * sizeof(Id));
+ data->attriddata = solv_extend_resize(data->attriddata, data->attriddatalen, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+ }
+}
+
+/* internalalize some key into incore/vincore data */
+
+static void
+repodata_serialize_key(Repodata *data, struct extdata *newincore,
+ struct extdata *newvincore,
+ Id *schema,
+ Repokey *key, Id val)
+{
+ Id *ida;
+ struct extdata *xd;
+ unsigned int oldvincorelen = 0;
+ Id schemaid, *sp;
+
+ xd = newincore;
+ if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ xd = newvincore;
+ oldvincorelen = xd->len;
+ }
+ switch (key->type)
+ {
+ case REPOKEY_TYPE_VOID:
+ case REPOKEY_TYPE_CONSTANT:
+ case REPOKEY_TYPE_CONSTANTID:
+ case REPOKEY_TYPE_DELETED:
+ break;
+ case REPOKEY_TYPE_STR:
+ data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
+ break;
+ case REPOKEY_TYPE_MD5:
+ data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
+ break;
+ case REPOKEY_TYPE_SHA1:
+ data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
+ break;
+ case REPOKEY_TYPE_SHA224:
+ data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
+ break;
+ case REPOKEY_TYPE_SHA256:
+ data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
+ break;
+ case REPOKEY_TYPE_SHA384:
+ data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
+ break;
+ case REPOKEY_TYPE_SHA512:
+ data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
+ break;
+ case REPOKEY_TYPE_NUM:
+ if (val & 0x80000000)
+ {
+ data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
+ break;
+ }
+ /* FALLTHROUGH */
+ case REPOKEY_TYPE_ID:
+ case REPOKEY_TYPE_DIR:
+ data_addid(xd, val);
+ break;
+ case REPOKEY_TYPE_BINARY:
+ {
+ Id len;
+ unsigned char *dp = data_read_id(data->attrdata + val, &len);
+ dp += (unsigned int)len;
+ data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
+ }
+ break;
+ case REPOKEY_TYPE_IDARRAY:
+ for (ida = data->attriddata + val; *ida; ida++)
+ data_addideof(xd, ida[0], ida[1] ? 0 : 1);
+ break;
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ for (ida = data->attriddata + val; *ida; ida += 3)
+ {
+ data_addid(xd, ida[0]);
+ data_addid(xd, ida[1]);
+ data_addideof(xd, ida[2], ida[3] ? 0 : 1);
+ }
+ break;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ for (ida = data->attriddata + val; *ida; ida += 2)
+ {
+ data_addideof(xd, ida[0], ida[2] ? 0 : 1);
+ data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
+ }
+ break;
+ case REPOKEY_TYPE_FIXARRAY:
+ {
+ int num = 0;
+ schemaid = 0;
+ for (ida = data->attriddata + val; *ida; ida++)
+ {
+ Id *kp;
+ sp = schema;
+ kp = data->xattrs[-*ida];
+ if (!kp)
+ continue; /* ignore empty elements */
+ num++;
+ for (; *kp; kp += 2)
+ *sp++ = *kp;
+ *sp = 0;
+ if (!schemaid)
+ schemaid = repodata_schema2id(data, schema, 1);
+ else if (schemaid != repodata_schema2id(data, schema, 0))
+ {
+ pool_debug(data->repo->pool, SOLV_ERROR, "repodata_serialize_key: fixarray substructs with different schemas\n");
+ num = 0;
+ break;
+ }
+ }
+ data_addid(xd, num);
+ if (!num)
+ break;
+ data_addid(xd, schemaid);
+ for (ida = data->attriddata + val; *ida; ida++)
+ {
+ Id *kp = data->xattrs[-*ida];
+ if (!kp)
+ continue;
+ for (; *kp; kp += 2)
+ repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
+ }
+ break;
+ }
+ case REPOKEY_TYPE_FLEXARRAY:
+ {
+ int num = 0;
+ for (ida = data->attriddata + val; *ida; ida++)
+ num++;
+ data_addid(xd, num);
+ for (ida = data->attriddata + val; *ida; ida++)
+ {
+ Id *kp = data->xattrs[-*ida];
+ if (!kp)
+ {
+ data_addid(xd, 0); /* XXX */
+ continue;
+ }
+ sp = schema;
+ for (;*kp; kp += 2)
+ *sp++ = *kp;
+ *sp = 0;
+ schemaid = repodata_schema2id(data, schema, 1);
+ data_addid(xd, schemaid);
+ kp = data->xattrs[-*ida];
+ for (;*kp; kp += 2)
+ repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
+ }
+ break;
+ }
+ default:
+ pool_debug(data->repo->pool, SOLV_FATAL, "repodata_serialize_key: don't know how to handle type %d\n", key->type);
+ exit(1);
+ }
+ if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ /* put offset/len in incore */
+ data_addid(newincore, data->lastverticaloffset + oldvincorelen);
+ oldvincorelen = xd->len - oldvincorelen;
+ data_addid(newincore, oldvincorelen);
+ }
+}
+
+/* create a circular linked list of all keys that share
+ * the same keyname */
+static Id *
+calculate_keylink(Repodata *data)
+{
+ int i, j;
+ Id *link;
+ Id maxkeyname = 0, *keytable = 0;
+ link = solv_calloc(data->nkeys, sizeof(Id));
+ if (data->nkeys <= 2)
+ return link;
+ for (i = 1; i < data->nkeys; i++)
+ {
+ Id n = data->keys[i].name;
+ if (n >= maxkeyname)
+ {
+ keytable = solv_realloc2(keytable, n + 128, sizeof(Id));
+ memset(keytable + maxkeyname, 0, (n + 128 - maxkeyname) * sizeof(Id));
+ maxkeyname = n + 128;
+ }
+ j = keytable[n];
+ if (j)
+ link[i] = link[j];
+ else
+ j = i;
+ link[j] = i;
+ keytable[n] = i;
+ }
+ /* remove links that just point to themselfs */
+ for (i = 1; i < data->nkeys; i++)
+ if (link[i] == i)
+ link[i] = 0;
+ solv_free(keytable);
+ return link;
+}
+
+void
+repodata_internalize(Repodata *data)
+{
+ Repokey *key, solvkey;
+ Id entry, nentry;
+ Id schemaid, keyid, *schema, *sp, oldschemaid, *keyp, *seen;
+ Offset *oldincoreoffs = 0;
+ int schemaidx;
+ unsigned char *dp, *ndp;
+ int neednewschema;
+ struct extdata newincore;
+ struct extdata newvincore;
+ Id solvkeyid;
+ Id *keylink;
+ int haveoldkl;
+
+ if (!data->attrs && !data->xattrs)
+ return;
+
+#if 0
+ printf("repodata_internalize %d\n", data->repodataid);
+ printf(" attr data: %d K\n", data->attrdatalen / 1024);
+ printf(" attrid data: %d K\n", data->attriddatalen / (1024 / 4));
+#endif
+ newvincore.buf = data->vincore;
+ newvincore.len = data->vincorelen;
+
+ /* find the solvables key, create if needed */
+ memset(&solvkey, 0, sizeof(solvkey));
+ solvkey.name = REPOSITORY_SOLVABLES;
+ solvkey.type = REPOKEY_TYPE_FLEXARRAY;
+ solvkey.size = 0;
+ solvkey.storage = KEY_STORAGE_INCORE;
+ solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
+
+ schema = solv_malloc2(data->nkeys, sizeof(Id));
+ seen = solv_malloc2(data->nkeys, sizeof(Id));
+
+ /* Merge the data already existing (in data->schemata, ->incoredata and
+ friends) with the new attributes in data->attrs[]. */
+ nentry = data->end - data->start;
+ memset(&newincore, 0, sizeof(newincore));
+ data_addid(&newincore, 0); /* start data at offset 1 */
+
+ data->mainschema = 0;
+ data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
+
+ keylink = calculate_keylink(data);
+ /* join entry data */
+ /* we start with the meta data, entry -1 */
+ for (entry = -1; entry < nentry; entry++)
+ {
+ oldschemaid = 0;
+ dp = data->incoredata;
+ if (dp)
+ {
+ dp += entry >= 0 ? data->incoreoffset[entry] : 1;
+ dp = data_read_id(dp, &oldschemaid);
+ }
+ memset(seen, 0, data->nkeys * sizeof(Id));
+#if 0
+fprintf(stderr, "oldschemaid %d\n", oldschemaid);
+fprintf(stderr, "schemata %d\n", data->schemata[oldschemaid]);
+fprintf(stderr, "schemadata %p\n", data->schemadata);
+#endif
+
+ /* seen: -1: old data, 0: skipped, >0: id + 1 */
+ neednewschema = 0;
+ sp = schema;
+ haveoldkl = 0;
+ for (keyp = data->schemadata + data->schemata[oldschemaid]; *keyp; keyp++)
+ {
+ if (seen[*keyp])
+ {
+ /* oops, should not happen */
+ neednewschema = 1;
+ continue;
+ }
+ seen[*keyp] = -1; /* use old marker */
+ *sp++ = *keyp;
+ if (keylink[*keyp])
+ haveoldkl = 1; /* potential keylink conflict */
+ }
+
+ /* strip solvables key */
+ if (entry < 0 && solvkeyid && seen[solvkeyid])
+ {
+ *sp = 0;
+ for (sp = keyp = schema; *sp; sp++)
+ if (*sp != solvkeyid)
+ *keyp++ = *sp;
+ sp = keyp;
+ seen[solvkeyid] = 0;
+ neednewschema = 1;
+ }
+
+ /* add new entries */
+ if (entry >= 0)
+ keyp = data->attrs ? data->attrs[entry] : 0;
+ else
+ keyp = data->xattrs ? data->xattrs[1] : 0;
+ if (keyp)
+ for (; *keyp; keyp += 2)
+ {
+ if (!seen[*keyp])
+ {
+ neednewschema = 1;
+ *sp++ = *keyp;
+ if (haveoldkl && keylink[*keyp]) /* this should be pretty rare */
+ {
+ Id kl;
+ for (kl = keylink[*keyp]; kl != *keyp; kl = keylink[kl])
+ if (seen[kl] == -1)
+ {
+ /* replacing old key kl, remove from schema and seen */
+ Id *osp;
+ for (osp = schema; osp < sp; osp++)
+ if (*osp == kl)
+ {
+ memmove(osp, osp + 1, (sp - osp) * sizeof(Id));
+ sp--;
+ seen[kl] = 0;
+ break;
+ }
+ }
+ }
+ }
+ seen[*keyp] = keyp[1] + 1;
+ }
+
+ /* add solvables key if needed */
+ if (entry < 0 && data->end != data->start)
+ {
+ *sp++ = solvkeyid; /* always last in schema */
+ neednewschema = 1;
+ }
+
+ /* commit schema */
+ *sp = 0;
+ if (neednewschema)
+ /* Ideally we'd like to sort the new schema here, to ensure
+ schema equality independend of the ordering. */
+ schemaid = repodata_schema2id(data, schema, 1);
+ else
+ schemaid = oldschemaid;
+
+ if (entry < 0)
+ {
+ data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
+ data->mainschema = schemaid;
+ }
+
+ /* find offsets in old incore data */
+ if (oldschemaid)
+ {
+ Id *lastneeded = 0;
+ for (sp = data->schemadata + data->schemata[oldschemaid]; *sp; sp++)
+ if (seen[*sp] == -1)
+ lastneeded = sp + 1;
+ if (lastneeded)
+ {
+ if (!oldincoreoffs)
+ oldincoreoffs = solv_malloc2(data->nkeys, 2 * sizeof(Offset));
+ for (sp = data->schemadata + data->schemata[oldschemaid]; sp != lastneeded; sp++)
+ {
+ /* Skip the data associated with this old key. */
+ key = data->keys + *sp;
+ ndp = dp;
+ if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ ndp = data_skip(ndp, REPOKEY_TYPE_ID);
+ ndp = data_skip(ndp, REPOKEY_TYPE_ID);
+ }
+ else if (key->storage == KEY_STORAGE_INCORE)
+ ndp = data_skip_key(data, ndp, key);
+ oldincoreoffs[*sp * 2] = dp - data->incoredata;
+ oldincoreoffs[*sp * 2 + 1] = ndp - dp;
+ dp = ndp;
+ }
+ }
+ }
+
+ /* just copy over the complete old entry (including the schemaid) if there was no new data */
+ if (entry >= 0 && !neednewschema && oldschemaid && (!data->attrs || !data->attrs[entry]) && dp)
+ {
+ ndp = data->incoredata + data->incoreoffset[entry];
+ data->incoreoffset[entry] = newincore.len;
+ data_addblob(&newincore, ndp, dp - ndp);
+ goto entrydone;
+ }
+
+ /* Now create data blob. We walk through the (possibly new) schema
+ and either copy over old data, or insert the new. */
+ if (entry >= 0)
+ data->incoreoffset[entry] = newincore.len;
+ data_addid(&newincore, schemaid);
+
+ /* we don't use a pointer to the schemadata here as repodata_serialize_key
+ * may call repodata_schema2id() which might realloc our schemadata */
+ for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
+ {
+ if (entry < 0)
+ {
+ data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
+ if (keyid == solvkeyid)
+ {
+ /* add flexarray entry count */
+ data_addid(&newincore, data->end - data->start);
+ break; /* always the last entry */
+ }
+ }
+ if (seen[keyid] == -1)
+ {
+ if (oldincoreoffs[keyid * 2 + 1])
+ data_addblob(&newincore, data->incoredata + oldincoreoffs[keyid * 2], oldincoreoffs[keyid * 2 + 1]);
+ }
+ else if (seen[keyid])
+ repodata_serialize_key(data, &newincore, &newvincore, schema, data->keys + keyid, seen[keyid] - 1);
+ }
+
+entrydone:
+ /* free memory */
+ if (entry >= 0 && data->attrs)
+ {
+ if (data->attrs[entry])
+ data->attrs[entry] = solv_free(data->attrs[entry]);
+ if (entry && entry % 4096 == 0 && data->nxattrs <= 2 && entry + 64 < nentry)
+ {
+ compact_attrdata(data, entry + 1, nentry); /* try to free some memory */
+#if 0
+ printf(" attr data: %d K\n", data->attrdatalen / 1024);
+ printf(" attrid data: %d K\n", data->attriddatalen / (1024 / 4));
+ printf(" incore data: %d K\n", newincore.len / 1024);
+ printf(" sum: %d K\n", (newincore.len + data->attrdatalen + data->attriddatalen * 4) / 1024);
+ /* malloc_stats(); */
+#endif
+ }
+ }
+ }
+ /* free all xattrs */
+ for (entry = 0; entry < data->nxattrs; entry++)
+ if (data->xattrs[entry])
+ solv_free(data->xattrs[entry]);
+ data->xattrs = solv_free(data->xattrs);
+ data->nxattrs = 0;
+
+ data->lasthandle = 0;
+ data->lastkey = 0;
+ data->lastdatalen = 0;
+ solv_free(schema);
+ solv_free(seen);
+ solv_free(keylink);
+ solv_free(oldincoreoffs);
+ repodata_free_schemahash(data);
+
+ solv_free(data->incoredata);
+ data->incoredata = newincore.buf;
+ data->incoredatalen = newincore.len;
+ data->incoredatafree = 0;
+
+ solv_free(data->vincore);
+ data->vincore = newvincore.buf;
+ data->vincorelen = newvincore.len;
+
+ data->attrs = solv_free(data->attrs);
+ data->attrdata = solv_free(data->attrdata);
+ data->attriddata = solv_free(data->attriddata);
+ data->attrnum64data = solv_free(data->attrnum64data);
+ data->attrdatalen = 0;
+ data->attriddatalen = 0;
+ data->attrnum64datalen = 0;
+#if 0
+ printf("repodata_internalize %d done\n", data->repodataid);
+ printf(" incore data: %d K\n", data->incoredatalen / 1024);
+#endif
+}
+
+void
+repodata_disable_paging(Repodata *data)
+{
+ if (maybe_load_repodata(data, 0))
+ {
+ repopagestore_disable_paging(&data->store);
+ data->storestate++;
+ }
+}
+
+static void
+repodata_load_stub(Repodata *data)
+{
+ Repo *repo = data->repo;
+ Pool *pool = repo->pool;
+ int r, i;
+ struct _Pool_tmpspace oldtmpspace;
+ Datapos oldpos;
+
+ if (!pool->loadcallback)
+ {
+ data->state = REPODATA_ERROR;
+ return;
+ }
+ data->state = REPODATA_LOADING;
+
+ /* save tmp space and pos */
+ oldtmpspace = pool->tmpspace;
+ memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
+ oldpos = pool->pos;
+
+ r = pool->loadcallback(pool, data, pool->loadcallbackdata);
+
+ /* restore tmp space and pos */
+ for (i = 0; i < POOL_TMPSPACEBUF; i++)
+ solv_free(pool->tmpspace.buf[i]);
+ pool->tmpspace = oldtmpspace;
+ if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
+ memset(&oldpos, 0, sizeof(oldpos));
+ pool->pos = oldpos;
+
+ data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
+}
+
+static inline void
+repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
+{
+ Repokey xkey;
+
+ xkey.name = keyname;
+ xkey.type = keytype;
+ xkey.storage = KEY_STORAGE_INCORE;
+ xkey.size = 0;
+ repodata_key2id(data, &xkey, 1);
+}
+
+static Repodata *
+repodata_add_stub(Repodata **datap)
+{
+ Repodata *data = *datap;
+ Repo *repo = data->repo;
+ Id repodataid = data - repo->repodata;
+ Repodata *sdata = repo_add_repodata(repo, 0);
+ data = repo->repodata + repodataid;
+ if (data->end > data->start)
+ repodata_extend_block(sdata, data->start, data->end - data->start);
+ sdata->state = REPODATA_STUB;
+ sdata->loadcallback = repodata_load_stub;
+ *datap = data;
+ return sdata;
+}
+
+Repodata *
+repodata_create_stubs(Repodata *data)
+{
+ Repo *repo = data->repo;
+ Pool *pool = repo->pool;
+ Repodata *sdata;
+ int *stubdataids;
+ Dataiterator di;
+ Id xkeyname = 0;
+ int i, cnt = 0;
+
+ dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
+ while (dataiterator_step(&di))
+ if (di.data == data)
+ cnt++;
+ dataiterator_free(&di);
+ if (!cnt)
+ return data;
+ stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
+ for (i = 0; i < cnt; i++)
+ {
+ sdata = repodata_add_stub(&data);
+ stubdataids[i] = sdata - repo->repodata;
+ }
+ i = 0;
+ dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
+ sdata = 0;
+ while (dataiterator_step(&di))
+ {
+ if (di.data != data)
+ continue;
+ if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
+ {
+ dataiterator_entersub(&di);
+ sdata = repo->repodata + stubdataids[i++];
+ xkeyname = 0;
+ continue;
+ }
+ switch (di.key->type)
+ {
+ case REPOKEY_TYPE_ID:
+ repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
+ break;
+ case REPOKEY_TYPE_CONSTANTID:
+ repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
+ break;
+ case REPOKEY_TYPE_STR:
+ repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
+ break;
+ case REPOKEY_TYPE_VOID:
+ repodata_set_void(sdata, SOLVID_META, di.key->name);
+ break;
+ case REPOKEY_TYPE_NUM:
+ repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
+ break;
+ case_CHKSUM_TYPES:
+ repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
+ break;
+ case REPOKEY_TYPE_IDARRAY:
+ repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
+ if (di.key->name == REPOSITORY_KEYS)
+ {
+ if (!xkeyname)
+ {
+ if (!di.kv.eof)
+ xkeyname = di.kv.id;
+ }
+ else
+ {
+ repodata_add_stubkey(sdata, xkeyname, di.kv.id);
+ xkeyname = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ dataiterator_free(&di);
+ for (i = 0; i < cnt; i++)
+ repodata_internalize(repo->repodata + stubdataids[i]);
+ solv_free(stubdataids);
+ return data;
+}
+
+unsigned int
+repodata_memused(Repodata *data)
+{
+ return data->incoredatalen + data->vincorelen;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repodata.h
+ *
+ */
+
+#ifndef LIBSOLV_REPODATA_H
+#define LIBSOLV_REPODATA_H
+
+#include <stdio.h>
+
+#include "pooltypes.h"
+#include "pool.h"
+#include "dirpool.h"
+
+#ifdef LIBSOLV_INTERNAL
+#include "repopage.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SIZEOF_MD5 16
+#define SIZEOF_SHA1 20
+#define SIZEOF_SHA224 28
+#define SIZEOF_SHA256 32
+#define SIZEOF_SHA384 48
+#define SIZEOF_SHA512 64
+
+struct _Repo;
+struct _KeyValue;
+
+typedef struct _Repokey {
+ Id name;
+ Id type; /* REPOKEY_TYPE_xxx */
+ unsigned int size;
+ unsigned int storage; /* KEY_STORAGE_xxx */
+} Repokey;
+
+#define KEY_STORAGE_DROPPED 0
+#define KEY_STORAGE_SOLVABLE 1
+#define KEY_STORAGE_INCORE 2
+#define KEY_STORAGE_VERTICAL_OFFSET 3
+
+#ifdef LIBSOLV_INTERNAL
+struct dircache;
+#endif
+
+typedef struct _Repodata {
+ Id repodataid; /* our id */
+ struct _Repo *repo; /* back pointer to repo */
+
+#define REPODATA_AVAILABLE 0
+#define REPODATA_STUB 1
+#define REPODATA_ERROR 2
+#define REPODATA_STORE 3
+#define REPODATA_LOADING 4
+
+ int state; /* available, stub or error */
+
+ void (*loadcallback)(struct _Repodata *);
+
+ int start; /* start of solvables this repodata is valid for */
+ int end; /* last solvable + 1 of this repodata */
+
+ Repokey *keys; /* keys, first entry is always zero */
+ int nkeys; /* length of keys array */
+ unsigned char keybits[32]; /* keyname hash */
+
+ Id *schemata; /* schema -> offset into schemadata */
+ int nschemata; /* number of schemata */
+ Id *schemadata; /* schema storage */
+
+ Stringpool spool; /* local string pool */
+ int localpool; /* is local string pool used */
+
+ Dirpool dirpool; /* local dir pool */
+
+#ifdef LIBSOLV_INTERNAL
+ FILE *fp; /* file pointer of solv file */
+ int error; /* corrupt solv file */
+
+ unsigned int schemadatalen; /* schema storage size */
+ Id *schematahash; /* unification helper */
+
+ unsigned char *incoredata; /* in-core data */
+ unsigned int incoredatalen; /* in-core data used */
+ unsigned int incoredatafree; /* free data len */
+
+ Id mainschema; /* SOLVID_META schema */
+ Id *mainschemaoffsets; /* SOLVID_META offsets into incoredata */
+
+ Id *incoreoffset; /* offset for all entries */
+
+ Id *verticaloffset; /* offset for all verticals, nkeys elements */
+ Id lastverticaloffset; /* end of verticals */
+
+ Repopagestore store; /* our page store */
+ Id storestate; /* incremented every time the store might change */
+
+ unsigned char *vincore; /* internal vertical data */
+ unsigned int vincorelen; /* data size */
+
+ Id **attrs; /* un-internalized attributes */
+ Id **xattrs; /* anonymous handles */
+ int nxattrs; /* number of handles */
+
+ unsigned char *attrdata; /* their string data space */
+ unsigned int attrdatalen; /* its len */
+ Id *attriddata; /* their id space */
+ unsigned int attriddatalen; /* its len */
+ unsigned long long *attrnum64data; /* their 64bit num data space */
+ unsigned int attrnum64datalen; /* its len */
+
+ /* array cache to speed up repodata_add functions*/
+ Id lasthandle;
+ Id lastkey;
+ Id lastdatalen;
+
+ /* directory cache to speed up repodata_str2dir */
+ struct dircache *dircache;
+#endif
+
+} Repodata;
+
+#define SOLVID_META -1
+#define SOLVID_POS -2
+#define SOLVID_SUBSCHEMA -3 /* internal! */
+
+
+/*-----
+ * management functions
+ */
+void repodata_initdata(Repodata *data, struct _Repo *repo, int localpool);
+void repodata_freedata(Repodata *data);
+
+void repodata_free(Repodata *data);
+void repodata_empty(Repodata *data, int localpool);
+
+
+/*
+ * key management functions
+ */
+Id repodata_key2id(Repodata *data, Repokey *key, int create);
+
+static inline Repokey *
+repodata_id2key(Repodata *data, Id keyid)
+{
+ return data->keys + keyid;
+}
+
+/*
+ * schema management functions
+ */
+Id repodata_schema2id(Repodata *data, Id *schema, int create);
+void repodata_free_schemahash(Repodata *data);
+
+static inline Id *
+repodata_id2schema(Repodata *data, Id schemaid)
+{
+ return data->schemadata + data->schemata[schemaid];
+}
+
+/*
+ * data search and access
+ */
+
+/* check if there is a chance that the repodata contains data for
+ * the specified keyname */
+static inline int
+repodata_precheck_keyname(Repodata *data, Id keyname)
+{
+ unsigned char x = data->keybits[(keyname >> 3) & (sizeof(data->keybits) - 1)];
+ return x && (x & (1 << (keyname & 7))) ? 1 : 0;
+}
+
+/* check if the repodata contains data for the specified keyname */
+static inline int
+repodata_has_keyname(Repodata *data, Id keyname)
+{
+ int i;
+ if (!repodata_precheck_keyname(data, keyname))
+ return 0;
+ for (i = 1; i < data->nkeys; i++)
+ if (data->keys[i].name == keyname)
+ return 1;
+ return 0;
+}
+
+/* search key <keyname> (all keys, if keyname == 0) for Id <solvid>
+ * Call <callback> for each match */
+void repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, struct _KeyValue *kv), void *cbdata);
+
+/* Make sure the found KeyValue has the "str" field set. Return "str"
+ * if valid, NULL if not possible */
+const char *repodata_stringify(Pool *pool, Repodata *data, Repokey *key, struct _KeyValue *kv, int flags);
+
+int repodata_filelistfilter_matches(Repodata *data, const char *str);
+
+
+/* lookup functions */
+Id repodata_lookup_type(Repodata *data, Id solvid, Id keyname);
+Id repodata_lookup_id(Repodata *data, Id solvid, Id keyname);
+const char *repodata_lookup_str(Repodata *data, Id solvid, Id keyname);
+int repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value);
+int repodata_lookup_void(Repodata *data, Id solvid, Id keyname);
+const unsigned char *repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep);
+int repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q);
+const void *repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp);
+
+
+/*-----
+ * data assignment functions
+ */
+
+/*
+ * extend the data so that it contains the specified solvables
+ * (no longer needed, as the repodata_set functions autoextend)
+ */
+void repodata_extend(Repodata *data, Id p);
+void repodata_extend_block(Repodata *data, Id p, int num);
+void repodata_shrink(Repodata *data, int end);
+
+/* internalize freshly set data, so that it is found by the search
+ * functions and written out */
+void repodata_internalize(Repodata *data);
+
+/* create an anonymous handle. useful for substructures like
+ * fixarray/flexarray */
+Id repodata_new_handle(Repodata *data);
+
+/* basic types: void, num, string, Id */
+void repodata_set_void(Repodata *data, Id solvid, Id keyname);
+void repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num);
+void repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id);
+void repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str);
+void repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len);
+/* create id from string, then set_id */
+void repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str);
+
+/* set numeric constant */
+void repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant);
+
+/* set Id constant */
+void repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id);
+
+/* checksum */
+void repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
+ const unsigned char *buf);
+void repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
+ const char *str);
+void repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q);
+
+
+/* directory (for package file list) */
+void repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2);
+void repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str);
+void repodata_free_dircache(Repodata *data);
+
+
+/* Arrays */
+void repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id);
+void repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname, const char *str);
+void repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle);
+void repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle);
+
+void repodata_unset(Repodata *data, Id solvid, Id keyname);
+void repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname);
+
+/*
+ merge/swap attributes from one solvable to another
+ works only if the data is not yet internalized
+*/
+void repodata_merge_attrs(Repodata *data, Id dest, Id src);
+void repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite);
+void repodata_swap_attrs(Repodata *data, Id dest, Id src);
+
+Repodata *repodata_create_stubs(Repodata *data);
+
+/*
+ * load all paged data, used to speed up copying in repo_rpmdb
+ */
+void repodata_disable_paging(Repodata *data);
+
+/* helper functions */
+Id repodata_globalize_id(Repodata *data, Id id, int create);
+Id repodata_localize_id(Repodata *data, Id id, int create);
+Id repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create);
+
+Id repodata_str2dir(Repodata *data, const char *dir, int create);
+const char *repodata_dir2str(Repodata *data, Id did, const char *suf);
+const char *repodata_chk2str(Repodata *data, Id type, const unsigned char *buf);
+void repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file);
+void repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file);
+void repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg);
+Id repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid);
+const char *repodata_lookup_dirstrarray_uninternalized(Repodata *data, Id solvid, Id keyname, Id *didp, Id *iterp);
+
+/* stats */
+unsigned int repodata_memused(Repodata *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_REPODATA_H */
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* pack/unpack functions for key data */
+
+#ifndef LIBSOLV_REPOPACK_H
+#define LIBSOLV_REPOPACK_H
+
+static inline unsigned char *
+data_read_id(unsigned char *dp, Id *idp)
+{
+ Id x;
+ unsigned char c;
+ if (!(dp[0] & 0x80))
+ {
+ *idp = dp[0];
+ return dp + 1;
+ }
+ if (!(dp[1] & 0x80))
+ {
+ *idp = dp[0] << 7 ^ dp[1] ^ 0x4000;
+ return dp + 2;
+ }
+ if (!(dp[2] & 0x80))
+ {
+ *idp = dp[0] << 14 ^ dp[1] << 7 ^ dp[2] ^ 0x204000;
+ return dp + 3;
+ }
+ if (!(dp[3] & 0x80))
+ {
+ *idp = dp[0] << 21 ^ dp[1] << 14 ^ dp[2] << 7 ^ dp[3] ^ 0x10204000;
+ return dp + 4;
+ }
+ x = dp[0] << 28 ^ dp[1] << 21 ^ dp[2] << 14 ^ dp[3] << 7 ^ dp[4] ^ 0x10204000;
+ if (!(dp[4] & 0x80))
+ {
+ *idp = x;
+ return dp + 5;
+ }
+ x ^= 80;
+ dp += 5;
+ for (;;)
+ {
+ c = *dp++;
+ if (!(c & 0x80))
+ {
+ *idp = (x << 7) ^ c;
+ return dp;
+ }
+ x = (x << 7) ^ (c ^ 128);
+ }
+}
+
+static inline unsigned char *
+data_read_num64(unsigned char *dp, unsigned int *low, unsigned int *high)
+{
+ unsigned long long int x;
+ unsigned char c;
+
+ *high = 0;
+ if (!(dp[0] & 0x80))
+ {
+ *low = dp[0];
+ return dp + 1;
+ }
+ if (!(dp[1] & 0x80))
+ {
+ *low = dp[0] << 7 ^ dp[1] ^ 0x4000;
+ return dp + 2;
+ }
+ if (!(dp[2] & 0x80))
+ {
+ *low = dp[0] << 14 ^ dp[1] << 7 ^ dp[2] ^ 0x204000;
+ return dp + 3;
+ }
+ if (!(dp[3] & 0x80))
+ {
+ *low = dp[0] << 21 ^ dp[1] << 14 ^ dp[2] << 7 ^ dp[3] ^ 0x10204000;
+ return dp + 4;
+ }
+ if (!(dp[4] & 0x80))
+ {
+ *low = dp[0] << 28 ^ dp[1] << 21 ^ dp[2] << 14 ^ dp[3] << 7 ^ dp[4] ^ 0x10204000;
+ *high = (dp[0] ^ 0x80) >> 4;
+ return dp + 5;
+ }
+ x = (unsigned long long)(dp[0] ^ 0x80) << 28 ^ (unsigned int)(dp[1] << 21 ^ dp[2] << 14 ^ dp[3] << 7 ^ dp[4] ^ 0x10204080);
+ dp += 5;
+ for (;;)
+ {
+ c = *dp++;
+ if (!(c & 0x80))
+ {
+ x = (x << 7) ^ c;
+ *low = x;
+ *high = x >> 32;
+ return dp;
+ }
+ x = (x << 7) ^ (c ^ 128);
+ }
+}
+
+static inline unsigned char *
+data_read_ideof(unsigned char *dp, Id *idp, int *eof)
+{
+ Id x = 0;
+ unsigned char c;
+ for (;;)
+ {
+ c = *dp++;
+ if (!(c & 0x80))
+ {
+ if (c & 0x40)
+ {
+ c ^= 0x40;
+ *eof = 0;
+ }
+ else
+ *eof = 1;
+ *idp = (x << 6) ^ c;
+ return dp;
+ }
+ x = (x << 7) ^ c ^ 128;
+ }
+}
+
+static inline unsigned char *
+data_read_u32(unsigned char *dp, unsigned int *nump)
+{
+ *nump = (dp[0] << 24) | (dp[1] << 16) | (dp[2] << 8) | dp[3];
+ return dp + 4;
+}
+
+static inline unsigned char *
+data_fetch(unsigned char *dp, KeyValue *kv, Repokey *key)
+{
+ kv->eof = 1;
+ if (!dp)
+ return 0;
+ switch (key->type)
+ {
+ case REPOKEY_TYPE_VOID:
+ return dp;
+ case REPOKEY_TYPE_CONSTANT:
+ kv->num2 = 0;
+ kv->num = key->size;
+ return dp;
+ case REPOKEY_TYPE_CONSTANTID:
+ kv->id = key->size;
+ return dp;
+ case REPOKEY_TYPE_STR:
+ kv->str = (const char *)dp;
+ return dp + strlen(kv->str) + 1;
+ case REPOKEY_TYPE_ID:
+ case REPOKEY_TYPE_DIR:
+ return data_read_id(dp, &kv->id);
+ case REPOKEY_TYPE_NUM:
+ return data_read_num64(dp, &kv->num, &kv->num2);
+ case REPOKEY_TYPE_U32:
+ kv->num2 = 0;
+ return data_read_u32(dp, &kv->num);
+ case REPOKEY_TYPE_MD5:
+ kv->num = 0; /* not stringified yet */
+ kv->str = (const char *)dp;
+ return dp + SIZEOF_MD5;
+ case REPOKEY_TYPE_SHA1:
+ kv->num = 0; /* not stringified yet */
+ kv->str = (const char *)dp;
+ return dp + SIZEOF_SHA1;
+ case REPOKEY_TYPE_SHA224:
+ kv->num = 0; /* not stringified yet */
+ kv->str = (const char *)dp;
+ return dp + SIZEOF_SHA224;
+ case REPOKEY_TYPE_SHA256:
+ kv->num = 0; /* not stringified yet */
+ kv->str = (const char *)dp;
+ return dp + SIZEOF_SHA256;
+ case REPOKEY_TYPE_SHA384:
+ kv->num = 0; /* not stringified yet */
+ kv->str = (const char *)dp;
+ return dp + SIZEOF_SHA384;
+ case REPOKEY_TYPE_SHA512:
+ kv->num = 0; /* not stringified yet */
+ kv->str = (const char *)dp;
+ return dp + SIZEOF_SHA512;
+ case REPOKEY_TYPE_BINARY:
+ dp = data_read_id(dp, (Id *)&kv->num);
+ kv->str = (const char *)dp;
+ return dp + kv->num;
+ case REPOKEY_TYPE_IDARRAY:
+ return data_read_ideof(dp, &kv->id, &kv->eof);
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ dp = data_read_ideof(dp, &kv->id, &kv->eof);
+ kv->num = 0; /* not stringified yet */
+ kv->str = (const char *)dp;
+ return dp + strlen(kv->str) + 1;
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ dp = data_read_id(dp, &kv->id);
+ dp = data_read_id(dp, (Id *)&kv->num);
+ return data_read_ideof(dp, (Id *)&kv->num2, &kv->eof);
+ case REPOKEY_TYPE_FIXARRAY:
+ dp = data_read_id(dp, (Id *)&kv->num);
+ return data_read_id(dp, &kv->id);
+ case REPOKEY_TYPE_FLEXARRAY:
+ return data_read_id(dp, (Id *)&kv->num);
+ default:
+ return 0;
+ }
+}
+
+static inline unsigned char *
+data_skip(unsigned char *dp, int type)
+{
+ unsigned char x;
+ switch (type)
+ {
+ case REPOKEY_TYPE_VOID:
+ case REPOKEY_TYPE_CONSTANT:
+ case REPOKEY_TYPE_CONSTANTID:
+ case REPOKEY_TYPE_DELETED:
+ return dp;
+ case REPOKEY_TYPE_ID:
+ case REPOKEY_TYPE_NUM:
+ case REPOKEY_TYPE_DIR:
+ while ((*dp & 0x80) != 0)
+ dp++;
+ return dp + 1;
+ case REPOKEY_TYPE_U32:
+ return dp + 4;
+ case REPOKEY_TYPE_MD5:
+ return dp + SIZEOF_MD5;
+ case REPOKEY_TYPE_SHA1:
+ return dp + SIZEOF_SHA1;
+ case REPOKEY_TYPE_SHA224:
+ return dp + SIZEOF_SHA224;
+ case REPOKEY_TYPE_SHA256:
+ return dp + SIZEOF_SHA256;
+ case REPOKEY_TYPE_SHA384:
+ return dp + SIZEOF_SHA384;
+ case REPOKEY_TYPE_SHA512:
+ return dp + SIZEOF_SHA512;
+ case REPOKEY_TYPE_IDARRAY:
+ case REPOKEY_TYPE_REL_IDARRAY:
+ while ((*dp & 0xc0) != 0)
+ dp++;
+ return dp + 1;
+ case REPOKEY_TYPE_STR:
+ while ((*dp) != 0)
+ dp++;
+ return dp + 1;
+ case REPOKEY_TYPE_BINARY:
+ {
+ unsigned int len;
+ dp = data_read_id(dp, (Id *)&len);
+ return dp + len;
+ }
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ for (;;)
+ {
+ while ((*dp & 0x80) != 0)
+ dp++;
+ x = *dp++;
+ while ((*dp) != 0)
+ dp++;
+ dp++;
+ if (!(x & 0x40))
+ return dp;
+ }
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ for (;;)
+ {
+ while ((*dp & 0x80) != 0)
+ dp++;
+ dp++;
+ while ((*dp & 0x80) != 0)
+ dp++;
+ dp++;
+ while ((*dp & 0x80) != 0)
+ dp++;
+ if (!(*dp & 0x40))
+ return dp + 1;
+ dp++;
+ }
+ default:
+ return 0;
+ }
+}
+
+static inline unsigned char *
+data_skip_verify(unsigned char *dp, int type, int maxid, int maxdir)
+{
+ Id id;
+ int eof;
+
+ switch (type)
+ {
+ case REPOKEY_TYPE_VOID:
+ case REPOKEY_TYPE_CONSTANT:
+ case REPOKEY_TYPE_CONSTANTID:
+ case REPOKEY_TYPE_DELETED:
+ return dp;
+ case REPOKEY_TYPE_NUM:
+ while ((*dp & 0x80) != 0)
+ dp++;
+ return dp + 1;
+ case REPOKEY_TYPE_U32:
+ return dp + 4;
+ case REPOKEY_TYPE_MD5:
+ return dp + SIZEOF_MD5;
+ case REPOKEY_TYPE_SHA1:
+ return dp + SIZEOF_SHA1;
+ case REPOKEY_TYPE_SHA224:
+ return dp + SIZEOF_SHA224;
+ case REPOKEY_TYPE_SHA256:
+ return dp + SIZEOF_SHA256;
+ case REPOKEY_TYPE_SHA384:
+ return dp + SIZEOF_SHA384;
+ case REPOKEY_TYPE_SHA512:
+ return dp + SIZEOF_SHA512;
+ case REPOKEY_TYPE_ID:
+ dp = data_read_id(dp, &id);
+ if (id >= maxid)
+ return 0;
+ return dp;
+ case REPOKEY_TYPE_DIR:
+ dp = data_read_id(dp, &id);
+ if (id >= maxdir)
+ return 0;
+ return dp;
+ case REPOKEY_TYPE_IDARRAY:
+ for (;;)
+ {
+ dp = data_read_ideof(dp, &id, &eof);
+ if (id >= maxid)
+ return 0;
+ if (eof)
+ return dp;
+ }
+ case REPOKEY_TYPE_STR:
+ while ((*dp) != 0)
+ dp++;
+ return dp + 1;
+ case REPOKEY_TYPE_BINARY:
+ {
+ unsigned int len;
+ dp = data_read_id(dp, (Id *)&len);
+ return dp + len;
+ }
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ for (;;)
+ {
+ dp = data_read_ideof(dp, &id, &eof);
+ if (id >= maxdir)
+ return 0;
+ while ((*dp) != 0)
+ dp++;
+ dp++;
+ if (eof)
+ return dp;
+ }
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ for (;;)
+ {
+ dp = data_read_id(dp, &id);
+ if (id >= maxdir)
+ return 0;
+ while ((*dp & 0x80) != 0)
+ dp++;
+ dp++;
+ while ((*dp & 0x80) != 0)
+ dp++;
+ if (!(*dp & 0x40))
+ return dp + 1;
+ dp++;
+ }
+ default:
+ return 0;
+ }
+}
+
+#endif /* LIBSOLV_REPOPACK */
--- /dev/null
+/*
+ * Copyright (c) 2007-2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repopage.c
+ *
+ * Paging and compression functions for the vertical repository data.
+ * Vertical data is grouped by key, normal data is grouped by solvable.
+ * This makes searching for a string in vertical data fast as there's
+ * no need to skip over data if keys we're not interested in.
+ *
+ * The vertical data is split into pages, each page is compressed with a fast
+ * compression algorithm. These pages are read in on demand, not recently used
+ * pages automatically get dropped.
+ */
+
+#define _XOPEN_SOURCE 500
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include "repo.h"
+#include "repopage.h"
+
+
+
+#define BLOCK_SIZE (65536*1)
+#if BLOCK_SIZE <= 65536
+typedef uint16_t Ref;
+#else
+typedef uint32_t Ref;
+#endif
+
+/*
+ The format is tailored for fast decompression (i.e. only byte based),
+ and skewed to ASCII content (highest bit often not set):
+
+ a 0LLLLLLL
+ - self-describing ASCII character hex L
+ b 100lllll <l+1 bytes>
+ - literal run of length l+1
+ c 101oolll <8o>
+ - back ref of length l+2, at offset -(o+1) (o < 1 << 10)
+ d 110lllll <8o>
+ - back ref of length l+2+8, at offset -(o+1) (o < 1 << 8)
+ e 1110llll <8o> <8o>
+ - back ref of length l+3, at offset -(o+1) (o < 1 << 16)
+ f1 1111llll <8l> <8o> <8o>
+ - back ref, length l+19 (l < 1<<12), offset -(o+1) (o < 1<<16)
+ f2 11110lll <8l> <8o> <8o>
+ - back ref, length l+19 (l < 1<<11), offset -(o+1) (o < 1<<16)
+ g 11111lll <8l> <8o> <8o> <8o>
+ - back ref, length l+5 (l < 1<<11), offset -(o+1) (o < 1<<24)
+
+ Generally for a literal of length L we need L+1 bytes, hence it is
+ better to encode also very short backrefs (2 chars) as backrefs if
+ their offset is small, as that only needs two bytes. Except if we
+ already have a literal run, in that case it's better to append there,
+ instead of breaking it for a backref. So given a potential backref
+ at offset O, length L the strategy is as follows:
+
+ L < 2 : encode as 1-literal
+ L == 2, O > 1024 : encode as 1-literal
+ L == 2, have already literals: encode as 1-literal
+ O = O - 1
+ L >= 2, L <= 9, O < 1024 : encode as c
+ L >= 10, L <= 41, O < 256 : encode as d
+ else we have either O >= 1024, or L >= 42:
+ L < 3 : encode as 1-literal
+ L >= 3, L <= 18, O < 65536 : encode as e
+ L >= 19, L <= 4095+18, O < 65536 : encode as f
+ else we have either L >= 4096+18 or O >= 65536.
+ O >= 65536: encode as 1-literal, too bad
+ (with the current block size this can't happen)
+ L >= 4096+18, so reduce to 4095+18 : encode as f
+*/
+
+
+static unsigned int
+compress_buf(const unsigned char *in, unsigned int in_len,
+ unsigned char *out, unsigned int out_len)
+{
+ unsigned int oo = 0; /* out-offset */
+ unsigned int io = 0; /* in-offset */
+#define HS (65536)
+ Ref htab[HS];
+ Ref hnext[BLOCK_SIZE];
+ unsigned int litofs = 0;
+ memset(htab, -1, sizeof (htab));
+ memset(hnext, -1, sizeof (hnext));
+ while (io + 2 < in_len)
+ {
+ /* Search for a match of the string starting at IN, we have at
+ least three characters. */
+ unsigned int hval = in[io] | in[io + 1] << 8 | in[io + 2] << 16;
+ unsigned int try, mlen, mofs, tries;
+ hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
+ hval = hval & (HS - 1);
+ try = htab[hval];
+ hnext[io] = htab[hval];
+ htab[hval] = io;
+ mlen = 0;
+ mofs = 0;
+
+ for (tries = 0; try != -1 && tries < 12; tries++)
+ {
+ if (try < io
+ && in[try] == in[io] && in[try + 1] == in[io + 1])
+ {
+ mlen = 2;
+ mofs = (io - try) - 1;
+ break;
+ }
+ try = hnext[try];
+ }
+ for (; try != -1 && tries < 12; tries++)
+ {
+ /* assert(mlen >= 2); */
+ /* assert(io + mlen < in_len); */
+ /* Try a match starting from [io] with the strings at [try].
+ That's only sensible if TRY actually is before IO (can happen
+ with uninit hash table). If we have a previous match already
+ we're only going to take the new one if it's longer, hence
+ check the potentially last character. */
+ if (try < io && in[try + mlen] == in[io + mlen])
+ {
+ unsigned int this_len, this_ofs;
+ if (memcmp(in + try, in + io, mlen))
+ goto no_match;
+ this_len = mlen + 1;
+ /* Now try extending the match by more characters. */
+ for (;
+ io + this_len < in_len
+ && in[try + this_len] == in[io + this_len]; this_len++)
+ ;
+#if 0
+ unsigned int testi;
+ for (testi = 0; testi < this_len; testi++)
+ assert(in[try + testi] == in[io + testi]);
+#endif
+ this_ofs = (io - try) - 1;
+ /*if (this_ofs > 65535)
+ goto no_match; */
+#if 0
+ assert(this_len >= 2);
+ assert(this_len >= mlen);
+ assert(this_len > mlen || (this_len == mlen && this_ofs > mofs));
+#endif
+ mlen = this_len, mofs = this_ofs;
+ /* If our match extends up to the end of input, no next
+ match can become better. This is not just an
+ optimization, it establishes a loop invariant
+ (io + mlen < in_len). */
+ if (io + mlen >= in_len)
+ goto match_done;
+ }
+ no_match:
+ try = hnext[try];
+ /*if (io - try - 1 >= 65536)
+ break;*/
+ }
+
+match_done:
+ if (mlen)
+ {
+ /*fprintf(stderr, "%d %d\n", mlen, mofs);*/
+ if (mlen == 2 && (litofs || mofs >= 1024))
+ mlen = 0;
+ /*else if (mofs >= 65536)
+ mlen = 0;*/
+ else if (mofs >= 65536)
+ {
+ if (mlen >= 2048 + 5)
+ mlen = 2047 + 5;
+ else if (mlen < 5)
+ mlen = 0;
+ }
+ else if (mlen < 3)
+ mlen = 0;
+ /*else if (mlen >= 4096 + 19)
+ mlen = 4095 + 19;*/
+ else if (mlen >= 2048 + 19)
+ mlen = 2047 + 19;
+ /* Skip this match if the next character would deliver a better one,
+ but only do this if we have the chance to really extend the
+ length (i.e. our current length isn't yet the (conservative)
+ maximum). */
+ if (mlen && mlen < (2048 + 5) && io + 3 < in_len)
+ {
+ unsigned int hval =
+ in[io + 1] | in[io + 2] << 8 | in[io + 3] << 16;
+ unsigned int try;
+ hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
+ hval = hval & (HS - 1);
+ try = htab[hval];
+ if (try < io + 1
+ && in[try] == in[io + 1] && in[try + 1] == in[io + 2])
+ {
+ unsigned int this_len;
+ this_len = 2;
+ for (;
+ io + 1 + this_len < in_len
+ && in[try + this_len] == in[io + 1 + this_len];
+ this_len++)
+ ;
+ if (this_len >= mlen)
+ mlen = 0;
+ }
+ }
+ }
+ if (!mlen)
+ {
+ if (!litofs)
+ litofs = io + 1;
+ io++;
+ }
+ else
+ {
+ if (litofs)
+ {
+ unsigned litlen;
+ litofs--;
+ litlen = io - litofs;
+ /* fprintf(stderr, "lit: %d\n", litlen); */
+ while (litlen)
+ {
+ unsigned int easy_sz;
+ /* Emit everything we can as self-describers. As soon as
+ we hit a byte we can't emit as such we're going to emit
+ a length descriptor anyway, so we can as well include
+ bytes < 0x80 which might follow afterwards in that run. */
+ for (easy_sz = 0;
+ easy_sz < litlen && in[litofs + easy_sz] < 0x80;
+ easy_sz++)
+ ;
+ if (easy_sz)
+ {
+ if (oo + easy_sz >= out_len)
+ return 0;
+ memcpy(out + oo, in + litofs, easy_sz);
+ litofs += easy_sz;
+ oo += easy_sz;
+ litlen -= easy_sz;
+ if (!litlen)
+ break;
+ }
+ if (litlen <= 32)
+ {
+ if (oo + 1 + litlen >= out_len)
+ return 0;
+ out[oo++] = 0x80 | (litlen - 1);
+ while (litlen--)
+ out[oo++] = in[litofs++];
+ break;
+ }
+ else
+ {
+ /* Literal length > 32, so chunk it. */
+ if (oo + 1 + 32 >= out_len)
+ return 0;
+ out[oo++] = 0x80 | 31;
+ memcpy(out + oo, in + litofs, 32);
+ oo += 32;
+ litofs += 32;
+ litlen -= 32;
+ }
+ }
+ litofs = 0;
+ }
+
+ /* fprintf(stderr, "ref: %d @ %d\n", mlen, mofs); */
+
+ if (mlen >= 2 && mlen <= 9 && mofs < 1024)
+ {
+ if (oo + 2 >= out_len)
+ return 0;
+ out[oo++] = 0xa0 | ((mofs & 0x300) >> 5) | (mlen - 2);
+ out[oo++] = mofs & 0xff;
+ }
+ else if (mlen >= 10 && mlen <= 41 && mofs < 256)
+ {
+ if (oo + 2 >= out_len)
+ return 0;
+ out[oo++] = 0xc0 | (mlen - 10);
+ out[oo++] = mofs;
+ }
+ else if (mofs >= 65536)
+ {
+ assert(mlen >= 5 && mlen < 2048 + 5);
+ if (oo + 5 >= out_len)
+ return 0;
+ out[oo++] = 0xf8 | ((mlen - 5) >> 8);
+ out[oo++] = (mlen - 5) & 0xff;
+ out[oo++] = mofs & 0xff;
+ out[oo++] = (mofs >> 8) & 0xff;
+ out[oo++] = mofs >> 16;
+ }
+ else if (mlen >= 3 && mlen <= 18)
+ {
+ assert(mofs < 65536);
+ if (oo + 3 >= out_len)
+ return 0;
+ out[oo++] = 0xe0 | (mlen - 3);
+ out[oo++] = mofs & 0xff;
+ out[oo++] = mofs >> 8;
+ }
+ else
+ {
+ assert(mlen >= 19 && mlen <= 4095 + 19 && mofs < 65536);
+ if (oo + 4 >= out_len)
+ return 0;
+ out[oo++] = 0xf0 | ((mlen - 19) >> 8);
+ out[oo++] = (mlen - 19) & 0xff;
+ out[oo++] = mofs & 0xff;
+ out[oo++] = mofs >> 8;
+ }
+ /* Insert the hashes for the compressed run [io..io+mlen-1].
+ For [io] we have it already done at the start of the loop.
+ So it's from [io+1..io+mlen-1], and we need three chars per
+ hash, so the accessed characters will be [io+1..io+mlen-1+2],
+ ergo io+mlen+1 < in_len. */
+ mlen--;
+ io++;
+ while (mlen--)
+ {
+ if (io + 2 < in_len)
+ {
+ unsigned int hval =
+ in[io] | in[io + 1] << 8 | in[io + 2] << 16;
+ hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
+ hval = hval & (HS - 1);
+ hnext[io] = htab[hval];
+ htab[hval] = io;
+ }
+ io++;
+ };
+ }
+ }
+ /* We might have some characters left. */
+ if (io < in_len && !litofs)
+ litofs = io + 1;
+ io = in_len;
+ if (litofs)
+ {
+ unsigned litlen;
+ litofs--;
+ litlen = io - litofs;
+ /* fprintf(stderr, "lit: %d\n", litlen); */
+ while (litlen)
+ {
+ unsigned int easy_sz;
+ /* Emit everything we can as self-describers. As soon as we hit a
+ byte we can't emit as such we're going to emit a length
+ descriptor anyway, so we can as well include bytes < 0x80 which
+ might follow afterwards in that run. */
+ for (easy_sz = 0; easy_sz < litlen && in[litofs + easy_sz] < 0x80;
+ easy_sz++)
+ ;
+ if (easy_sz)
+ {
+ if (oo + easy_sz >= out_len)
+ return 0;
+ memcpy(out + oo, in + litofs, easy_sz);
+ litofs += easy_sz;
+ oo += easy_sz;
+ litlen -= easy_sz;
+ if (!litlen)
+ break;
+ }
+ if (litlen <= 32)
+ {
+ if (oo + 1 + litlen >= out_len)
+ return 0;
+ out[oo++] = 0x80 | (litlen - 1);
+ while (litlen--)
+ out[oo++] = in[litofs++];
+ break;
+ }
+ else
+ {
+ /* Literal length > 32, so chunk it. */
+ if (oo + 1 + 32 >= out_len)
+ return 0;
+ out[oo++] = 0x80 | 31;
+ memcpy(out + oo, in + litofs, 32);
+ oo += 32;
+ litofs += 32;
+ litlen -= 32;
+ }
+ }
+ litofs = 0;
+ }
+ return oo;
+}
+
+static unsigned int
+unchecked_decompress_buf(const unsigned char *in, unsigned int in_len,
+ unsigned char *out,
+ unsigned int out_len __attribute__((unused)))
+{
+ unsigned char *orig_out = out;
+ const unsigned char *in_end = in + in_len;
+ while (in < in_end)
+ {
+ unsigned int first = *in++;
+ int o;
+ switch (first >> 4)
+ {
+ default:
+ /* This default case can't happen, but GCCs VRP is not strong
+ enough to see this, so make this explicitely not fall to
+ the end of the switch, so that we don't have to initialize
+ o above. */
+ continue;
+ case 0: case 1:
+ case 2: case 3:
+ case 4: case 5:
+ case 6: case 7:
+ /* a 0LLLLLLL */
+ /* fprintf (stderr, "lit: 1\n"); */
+ *out++ = first;
+ continue;
+ case 8: case 9:
+ /* b 100lllll <l+1 bytes> */
+ {
+ unsigned int l = first & 31;
+ /* fprintf (stderr, "lit: %d\n", l); */
+ do
+ *out++ = *in++;
+ while (l--);
+ continue;
+ }
+ case 10: case 11:
+ /* c 101oolll <8o> */
+ {
+ o = first & (3 << 3);
+ o = (o << 5) | *in++;
+ first = (first & 7) + 2;
+ break;
+ }
+ case 12: case 13:
+ /* d 110lllll <8o> */
+ {
+ o = *in++;
+ first = (first & 31) + 10;
+ break;
+ }
+ case 14:
+ /* e 1110llll <8o> <8o> */
+ {
+ o = in[0] | (in[1] << 8);
+ in += 2;
+ first = first & 31;
+ first += 3;
+ break;
+ }
+ case 15:
+ /* f1 1111llll <8o> <8o> <8l> */
+ /* f2 11110lll <8o> <8o> <8l> */
+ /* g 11111lll <8o> <8o> <8o> <8l> */
+ {
+ first = first & 15;
+ if (first >= 8)
+ {
+ first = (((first - 8) << 8) | in[0]) + 5;
+ o = in[1] | (in[2] << 8) | (in[3] << 16);
+ in += 4;
+ }
+ else
+ {
+ first = ((first << 8) | in[0]) + 19;
+ o = in[1] | (in[2] << 8);
+ in += 3;
+ }
+ break;
+ }
+ }
+ /* fprintf(stderr, "ref: %d @ %d\n", first, o); */
+ o++;
+ o = -o;
+#if 0
+ /* We know that first will not be zero, and this loop structure is
+ better optimizable. */
+ do
+ {
+ *out = *(out - o);
+ out++;
+ }
+ while (--first);
+#else
+ switch (first)
+ {
+ case 18: *out = *(out + o); out++;
+ case 17: *out = *(out + o); out++;
+ case 16: *out = *(out + o); out++;
+ case 15: *out = *(out + o); out++;
+ case 14: *out = *(out + o); out++;
+ case 13: *out = *(out + o); out++;
+ case 12: *out = *(out + o); out++;
+ case 11: *out = *(out + o); out++;
+ case 10: *out = *(out + o); out++;
+ case 9: *out = *(out + o); out++;
+ case 8: *out = *(out + o); out++;
+ case 7: *out = *(out + o); out++;
+ case 6: *out = *(out + o); out++;
+ case 5: *out = *(out + o); out++;
+ case 4: *out = *(out + o); out++;
+ case 3: *out = *(out + o); out++;
+ case 2: *out = *(out + o); out++;
+ case 1: *out = *(out + o); out++;
+ case 0: break;
+ default:
+ /* Duff duff :-) */
+ switch (first & 15)
+ {
+ do
+ {
+ case 0: *out = *(out + o); out++;
+ case 15: *out = *(out + o); out++;
+ case 14: *out = *(out + o); out++;
+ case 13: *out = *(out + o); out++;
+ case 12: *out = *(out + o); out++;
+ case 11: *out = *(out + o); out++;
+ case 10: *out = *(out + o); out++;
+ case 9: *out = *(out + o); out++;
+ case 8: *out = *(out + o); out++;
+ case 7: *out = *(out + o); out++;
+ case 6: *out = *(out + o); out++;
+ case 5: *out = *(out + o); out++;
+ case 4: *out = *(out + o); out++;
+ case 3: *out = *(out + o); out++;
+ case 2: *out = *(out + o); out++;
+ case 1: *out = *(out + o); out++;
+ }
+ while ((int)(first -= 16) > 0);
+ }
+ break;
+ }
+#endif
+ }
+ return out - orig_out;
+}
+
+/**********************************************************************/
+
+void repopagestore_init(Repopagestore *store)
+{
+ memset(store, 0, sizeof(*store));
+ store->pagefd = -1;
+}
+
+void repopagestore_free(Repopagestore *store)
+{
+ store->blob_store = solv_free(store->blob_store);
+ store->file_pages = solv_free(store->file_pages);
+ store->mapped_at = solv_free(store->mapped_at);
+ store->mapped = solv_free(store->mapped);
+ if (store->pagefd != -1)
+ close(store->pagefd);
+ store->pagefd = -1;
+}
+
+
+/**********************************************************************/
+
+unsigned char *
+repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigned int pend)
+{
+/* Make sure all pages from PSTART to PEND (inclusive) are loaded,
+ and are consecutive. Return a pointer to the mapping of PSTART. */
+ unsigned char buf[REPOPAGE_BLOBSIZE];
+ unsigned int i, best, pnum;
+
+ if (pstart == pend)
+ {
+ /* Quick check in case the requested page is already mapped */
+ if (store->mapped_at[pstart] != -1)
+ return store->blob_store + store->mapped_at[pstart];
+ }
+ else
+ {
+ /* Quick check in case all pages are already mapped and consecutive. */
+ for (pnum = pstart; pnum <= pend; pnum++)
+ if (store->mapped_at[pnum] == -1
+ || (pnum > pstart
+ && store->mapped_at[pnum]
+ != store->mapped_at[pnum-1] + REPOPAGE_BLOBSIZE))
+ break;
+ if (pnum > pend)
+ return store->blob_store + store->mapped_at[pstart];
+ }
+
+ if (store->pagefd == -1 || !store->file_pages)
+ return 0; /* no backing file */
+
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "PAGE: want %d pages starting at %d\n", pend - pstart + 1, pstart);
+#endif
+
+ /* Ensure that we can map the numbers of pages we need at all. */
+ if (pend - pstart + 1 > store->nmapped)
+ {
+ unsigned int oldcan = store->nmapped;
+ store->nmapped = pend - pstart + 1;
+ if (store->nmapped < 4)
+ store->nmapped = 4;
+ store->mapped = solv_realloc2(store->mapped, store->nmapped, sizeof(store->mapped[0]));
+ for (i = oldcan; i < store->nmapped; i++)
+ store->mapped[i] = -1;
+ store->blob_store = solv_realloc2(store->blob_store, store->nmapped, REPOPAGE_BLOBSIZE);
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "PAGE: can map %d pages\n", store->nmapped);
+#endif
+ }
+
+ if (store->mapped_at[pstart] != -1)
+ {
+ /* assume forward search */
+ best = store->mapped_at[pstart] / REPOPAGE_BLOBSIZE;
+ if (best + (pend - pstart) >= store->nmapped)
+ best = 0;
+ }
+ else if (store->mapped_at[pend] != -1)
+ {
+ /* assume backward search */
+ best = store->mapped_at[pend] / REPOPAGE_BLOBSIZE;
+ if (best < pend - pstart)
+ best = store->nmapped - 1;
+ best -= pend - pstart;
+ }
+ else
+ {
+ /* choose some "random" location to avoid thrashing */
+ best = (pstart + store->rr_counter++) % (store->nmapped - pend + pstart);
+ }
+
+ /* So we want to map our pages from [best] to [best+pend-pstart].
+ Use a very simple strategy, which doesn't make the best use of
+ our resources, but works. Throw away all pages in that range
+ (even ours) then copy around ours or read them in. */
+ for (i = best, pnum = pstart; pnum <= pend; i++, pnum++)
+ {
+ unsigned int pnum_mapped_at;
+ unsigned int oldpnum = store->mapped[i];
+ if (oldpnum != -1)
+ {
+ if (oldpnum == pnum)
+ continue; /* already have the correct page */
+ /* Evict this page. */
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "PAGE: evict page %d from %d\n", oldpnum, i);
+#endif
+ store->mapped[i] = -1;
+ store->mapped_at[oldpnum] = -1;
+ }
+ /* check if we can copy the correct content (before it gets evicted) */
+ pnum_mapped_at = store->mapped_at[pnum];
+ if (pnum_mapped_at != -1 && pnum_mapped_at != i * REPOPAGE_BLOBSIZE)
+ {
+ void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "PAGECOPY: %d from %d to %d\n", pnum, pnum_mapped_at / REPOPAGE_BLOBSIZE, i);
+#endif
+ memcpy(dest, store->blob_store + pnum_mapped_at, REPOPAGE_BLOBSIZE);
+ store->mapped[pnum_mapped_at / REPOPAGE_BLOBSIZE] = -1;
+ store->mapped[i] = pnum;
+ store->mapped_at[pnum] = i * REPOPAGE_BLOBSIZE;
+ }
+ }
+
+ /* Everything is free now. Read in or copy the pages we want. */
+ for (i = best, pnum = pstart; pnum <= pend; i++, pnum++)
+ {
+ void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
+ if (store->mapped_at[pnum] != -1)
+ {
+ unsigned int pnum_mapped_at = store->mapped_at[pnum];
+ if (pnum_mapped_at != i * REPOPAGE_BLOBSIZE)
+ {
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "PAGECOPY: %d from %d to %d\n", pnum, pnum_mapped_at / REPOPAGE_BLOBSIZE, i);
+#endif
+ /* Still mapped somewhere else, so just copy it from there. */
+ memcpy(dest, store->blob_store + pnum_mapped_at, REPOPAGE_BLOBSIZE);
+ store->mapped[pnum_mapped_at / REPOPAGE_BLOBSIZE] = -1;
+ }
+ }
+ else
+ {
+ Attrblobpage *p = store->file_pages + pnum;
+ unsigned int in_len = p->page_size;
+ unsigned int compressed = in_len & 1;
+ in_len >>= 1;
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "PAGEIN: %d to %d", pnum, i);
+#endif
+ if (pread(store->pagefd, compressed ? buf : dest, in_len, store->file_offset + p->page_offset) != in_len)
+ {
+ perror("mapping pread");
+ return 0;
+ }
+ if (compressed)
+ {
+ unsigned int out_len;
+ out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE);
+ if (out_len != REPOPAGE_BLOBSIZE && pnum < store->num_pages - 1)
+ {
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "can't decompress\n");
+#endif
+ return 0;
+ }
+#ifdef DEBUG_PAGING
+ fprintf(stderr, " (expand %d to %d)", in_len, out_len);
+#endif
+ }
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "\n");
+#endif
+ }
+ store->mapped_at[pnum] = i * REPOPAGE_BLOBSIZE;
+ store->mapped[i] = pnum;
+ }
+ return store->blob_store + best * REPOPAGE_BLOBSIZE;
+}
+
+unsigned int
+repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
+{
+ return compress_buf(page, len, cpage, max);
+}
+
+#define SOLV_ERROR_EOF 3
+#define SOLV_ERROR_CORRUPT 6
+
+static inline 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)
+ return 0;
+ x = (x << 8) | c;
+ }
+ return x;
+}
+
+/* Try to either setup on-demand paging (using FP as backing
+ file), or in case that doesn't work (FP not seekable) slurps in
+ all pages and deactivates paging. */
+int
+repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int pagesz, unsigned int blobsz)
+{
+ unsigned int npages;
+ unsigned int i;
+ unsigned int can_seek;
+ unsigned int cur_page_ofs;
+ unsigned char buf[REPOPAGE_BLOBSIZE];
+
+ if (pagesz != REPOPAGE_BLOBSIZE)
+ {
+ /* We could handle this by slurping in everything. */
+ return SOLV_ERROR_CORRUPT;
+ }
+ can_seek = 1;
+ if ((store->file_offset = ftell(fp)) < 0)
+ can_seek = 0;
+ clearerr(fp);
+ if (can_seek)
+ store->pagefd = dup(fileno(fp));
+ if (store->pagefd == -1)
+ can_seek = 0;
+ else
+ fcntl(store->pagefd, F_SETFD, FD_CLOEXEC);
+
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "can %sseek\n", can_seek ? "" : "NOT ");
+#endif
+ npages = (blobsz + REPOPAGE_BLOBSIZE - 1) / REPOPAGE_BLOBSIZE;
+
+ store->num_pages = npages;
+ store->mapped_at = solv_malloc2(npages, sizeof(*store->mapped_at));
+
+ /* If we can't seek on our input we have to slurp in everything.
+ * Otherwise set up file_pages containing offest/length of the
+ * pages */
+ if (can_seek)
+ store->file_pages = solv_malloc2(npages, sizeof(*store->file_pages));
+ else
+ store->blob_store = solv_malloc2(npages, REPOPAGE_BLOBSIZE);
+ cur_page_ofs = 0;
+ for (i = 0; i < npages; i++)
+ {
+ unsigned int in_len = read_u32(fp);
+ unsigned int compressed = in_len & 1;
+ in_len >>= 1;
+#ifdef DEBUG_PAGING
+ fprintf(stderr, "page %d: len %d (%scompressed)\n",
+ i, in_len, compressed ? "" : "not ");
+#endif
+ if (can_seek)
+ {
+ Attrblobpage *p = store->file_pages + i;
+ cur_page_ofs += 4;
+ store->mapped_at[i] = -1; /* not mapped yet */
+ p->page_offset = cur_page_ofs;
+ p->page_size = in_len * 2 + compressed;
+ if (fseek(fp, in_len, SEEK_CUR) < 0)
+ {
+ /* We can't fall back to non-seeking behaviour as we already
+ read over some data pages without storing them away. */
+ close(store->pagefd);
+ store->pagefd = -1;
+ return SOLV_ERROR_EOF;
+ }
+ cur_page_ofs += in_len;
+ }
+ else
+ {
+ unsigned int out_len;
+ void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
+ store->mapped_at[i] = i * REPOPAGE_BLOBSIZE;
+ /* We can't seek, so suck everything in. */
+ if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
+ {
+ perror("fread");
+ return SOLV_ERROR_EOF;
+ }
+ if (compressed)
+ {
+ out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE);
+ if (out_len != REPOPAGE_BLOBSIZE && i < npages - 1)
+ {
+ return SOLV_ERROR_CORRUPT;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void
+repopagestore_disable_paging(Repopagestore *store)
+{
+ if (store->num_pages)
+ repopagestore_load_page_range(store, 0, store->num_pages - 1);
+}
+
+#ifdef STANDALONE
+
+static void
+transfer_file(FILE * from, FILE * to, int compress)
+{
+ unsigned char inb[BLOCK_SIZE];
+ unsigned char outb[BLOCK_SIZE];
+ while (!feof (from) && !ferror (from))
+ {
+ unsigned int in_len, out_len;
+ if (compress)
+ {
+ in_len = fread(inb, 1, BLOCK_SIZE, from);
+ if (in_len)
+ {
+ unsigned char *b = outb;
+ out_len = compress_buf(inb, in_len, outb, sizeof (outb));
+ if (!out_len)
+ b = inb, out_len = in_len;
+ if (fwrite(&out_len, sizeof (out_len), 1, to) != 1)
+ {
+ perror("write size");
+ exit (1);
+ }
+ if (fwrite(b, out_len, 1, to) != 1)
+ {
+ perror("write data");
+ exit (1);
+ }
+ }
+ }
+ else
+ {
+ if (fread(&in_len, sizeof(in_len), 1, from) != 1)
+ {
+ if (feof(from))
+ return;
+ perror("can't read size");
+ exit(1);
+ }
+ if (fread(inb, in_len, 1, from) != 1)
+ {
+ perror("can't read data");
+ exit(1);
+ }
+ out_len =
+ unchecked_decompress_buf(inb, in_len, outb, sizeof(outb));
+ if (fwrite(outb, out_len, 1, to) != 1)
+ {
+ perror("can't write output");
+ exit(1);
+ }
+ }
+ }
+}
+
+/* Just for benchmarking purposes. */
+static void
+dumb_memcpy(void *dest, const void *src, unsigned int len)
+{
+ char *d = dest;
+ const char *s = src;
+ while (len--)
+ *d++ = *s++;
+}
+
+static void
+benchmark(FILE * from)
+{
+ unsigned char inb[BLOCK_SIZE];
+ unsigned char outb[BLOCK_SIZE];
+ unsigned int in_len = fread(inb, 1, BLOCK_SIZE, from);
+ unsigned int out_len;
+ if (!in_len)
+ {
+ perror("can't read from input");
+ exit(1);
+ }
+
+ unsigned int calib_loop;
+ unsigned int per_loop;
+ unsigned int i, j;
+ clock_t start, end;
+ float seconds;
+
+#if 0
+ calib_loop = 1;
+ per_loop = 0;
+ start = clock();
+ while ((clock() - start) < CLOCKS_PER_SEC / 4)
+ {
+ calib_loop *= 2;
+ for (i = 0; i < calib_loop; i++)
+ dumb_memcpy(outb, inb, in_len);
+ per_loop += calib_loop;
+ }
+
+ fprintf(stderr, "memcpy:\nCalibrated to %d iterations per loop\n",
+ per_loop);
+
+ start = clock();
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < per_loop; j++)
+ dumb_memcpy(outb, inb, in_len);
+ end = clock();
+ seconds = (end - start) / (float) CLOCKS_PER_SEC;
+ fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
+ ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
+#endif
+
+ calib_loop = 1;
+ per_loop = 0;
+ start = clock();
+ while ((clock() - start) < CLOCKS_PER_SEC / 4)
+ {
+ calib_loop *= 2;
+ for (i = 0; i < calib_loop; i++)
+ compress_buf(inb, in_len, outb, sizeof(outb));
+ per_loop += calib_loop;
+ }
+
+ fprintf(stderr, "compression:\nCalibrated to %d iterations per loop\n",
+ per_loop);
+
+ start = clock();
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < per_loop; j++)
+ compress_buf(inb, in_len, outb, sizeof(outb));
+ end = clock();
+ seconds = (end - start) / (float) CLOCKS_PER_SEC;
+ fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
+ ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
+
+ out_len = compress_buf(inb, in_len, outb, sizeof(outb));
+
+ calib_loop = 1;
+ per_loop = 0;
+ start = clock();
+ while ((clock() - start) < CLOCKS_PER_SEC / 4)
+ {
+ calib_loop *= 2;
+ for (i = 0; i < calib_loop; i++)
+ unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
+ per_loop += calib_loop;
+ }
+
+ fprintf(stderr, "decompression:\nCalibrated to %d iterations per loop\n",
+ per_loop);
+
+ start = clock();
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < per_loop; j++)
+ unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
+ end = clock();
+ seconds = (end - start) / (float) CLOCKS_PER_SEC;
+ fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
+ ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
+}
+
+int
+main(int argc, char *argv[])
+{
+ int compress = 1;
+ if (argc > 1 && !strcmp(argv[1], "-d"))
+ compress = 0;
+ if (argc > 1 && !strcmp(argv[1], "-b"))
+ benchmark(stdin);
+ else
+ transfer_file(stdin, stdout, compress);
+ return 0;
+}
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2011, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef LIBSOLV_REPOPAGE_H
+#define LIBSOLV_REPOPAGE_H
+
+#define REPOPAGE_BLOBBITS 15
+#define REPOPAGE_BLOBSIZE (1 << REPOPAGE_BLOBBITS)
+
+typedef struct _Attrblobpage
+{
+ /* page_size == 0 means the page is not backed by some file storage.
+ Otherwise it is L*2+(compressed ? 1 : 0), with L being the data
+ length. */
+ unsigned int page_offset;
+ unsigned int page_size;
+} Attrblobpage;
+
+typedef struct _Repopagestore {
+ int pagefd; /* file descriptor we're paging from */
+ long file_offset; /* pages in file start here */
+
+ unsigned char *blob_store;
+ unsigned int num_pages;
+
+ /* mapped_at[page] == -1 --> not loaded, otherwise offset into
+ store->blob_store. The size of the mapping is REPOPAGE_BLOBSIZE
+ except for the last page. */
+ unsigned int *mapped_at;
+
+ Attrblobpage *file_pages;
+
+ /* mapped[i] is -1 if nothing is mapped at logical page I,
+ otherwise it contains the page number (of the mapped page). */
+ unsigned int *mapped;
+ unsigned int nmapped;
+ unsigned int rr_counter;
+} Repopagestore;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void repopagestore_init(Repopagestore *store);
+void repopagestore_free(Repopagestore *store);
+
+/* load pages pstart..pend into consecutive memory, return address */
+unsigned char *repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigned int pend);
+
+/* compress a page, return compressed len */
+unsigned int repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max);
+
+/* setup page data for repodata_load_page_range */
+int repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int pagesz, unsigned int blobsz);
+
+void repopagestore_disable_paging(Repopagestore *store);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_REPOPAGE_H */
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * rules.c
+ *
+ * SAT based dependency solver
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "solver.h"
+#include "solver_private.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "poolarch.h"
+#include "util.h"
+#include "evr.h"
+#include "policy.h"
+#include "solverdebug.h"
+#include "linkedpkg.h"
+#include "cplxdeps.h"
+
+#define RULES_BLOCK 63
+
+static void addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep);
+static void solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded);
+
+/*-------------------------------------------------------------------
+ * Check if dependency is possible
+ *
+ * mirrors solver_dep_fulfilled but uses map m instead of the decisionmap.
+ * used in solver_addpkgrulesforweak and solver_createcleandepsmap.
+ */
+
+static inline int
+dep_possible(Solver *solv, Id dep, Map *m)
+{
+ Pool *pool = solv->pool;
+ Id p, pp;
+
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags >= 8)
+ {
+ if (rd->flags == REL_COND)
+ return 1;
+ if (rd->flags == REL_AND)
+ {
+ if (!dep_possible(solv, rd->name, m))
+ return 0;
+ return dep_possible(solv, rd->evr, m);
+ }
+ if (rd->flags == REL_OR)
+ {
+ if (dep_possible(solv, rd->name, m))
+ return 1;
+ return dep_possible(solv, rd->evr, m);
+ }
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+ return solver_splitprovides(solv, rd->evr, m);
+ }
+ }
+ FOR_PROVIDES(p, pp, dep)
+ {
+ if (MAPTST(m, p))
+ return 1;
+ }
+ return 0;
+}
+
+static inline int
+is_otherproviders_dep(Pool *pool, Id dep)
+{
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
+ return 1;
+ }
+ return 0;
+}
+
+/********************************************************************
+ *
+ * Rule handling
+ *
+ * - unify rules, remove duplicates
+ */
+
+/*-------------------------------------------------------------------
+ *
+ * compare rules for unification sort
+ *
+ */
+
+static int
+unifyrules_sortcmp(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = dp;
+ 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;
+ while (*bd)
+ if ((x = *ad++ - *bd++) != 0)
+ return x;
+ return *ad;
+}
+
+int
+solver_rulecmp(Solver *solv, Rule *r1, Rule *r2)
+{
+ return unifyrules_sortcmp(r1, r2, solv->pool);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * unify rules
+ * go over all rules and remove duplicates
+ */
+
+void
+solver_unifyrules(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ int i, j;
+ Rule *ir, *jr;
+
+ if (solv->nrules <= 2) /* nothing to unify */
+ return;
+
+ /* sort rules first */
+ solv_sort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp, solv->pool);
+
+ /* prune rules
+ * i = unpruned
+ * j = pruned
+ */
+ jr = 0;
+ for (i = j = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
+ {
+ if (jr && !unifyrules_sortcmp(ir, jr, pool))
+ continue; /* prune! */
+ jr = solv->rules + j++; /* keep! */
+ if (ir != jr)
+ *jr = *ir;
+ }
+
+ /* reduced count from nrules to j rules */
+ POOL_DEBUG(SOLV_DEBUG_STATS, "pruned rules from %d to %d\n", solv->nrules, j);
+
+ /* adapt rule buffer */
+ solv->nrules = j;
+ solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
+
+ /*
+ * debug: log rule statistics
+ */
+ IF_POOLDEBUG (SOLV_DEBUG_STATS)
+ {
+ int binr = 0;
+ int lits = 0;
+ Id *dp;
+ Rule *r;
+
+ for (i = 1; i < solv->nrules; i++)
+ {
+ r = solv->rules + i;
+ if (r->d == 0)
+ binr++;
+ else
+ {
+ dp = solv->pool->whatprovidesdata + r->d;
+ while (*dp++)
+ lits++;
+ }
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, " binary: %d\n", binr);
+ POOL_DEBUG(SOLV_DEBUG_STATS, " normal: %d, %d literals\n", solv->nrules - 1 - binr, lits);
+ }
+}
+
+#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
+ *
+ * A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3)
+ *
+ * p < 0 : pkg id of A
+ * d > 0 : Offset in whatprovidesdata (list of providers of b)
+ *
+ * A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3)
+ * p < 0 : pkg id of A
+ * p2 < 0 : Id of solvable (e.g. B1)
+ *
+ * d == 0, p2 == 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 (also: uninstallable)
+ * Requires: p < 0, d > 0 (-A|B1|B2|...) d: <list of providers for requirement of p>
+ * Updates: p > 0, d > 0 (A|B1|B2|...) d: <list of updates for solvable p>
+ * Conflicts: p < 0, p2 < 0 (-A|-B) either p (conflict issuer) or d (conflict provider) (binary rule)
+ * also used for obsoletes
+ * No-op ?: p = 0, d = 0 (null) (used as placeholder in update/feature rules)
+ *
+ * resulting watches:
+ * ------------------
+ * Direct assertion (no watch needed) --> d = 0, w1 = p, w2 = 0
+ * Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p
+ * every other : w1 = p, w2 = whatprovidesdata[d];
+ *
+ * always returns a rule for non-pkg rules
+ */
+
+Rule *
+solver_addrule(Solver *solv, Id p, Id p2, Id d)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+
+ if (d)
+ {
+ assert(!p2 && d > 0);
+ if (!pool->whatprovidesdata[d])
+ d = 0;
+ else if (!pool->whatprovidesdata[d + 1])
+ {
+ p2 = pool->whatprovidesdata[d];
+ d = 0;
+ }
+ }
+
+ /* now we have two cases:
+ * 1 or 2 literals: d = 0, p, p2 contain the literals
+ * 3 or more literals: d > 0, p2 == 0, d is offset into whatprovidesdata
+ */
+
+ /* it often happenes that requires lead to adding the same pkg rule
+ * multiple times, so we prune those duplicates right away to make
+ * the work for unifyrules a bit easier */
+ if (!solv->pkgrules_end) /* we add pkg rules */
+ {
+ r = solv->rules + solv->nrules - 1;
+ if (d)
+ {
+ Id *dp;
+ /* check if rule is identical */
+ if (r->p == p)
+ {
+ Id *dp2;
+ if (r->d == d)
+ return r;
+ dp2 = pool->whatprovidesdata + r->d;
+ for (dp = pool->whatprovidesdata + d; *dp; dp++, dp2++)
+ if (*dp != *dp2)
+ break;
+ if (*dp == *dp2)
+ return r;
+ }
+ /* check if rule is self-fulfilling */
+ for (dp = pool->whatprovidesdata + d; *dp; dp++)
+ if (*dp == -p)
+ return 0; /* rule is self-fulfilling */
+ }
+ else
+ {
+ if (p2 && p > p2)
+ {
+ Id o = p; /* switch p1 and p2 */
+ p = p2;
+ p2 = o;
+ }
+ if (r->p == p && !r->d && r->w2 == p2)
+ return r;
+ if (p == -p2)
+ return 0; /* rule is self-fulfilling */
+ }
+ }
+
+ solv->rules = solv_extend(solv->rules, solv->nrules, 1, sizeof(Rule), RULES_BLOCK);
+ r = solv->rules + solv->nrules++; /* point to rule space */
+ r->p = p;
+ r->d = d;
+ r->w1 = p;
+ r->w2 = d ? pool->whatprovidesdata[d] : p2;
+ r->n1 = 0;
+ r->n2 = 0;
+ IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, " Add rule: ");
+ solver_printrule(solv, SOLV_DEBUG_RULE_CREATION, r);
+ }
+ return r;
+}
+
+
+void
+solver_shrinkrules(Solver *solv, int nrules)
+{
+ solv->nrules = nrules;
+ solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
+}
+
+/******************************************************************************
+ ***
+ *** pkg rule part: create rules representing the package dependencies
+ ***
+ ***/
+
+/*
+ * special multiversion patch conflict handling:
+ * a patch conflict is also satisfied if some other
+ * version with the same name/arch that doesn't conflict
+ * gets installed. The generated rule is thus:
+ * -patch|-cpack|opack1|opack2|...
+ */
+static Id
+makemultiversionconflict(Solver *solv, Id n, Id con)
+{
+ Pool *pool = solv->pool;
+ Solvable *s, *sn;
+ Queue q;
+ Id p, pp, qbuf[64];
+
+ sn = pool->solvables + n;
+ queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
+ queue_push(&q, -n);
+ FOR_PROVIDES(p, pp, sn->name)
+ {
+ s = pool->solvables + p;
+ if (s->name != sn->name || s->arch != sn->arch)
+ continue;
+ if (!MAPTST(&solv->multiversion, p))
+ continue;
+ if (pool_match_nevr(pool, pool->solvables + p, con))
+ continue;
+ /* here we have a multiversion solvable that doesn't conflict */
+ /* thus we're not in conflict if it is installed */
+ queue_push(&q, p);
+ }
+ if (q.count == 1)
+ n = 0; /* no other package found, normal conflict handling */
+ else
+ n = pool_queuetowhatprovides(pool, &q);
+ queue_free(&q);
+ return n;
+}
+
+static inline void
+addpkgrule(Solver *solv, Id p, Id p2, Id d, int type, Id dep)
+{
+ if (!solv->ruleinfoq)
+ solver_addrule(solv, p, p2, d);
+ else
+ addpkgruleinfo(solv, p, p2, d, type, dep);
+}
+
+#ifdef ENABLE_LINKED_PKGS
+
+static void
+addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m, Queue *workq)
+{
+ Pool *pool = solv->pool;
+ int i;
+ if (!qr->count)
+ return;
+#if 0
+ printf("ADDLINKS %s\n -> %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
+ for (i = 0; i < qr->count; i++)
+ printf(" - %s\n", pool_solvid2str(pool, qr->elements[i]));
+ printf(" <- %s\n", pool_dep2str(pool, prv));
+ for (i = 0; i < qp->count; i++)
+ printf(" - %s\n", pool_solvid2str(pool, qp->elements[i]));
+#endif
+
+ if (qr->count == 1)
+ addpkgrule(solv, -(s - pool->solvables), qr->elements[0], 0, SOLVER_RULE_PKG_REQUIRES, req);
+ else
+ addpkgrule(solv, -(s - pool->solvables), 0, pool_queuetowhatprovides(pool, qr), SOLVER_RULE_PKG_REQUIRES, req);
+ if (qp->count > 1)
+ {
+ Id d = pool_queuetowhatprovides(pool, qp);
+ for (i = 0; i < qr->count; i++)
+ addpkgrule(solv, -qr->elements[i], 0, d, SOLVER_RULE_PKG_REQUIRES, prv);
+ }
+ else if (qp->count)
+ {
+ for (i = 0; i < qr->count; i++)
+ addpkgrule(solv, -qr->elements[i], qp->elements[0], 0, SOLVER_RULE_PKG_REQUIRES, prv);
+ }
+ if (!m)
+ return; /* nothing more to do if called from getpkgruleinfos() */
+ for (i = 0; i < qr->count; i++)
+ if (!MAPTST(m, qr->elements[i]))
+ queue_push(workq, qr->elements[i]);
+ for (i = 0; i < qp->count; i++)
+ if (!MAPTST(m, qp->elements[i]))
+ queue_push(workq, qp->elements[i]);
+ if (solv->installed && s->repo == solv->installed)
+ {
+ Repo *installed = solv->installed;
+ /* record installed buddies */
+ if (!solv->instbuddy)
+ solv->instbuddy = solv_calloc(installed->end - installed->start, sizeof(Id));
+ if (qr->count == 1)
+ solv->instbuddy[s - pool->solvables - installed->start] = qr->elements[0];
+ for (i = 0; i < qr->count; i++)
+ {
+ Id p = qr->elements[i];
+ if (pool->solvables[p].repo != installed)
+ continue; /* huh? */
+ if (qp->count > 1 || (solv->instbuddy[p - installed->start] != 0 && solv->instbuddy[p - installed->start] != s - pool->solvables))
+ solv->instbuddy[p - installed->start] = 1; /* 1: ambiguous buddy */
+ else
+ solv->instbuddy[p - installed->start] = s - pool->solvables;
+ }
+ }
+}
+
+static void
+add_package_link(Solver *solv, Solvable *s, Map *m, Queue *workq)
+{
+ Queue qr, qp;
+ Id req = 0, prv = 0;
+ queue_init(&qr);
+ queue_init(&qp);
+ find_package_link(solv->pool, s, &req, &qr, &prv, &qp);
+ if (qr.count)
+ addlinks(solv, s, req, &qr, prv, &qp, m, workq);
+ queue_free(&qr);
+ queue_free(&qp);
+}
+
+#endif
+
+#ifdef ENABLE_COMPLEX_DEPS
+
+static void
+add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *workq, Map *m)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ int i, j, flags;
+ Queue bq;
+
+ queue_init(&bq);
+ flags = dontfix ? CPLXDEPS_DONTFIX : 0;
+ /* CNF expansion for requires, DNF + INVERT expansion for conflicts */
+ if (type == SOLVER_RULE_PKG_CONFLICTS)
+ flags |= CPLXDEPS_TODNF | CPLXDEPS_EXPAND | CPLXDEPS_INVERT;
+
+ i = pool_normalize_complex_dep(pool, dep, &bq, flags);
+ /* handle special cases */
+ if (i == 0)
+ {
+ if (dontfix)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken dependency %s of installed package %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, p));
+ }
+ else
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, p), p, pool_dep2str(pool, dep));
+ addpkgrule(solv, -p, 0, 0, type == SOLVER_RULE_PKG_REQUIRES ? SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP : type, dep);
+ }
+ queue_free(&bq);
+ return;
+ }
+ if (i == 1)
+ {
+ queue_free(&bq);
+ return;
+ }
+
+ /* go through all blocks and add a rule for each block */
+ for (i = 0; i < bq.count; i++)
+ {
+ if (!bq.elements[i])
+ continue; /* huh? */
+ if (bq.elements[i] == pool->nsolvables)
+ {
+ /* conventional requires (cannot be a conflicts as they have been expanded) */
+ Id *dp = pool->whatprovidesdata + bq.elements[i + 1];
+ i += 2;
+ if (dontfix)
+ {
+ for (j = 0; dp[j] != 0; j++)
+ if (pool->solvables[dp[j]].repo == installed)
+ break; /* provider was installed */
+ if (!dp[j])
+ continue;
+ }
+ /* check if the rule contains both p and -p */
+ for (j = 0; dp[j] != 0; j++)
+ if (dp[j] == p)
+ break;
+ if (dp[j])
+ continue;
+ addpkgrule(solv, -p, 0, dp - pool->whatprovidesdata, SOLVER_RULE_PKG_REQUIRES, dep);
+ /* push all non-visited providers on the work queue */
+ if (m)
+ for (; *dp; dp++)
+ if (!MAPTST(m, *dp))
+ queue_push(workq, *dp);
+ continue;
+ }
+ if (!bq.elements[i + 1])
+ {
+ Id p2 = bq.elements[i++];
+ /* simple rule with just two literals, we'll add a (-p, p2) rule */
+ if (dontfix)
+ {
+ if (p2 < 0 && pool->solvables[-p2].repo == installed)
+ continue;
+ if (p2 > 0 && pool->solvables[p2].repo != installed)
+ continue;
+ }
+ if (-p == p2)
+ {
+ if (type == SOLVER_RULE_PKG_CONFLICTS)
+ {
+ if (pool->forbidselfconflicts && !is_otherproviders_dep(pool, dep))
+ addpkgrule(solv, -p, 0, 0, SOLVER_RULE_PKG_SELF_CONFLICT, dep);
+ continue;
+ }
+ addpkgrule(solv, -p, 0, 0, type, dep);
+ continue;
+ }
+ /* check if the rule contains both p and -p */
+ if (p == p2)
+ continue;
+ addpkgrule(solv, -p, p2, 0, type, dep);
+ if (m && p2 > 0 && !MAPTST(m, p2))
+ queue_push(workq, p2);
+ }
+ else
+ {
+ Id *qele;
+ int qcnt;
+
+ qele = bq.elements + i;
+ qcnt = i;
+ while (bq.elements[i])
+ i++;
+ qcnt = i - qcnt;
+ if (dontfix)
+ {
+ for (j = 0; j < qcnt; j++)
+ {
+ if (qele[j] > 0 && pool->solvables[qele[j]].repo == installed)
+ break;
+ if (qele[j] < 0 && pool->solvables[-qele[j]].repo != installed)
+ break;
+ }
+ if (j == qcnt)
+ continue;
+ }
+ /* add -p to (ordered) rule (overwriting the trailing zero) */
+ for (j = 0; ; j++)
+ {
+ if (j == qcnt || qele[j] > -p)
+ {
+ if (j < qcnt)
+ memmove(qele + j + 1, qele + j, (qcnt - j) * sizeof(Id));
+ qele[j] = -p;
+ qcnt++;
+ break;
+ }
+ if (qele[j] == -p)
+ break;
+ }
+ /* check if the rule contains both p and -p */
+ for (j = 0; j < qcnt; j++)
+ if (qele[j] == p)
+ break;
+ if (j < qcnt)
+ continue;
+ addpkgrule(solv, qele[0], 0, pool_ids2whatprovides(pool, qele + 1, qcnt - 1), type, dep);
+ if (m)
+ for (j = 0; j < qcnt; j++)
+ if (qele[j] > 0 && !MAPTST(m, qele[j]))
+ queue_push(workq, qele[j]);
+ }
+ }
+ queue_free(&bq);
+}
+
+#endif
+
+/*-------------------------------------------------------------------
+ *
+ * add (install) rules for solvable
+ *
+ * s: Solvable for which to add rules
+ * m: m[s] = 1 for solvables which have rules, prevent rule duplication
+ *
+ * Algorithm: 'visit all nodes of a graph'. The graph nodes are
+ * solvables, the edges their dependencies.
+ * Starting from an installed solvable, this will create all rules
+ * representing the graph created by the solvables dependencies.
+ *
+ * for unfulfilled requirements, conflicts, obsoletes,....
+ * add a negative assertion for solvables that are not installable
+ *
+ * It will also create rules for all solvables referenced by 's'
+ * i.e. descend to all providers of requirements of 's'
+ *
+ */
+
+void
+solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+
+ Queue workq; /* list of solvables we still have to work on */
+ Id workqbuf[64];
+
+ int i;
+ int dontfix; /* ignore dependency errors for installed solvables */
+ Id req, *reqp;
+ Id con, *conp;
+ Id obs, *obsp;
+ Id rec, *recp;
+ Id sug, *sugp;
+ Id p, pp; /* whatprovides loops */
+ Id *dp; /* ptr to 'whatprovides' */
+ Id n; /* Id for current solvable 's' */
+
+ queue_init_buffer(&workq, workqbuf, sizeof(workqbuf)/sizeof(*workqbuf));
+ queue_push(&workq, s - pool->solvables); /* push solvable Id to work queue */
+
+ /* loop until there's no more work left */
+ while (workq.count)
+ {
+ /*
+ * n: Id of solvable
+ * s: Pointer to solvable
+ */
+
+ n = queue_shift(&workq); /* 'pop' next solvable to work on from queue */
+ if (m)
+ {
+ if (MAPTST(m, n)) /* continue if already visited */
+ continue;
+ MAPSET(m, n); /* mark as visited */
+ }
+
+ s = pool->solvables + n;
+
+ dontfix = 0;
+ if (installed /* Installed system available */
+ && s->repo == installed /* solvable is installed */
+ && !solv->fixmap_all /* NOT repair errors in dependency graph */
+ && !(solv->fixmap.size && MAPTST(&solv->fixmap, n - installed->start)))
+ {
+ dontfix = 1; /* dont care about broken deps */
+ }
+
+ if (!dontfix)
+ {
+ if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC
+ ? pool_disabled_solvable(pool, s)
+ : !pool_installable(pool, s))
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable\n", pool_solvid2str(pool, n), n);
+ addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_NOT_INSTALLABLE, 0);
+ }
+ }
+
+#ifdef ENABLE_LINKED_PKGS
+ /* add pseudo-package <-> real-package links */
+ if (has_package_link(pool, s))
+ add_package_link(solv, s, m, &workq);
+#endif
+
+ /*-----------------------------------------
+ * check requires of s
+ */
+
+ if (s->requires)
+ {
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0) /* go through all requires */
+ {
+ if (req == SOLVABLE_PREREQMARKER) /* skip the marker */
+ continue;
+
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, req))
+ {
+ /* we have AND/COND deps, normalize */
+ add_complex_deprules(solv, n, req, SOLVER_RULE_PKG_REQUIRES, dontfix, &workq, m);
+ continue;
+ }
+#endif
+
+ /* find list of solvables providing 'req' */
+ dp = pool_whatprovides_ptr(pool, req);
+
+ if (*dp == SYSTEMSOLVABLE) /* always installed */
+ continue;
+
+ if (dontfix)
+ {
+ /* the strategy here is to not insist on dependencies
+ * that are already broken. so if we find one provider
+ * that was already installed, we know that the
+ * dependency was not broken before so we enforce it */
+ for (i = 0; (p = dp[i]) != 0; i++)
+ if (pool->solvables[p].repo == installed)
+ break; /* found installed provider */
+ if (!p)
+ {
+ /* didn't find an installed provider: previously broken dependency */
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken requires %s of installed package %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s));
+ continue;
+ }
+ }
+
+ if (!*dp)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, n), n, pool_dep2str(pool, req));
+ addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP, req);
+ continue;
+ }
+
+ for (i = 0; dp[i] != 0; i++)
+ if (n == dp[i])
+ break;
+ if (dp[i])
+ continue; /* provided by itself, no need to add rule */
+
+ IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION," %s requires %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
+ for (i = 0; dp[i]; i++)
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, " provided by %s\n", pool_solvid2str(pool, dp[i]));
+ }
+
+ /* add 'requires' dependency */
+ /* rule: (-requestor|provider1|provider2|...|providerN) */
+ addpkgrule(solv, -n, 0, dp - pool->whatprovidesdata, SOLVER_RULE_PKG_REQUIRES, req);
+
+ /* push all non-visited providers on the work queue */
+ if (m)
+ for (; *dp; dp++)
+ if (!MAPTST(m, *dp))
+ queue_push(&workq, *dp);
+ }
+ }
+
+ /* that's all we check for src packages */
+ if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+ continue;
+
+ /*-----------------------------------------
+ * check conflicts of s
+ */
+
+ if (s->conflicts)
+ {
+ int ispatch = 0;
+
+ /* we treat conflicts in patches a bit differen:
+ * - nevr matching
+ * - multiversion handling
+ * XXX: we should really handle this different, looking
+ * at the name is a bad hack
+ */
+ if (!strncmp("patch:", pool_id2str(pool, s->name), 6))
+ ispatch = 1;
+ conp = s->repo->idarraydata + s->conflicts;
+ /* foreach conflicts of 's' */
+ while ((con = *conp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (!ispatch && pool_is_complex_dep(pool, con))
+ {
+ /* we have AND/COND deps, normalize */
+ add_complex_deprules(solv, n, con, SOLVER_RULE_PKG_CONFLICTS, dontfix, &workq, m);
+ continue;
+ }
+#endif
+ /* foreach providers of a conflict of 's' */
+ FOR_PROVIDES(p, pp, con)
+ {
+ if (ispatch && !pool_match_nevr(pool, pool->solvables + p, con))
+ continue;
+ /* dontfix: dont care about conflicts with already installed packs */
+ if (dontfix && pool->solvables[p].repo == installed)
+ continue;
+ if (p == n) /* p == n: self conflict */
+ {
+ if (!pool->forbidselfconflicts || is_otherproviders_dep(pool, con))
+ continue;
+ addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_SELF_CONFLICT, con);
+ continue;
+ }
+ if (ispatch && solv->multiversion.size && MAPTST(&solv->multiversion, p) && ISRELDEP(con))
+ {
+ /* our patch conflicts with a multiversion package */
+ Id d = makemultiversionconflict(solv, p, con);
+ if (d)
+ {
+ addpkgrule(solv, -n, 0, d, SOLVER_RULE_PKG_CONFLICTS, con);
+ continue;
+ }
+ }
+ if (p == SYSTEMSOLVABLE)
+ p = 0;
+ /* rule: -n|-p: either solvable _or_ provider of conflict */
+ addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_CONFLICTS, con);
+ }
+ }
+ }
+
+ /*-----------------------------------------
+ * check obsoletes and implicit obsoletes of a package
+ * if ignoreinstalledsobsoletes is not set, we're also checking
+ * obsoletes of installed packages (like newer rpm versions)
+ */
+ if ((!installed || s->repo != installed) || !pool->noinstalledobsoletes)
+ {
+ int multi = solv->multiversion.size && MAPTST(&solv->multiversion, n);
+ int isinstalled = (installed && s->repo == installed);
+ if (s->obsoletes && (!multi || solv->keepexplicitobsoletes))
+ {
+ obsp = s->repo->idarraydata + s->obsoletes;
+ /* foreach obsoletes */
+ while ((obs = *obsp++) != 0)
+ {
+ /* foreach provider of an obsoletes of 's' */
+ FOR_PROVIDES(p, pp, obs)
+ {
+ Solvable *ps = pool->solvables + p;
+ if (p == n)
+ continue;
+ if (isinstalled && dontfix && ps->repo == installed)
+ continue; /* don't repair installed/installed problems */
+ if (!pool->obsoleteusesprovides /* obsoletes are matched names, not provides */
+ && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ if (p == SYSTEMSOLVABLE)
+ p = 0;
+ if (!isinstalled)
+ addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_OBSOLETES, obs);
+ else
+ addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_INSTALLED_OBSOLETES, obs);
+ }
+ }
+ }
+ /* check implicit obsoletes
+ * for installed packages we only need to check installed/installed problems (and
+ * only when dontfix is not set), as the others are picked up when looking at the
+ * uninstalled package.
+ */
+ if (!isinstalled || !dontfix)
+ {
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ Solvable *ps = pool->solvables + p;
+ if (p == n)
+ continue;
+ if (isinstalled && ps->repo != installed)
+ continue;
+ /* we still obsolete packages with same nevra, like rpm does */
+ /* (actually, rpm mixes those packages. yuck...) */
+ if (multi && (s->name != ps->name || s->evr != ps->evr || s->arch != ps->arch))
+ {
+ if (isinstalled || ps->repo != installed)
+ continue;
+ /* also check the installed package for multi-ness */
+ if (MAPTST(&solv->multiversion, p))
+ continue;
+ }
+ if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
+ continue;
+ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ if (p == SYSTEMSOLVABLE)
+ p = 0;
+ if (s->name == ps->name)
+ {
+ /* optimization: do not add the same-name conflict rule if it was
+ * already added when we looked at the other package.
+ * (this assumes pool_colormatch is symmetric) */
+ if (p && m && ps->repo != installed && MAPTST(m, p) &&
+ (ps->arch != ARCH_SRC && ps->arch != ARCH_NOSRC) &&
+ !(solv->multiversion.size && MAPTST(&solv->multiversion, p)))
+ continue;
+ addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_SAME_NAME, 0);
+ }
+ else
+ addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_IMPLICIT_OBSOLETES, s->name);
+ }
+ }
+ }
+
+ if (m && pool->implicitobsoleteusescolors && (s->arch > pool->lastarch || pool->id2arch[s->arch] != 1))
+ {
+ int a = pool->id2arch[s->arch];
+ /* check lock-step candidates */
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ Solvable *ps = pool->solvables + p;
+ if (s->name != ps->name || s->evr != ps->evr || MAPTST(m, p))
+ continue;
+ if (ps->arch > pool->lastarch || pool->id2arch[ps->arch] == 1 || pool->id2arch[ps->arch] >= a)
+ continue;
+ queue_push(&workq, p);
+ }
+ }
+
+ /*-----------------------------------------
+ * add recommends/suggests to the work queue
+ */
+ if (s->recommends && m)
+ {
+ recp = s->repo->idarraydata + s->recommends;
+ while ((rec = *recp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, rec))
+ {
+ pool_add_pos_literals_complex_dep(pool, rec, &workq, m, 0);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, rec)
+ if (!MAPTST(m, p))
+ queue_push(&workq, p);
+ }
+ }
+ if (s->suggests && m)
+ {
+ sugp = s->repo->idarraydata + s->suggests;
+ while ((sug = *sugp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, sug))
+ {
+ pool_add_pos_literals_complex_dep(pool, sug, &workq, m, 0);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, sug)
+ if (!MAPTST(m, p))
+ queue_push(&workq, p);
+ }
+ }
+ }
+ queue_free(&workq);
+}
+
+#ifdef ENABLE_LINKED_PKGS
+void
+solver_addpkgrulesforlinked(Solver *solv, Map *m)
+{
+ Pool *pool = solv->pool;
+ Solvable *s;
+ int i, j;
+ Queue qr;
+
+ queue_init(&qr);
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ if (MAPTST(m, i))
+ continue;
+ s = pool->solvables + i;
+ if (!s->repo || s->repo == solv->installed)
+ continue;
+ if (!strchr(pool_id2str(pool, s->name), ':'))
+ continue;
+ if (!pool_installable(pool, s))
+ continue;
+ find_package_link(pool, s, 0, &qr, 0, 0);
+ if (qr.count)
+ {
+ for (j = 0; j < qr.count; j++)
+ if (MAPTST(m, qr.elements[j]))
+ {
+ solver_addpkgrulesforsolvable(solv, s, m);
+ break;
+ }
+ queue_empty(&qr);
+ }
+ }
+ queue_free(&qr);
+}
+#endif
+
+/*-------------------------------------------------------------------
+ *
+ * Add rules for packages possibly selected in by weak dependencies
+ *
+ * m: already added solvables
+ */
+
+void
+solver_addpkgrulesforweak(Solver *solv, Map *m)
+{
+ Pool *pool = solv->pool;
+ Solvable *s;
+ Id sup, *supp;
+ int i, n;
+
+ /* foreach solvable in pool */
+ for (i = n = 1; n < pool->nsolvables; i++, n++)
+ {
+ if (i == pool->nsolvables) /* wrap i */
+ i = 1;
+ if (MAPTST(m, i)) /* already added that one */
+ continue;
+
+ s = pool->solvables + i;
+ if (!s->repo)
+ continue;
+ if (s->repo != pool->installed && !pool_installable(pool, s))
+ continue; /* only look at installable ones */
+
+ sup = 0;
+ if (s->supplements)
+ {
+ /* find possible supplements */
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (dep_possible(solv, sup, m))
+ break;
+ }
+
+ /* if nothing found, check for enhances */
+ if (!sup && s->enhances)
+ {
+ supp = s->repo->idarraydata + s->enhances;
+ while ((sup = *supp++) != 0)
+ if (dep_possible(solv, sup, m))
+ break;
+ }
+ /* if nothing found, goto next solvables */
+ if (!sup)
+ continue;
+ solver_addpkgrulesforsolvable(solv, s, m);
+ n = 0; /* check all solvables again because we added solvables to m */
+ }
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * add package rules for possible updates
+ *
+ * s: solvable
+ * m: map of already visited solvables
+ * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
+ */
+
+void
+solver_addpkgrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
+{
+ Pool *pool = solv->pool;
+ int i;
+ /* queue and buffer for it */
+ Queue qs;
+ Id qsbuf[64];
+
+ queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
+ /* find update candidates for 's' */
+ policy_findupdatepackages(solv, s, &qs, allow_all);
+ /* add rule for 's' if not already done */
+ if (!MAPTST(m, s - pool->solvables))
+ solver_addpkgrulesforsolvable(solv, s, m);
+ /* foreach update candidate, add rule if not already done */
+ for (i = 0; i < qs.count; i++)
+ if (!MAPTST(m, qs.elements[i]))
+ solver_addpkgrulesforsolvable(solv, pool->solvables + qs.elements[i], m);
+ queue_free(&qs);
+}
+
+
+/***********************************************************************
+ ***
+ *** Update/Feature rule part
+ ***
+ *** Those rules make sure an installed package isn't silently deleted
+ ***
+ ***/
+
+static Id
+finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
+{
+ Pool *pool = solv->pool;
+ int i;
+
+ policy_findupdatepackages(solv, s, qs, allow_all ? allow_all : 2);
+ if (!qs->count)
+ {
+ if (allow_all)
+ return 0; /* orphaned, don't create feature rule */
+ /* check if this is an orphaned package */
+ policy_findupdatepackages(solv, s, qs, 1);
+ if (!qs->count)
+ return 0; /* orphaned, don't create update rule */
+ qs->count = 0;
+ return -SYSTEMSOLVABLE; /* supported but not installable */
+ }
+ if (allow_all)
+ return s - pool->solvables;
+ /* check if it is ok to keep the installed package */
+ if (solv->dupmap.size && MAPTST(&solv->dupmap, s - pool->solvables))
+ return s - pool->solvables;
+ for (i = 0; i < qs->count; i++)
+ {
+ Solvable *ns = pool->solvables + qs->elements[i];
+ if (s->evr == ns->evr && solvable_identical(s, ns))
+ return s - pool->solvables;
+ }
+ /* nope, it must be some other package */
+ return -SYSTEMSOLVABLE;
+}
+
+#if 0
+/* add packages from the dup repositories to the update candidates
+ * this isn't needed for the global dup mode as all packages are
+ * from dup repos in that case */
+static void
+addduppackages(Solver *solv, Solvable *s, Queue *qs)
+{
+ Queue dupqs;
+ Id p, dupqsbuf[64];
+ int i;
+ int oldnoupdateprovide = solv->noupdateprovide;
+
+ queue_init_buffer(&dupqs, dupqsbuf, sizeof(dupqsbuf)/sizeof(*dupqsbuf));
+ solv->noupdateprovide = 1;
+ policy_findupdatepackages(solv, s, &dupqs, 2);
+ solv->noupdateprovide = oldnoupdateprovide;
+ for (i = 0; i < dupqs.count; i++)
+ {
+ p = dupqs.elements[i];
+ if (MAPTST(&solv->dupmap, p))
+ queue_pushunique(qs, p);
+ }
+ queue_free(&dupqs);
+}
+#endif
+
+/*-------------------------------------------------------------------
+ *
+ * add rule for update
+ * (A|A1|A2|A3...) An = update candidates for A
+ *
+ * s = (installed) solvable
+ */
+
+void
+solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
+{
+ /* installed packages get a special upgrade allowed rule */
+ Pool *pool = solv->pool;
+ Id p, d;
+ Queue qs;
+ Id qsbuf[64];
+ int isorphaned = 0;
+
+ queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
+ p = s - pool->solvables;
+ /* find update candidates for 's' */
+ if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+ p = finddistupgradepackages(solv, s, &qs, allow_all);
+ else
+ policy_findupdatepackages(solv, s, &qs, allow_all);
+
+#ifdef ENABLE_LINKED_PKGS
+ if (solv->instbuddy && solv->instbuddy[s - pool->solvables - solv->installed->start])
+ {
+ const char *name = pool_id2str(pool, s->name);
+ if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
+ {
+ /* a linked pseudo package. As it is linked, we do not need an update/feature rule */
+ /* nevertheless we set specialupdaters so we can update */
+ solver_addrule(solv, 0, 0, 0);
+ if (!allow_all && qs.count)
+ {
+ if (p != -SYSTEMSOLVABLE)
+ queue_unshift(&qs, p);
+ if (!solv->specialupdaters)
+ solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
+ solv->specialupdaters[s - pool->solvables - solv->installed->start] = pool_queuetowhatprovides(pool, &qs);
+ }
+ queue_free(&qs);
+ return;
+ }
+ }
+#endif
+
+ if (!allow_all && !p) /* !p implies qs.count == 0 */
+ {
+ queue_push(&solv->orphaned, s - pool->solvables); /* an orphaned package */
+ if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start))))
+ p = s - pool->solvables; /* keep this orphaned package installed */
+ queue_free(&qs);
+ solver_addrule(solv, p, 0, 0);
+ return;
+ }
+
+ if (!allow_all && qs.count && solv->multiversion.size)
+ {
+ int i, j;
+
+ for (i = 0; i < qs.count; i++)
+ if (MAPTST(&solv->multiversion, qs.elements[i]))
+ break;
+ if (i < qs.count)
+ {
+ /* filter out all multiversion packages as they don't update */
+ d = pool_queuetowhatprovides(pool, &qs); /* save qs away */
+ for (j = i; i < qs.count; i++)
+ {
+ if (MAPTST(&solv->multiversion, qs.elements[i]))
+ {
+ Solvable *ps = pool->solvables + qs.elements[i];
+ /* if keepexplicitobsoletes is set and the name is different,
+ * we assume that there is an obsoletes. XXX: not 100% correct */
+ if (solv->keepexplicitobsoletes && ps->name != s->name)
+ {
+ qs.elements[j++] = qs.elements[i];
+ continue;
+ }
+ /* it's ok if they have same nevra */
+ if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
+ continue;
+ }
+ qs.elements[j++] = qs.elements[i];
+ }
+ if (j < qs.count) /* filtered at least one package? */
+ {
+ if (j == 0 && p == -SYSTEMSOLVABLE)
+ {
+ /* this is a multiversion orphan */
+ queue_push(&solv->orphaned, s - pool->solvables);
+ if (!solv->specialupdaters)
+ solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
+ solv->specialupdaters[s - pool->solvables - solv->installed->start] = d;
+ if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start))))
+ {
+ /* we need to keep the orphan */
+ queue_free(&qs);
+ solver_addrule(solv, s - pool->solvables, 0, 0);
+ return;
+ }
+ /* we can drop it as long as we update */
+ isorphaned = 1;
+ j = qs.count; /* force the update */
+ }
+ qs.count = j;
+ }
+ else if (p != -SYSTEMSOLVABLE)
+ {
+ /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
+ queue_free(&qs);
+ solver_addrule(solv, s - pool->solvables, 0, d); /* allow update of s */
+ return;
+ }
+ }
+ }
+ if (!isorphaned && p == -SYSTEMSOLVABLE && solv->dupmap.size)
+ p = s - pool->solvables; /* let the dup rules sort it out */
+ if (qs.count && p == -SYSTEMSOLVABLE)
+ p = queue_shift(&qs);
+ if (qs.count > 1)
+ {
+ d = pool_queuetowhatprovides(pool, &qs);
+ queue_free(&qs);
+ solver_addrule(solv, p, 0, d); /* allow update of s */
+ }
+ else
+ {
+ d = qs.count ? qs.elements[0] : 0;
+ queue_free(&qs);
+ solver_addrule(solv, p, d, 0); /* allow update of s */
+ }
+}
+
+static inline void
+disableupdaterule(Solver *solv, Id p)
+{
+ Rule *r;
+
+ MAPSET(&solv->noupdate, p - solv->installed->start);
+ r = solv->rules + solv->updaterules + (p - solv->installed->start);
+ if (r->p && r->d >= 0)
+ solver_disablerule(solv, r);
+ r = solv->rules + solv->featurerules + (p - solv->installed->start);
+ if (r->p && r->d >= 0)
+ solver_disablerule(solv, r);
+ if (solv->bestrules_pkg)
+ {
+ int i, ni;
+ ni = solv->bestrules_end - solv->bestrules;
+ for (i = 0; i < ni; i++)
+ if (solv->bestrules_pkg[i] == p)
+ solver_disablerule(solv, solv->rules + solv->bestrules + i);
+ }
+}
+
+static inline void
+reenableupdaterule(Solver *solv, Id p)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+
+ MAPCLR(&solv->noupdate, p - solv->installed->start);
+ r = solv->rules + solv->updaterules + (p - solv->installed->start);
+ if (r->p)
+ {
+ if (r->d < 0)
+ {
+ solver_enablerule(solv, r);
+ IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
+ }
+ }
+ }
+ else
+ {
+ r = solv->rules + solv->featurerules + (p - solv->installed->start);
+ if (r->p && r->d < 0)
+ {
+ solver_enablerule(solv, r);
+ IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
+ }
+ }
+ }
+ if (solv->bestrules_pkg)
+ {
+ int i, ni;
+ ni = solv->bestrules_end - solv->bestrules;
+ for (i = 0; i < ni; i++)
+ if (solv->bestrules_pkg[i] == p)
+ solver_enablerule(solv, solv->rules + solv->bestrules + i);
+ }
+}
+
+
+/***********************************************************************
+ ***
+ *** Infarch rule part
+ ***
+ *** Infarch rules make sure the solver uses the best architecture of
+ *** a package if multiple archetectures are available
+ ***
+ ***/
+
+void
+solver_addinfarchrules(Solver *solv, Map *addedmap)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = pool->installed;
+ int first, i, j;
+ Id p, pp, a, aa, bestarch;
+ Solvable *s, *ps, *bests;
+ Queue badq, allowedarchs;
+ Queue lsq;
+
+ queue_init(&badq);
+ queue_init(&allowedarchs);
+ queue_init(&lsq);
+ solv->infarchrules = solv->nrules;
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
+ continue;
+ s = pool->solvables + i;
+ first = i;
+ bestarch = 0;
+ bests = 0;
+ queue_empty(&allowedarchs);
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ ps = pool->solvables + p;
+ if (ps->name != s->name || !MAPTST(addedmap, p))
+ continue;
+ if (p == i)
+ first = 0;
+ if (first)
+ break;
+ a = ps->arch;
+ a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+ if (a != 1 && installed && ps->repo == installed)
+ {
+ if (!solv->dupmap_all && !(solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+ queue_pushunique(&allowedarchs, ps->arch); /* also ok to keep this architecture */
+ continue; /* ignore installed solvables when calculating the best arch */
+ }
+ if (a && a != 1 && (!bestarch || a < bestarch))
+ {
+ bestarch = a;
+ bests = ps;
+ }
+ }
+ if (first)
+ continue;
+
+ /* speed up common case where installed package already has best arch */
+ if (allowedarchs.count == 1 && bests && allowedarchs.elements[0] == bests->arch)
+ allowedarchs.count--; /* installed arch is best */
+
+ if (allowedarchs.count && pool->implicitobsoleteusescolors && installed && bestarch)
+ {
+ /* need an extra pass for lockstep checking: we only allow to keep an inferior arch
+ * if the corresponding installed package is not lock-stepped */
+ queue_empty(&allowedarchs);
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ Id p2, pp2;
+ ps = pool->solvables + p;
+ if (ps->name != s->name || ps->repo != installed || !MAPTST(addedmap, p))
+ continue;
+ if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+ continue;
+ a = ps->arch;
+ a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+ if (!a)
+ {
+ queue_pushunique(&allowedarchs, ps->arch); /* strange arch, allow */
+ continue;
+ }
+ if (a == 1 || ((a ^ bestarch) & 0xffff0000) == 0)
+ continue;
+ /* have installed package with inferior arch, check if lock-stepped */
+ FOR_PROVIDES(p2, pp2, s->name)
+ {
+ Solvable *s2 = pool->solvables + p2;
+ Id a2;
+ if (p2 == p || s2->name != s->name || s2->evr != pool->solvables[p].evr || s2->arch == pool->solvables[p].arch)
+ continue;
+ a2 = s2->arch;
+ a2 = (a2 <= pool->lastarch) ? pool->id2arch[a2] : 0;
+ if (a2 && (a2 == 1 || ((a2 ^ bestarch) & 0xffff0000) == 0))
+ break;
+ }
+ if (!p2)
+ queue_pushunique(&allowedarchs, ps->arch);
+ }
+ }
+
+ /* find all bad packages */
+ queue_empty(&badq);
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ ps = pool->solvables + p;
+ if (ps->name != s->name || !MAPTST(addedmap, p))
+ continue;
+ a = ps->arch;
+ a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+ if (a != 1 && bestarch && ((a ^ bestarch) & 0xffff0000) != 0)
+ {
+ if (installed && ps->repo == installed)
+ {
+ if (pool->implicitobsoleteusescolors)
+ queue_push(&badq, p); /* special lock-step handling, see below */
+ continue; /* always ok to keep an installed package */
+ }
+ for (j = 0; j < allowedarchs.count; j++)
+ {
+ aa = allowedarchs.elements[j];
+ if (ps->arch == aa)
+ break;
+ aa = (aa <= pool->lastarch) ? pool->id2arch[aa] : 0;
+ if (aa && ((a ^ aa) & 0xffff0000) == 0)
+ break; /* compatible */
+ }
+ if (j == allowedarchs.count)
+ queue_push(&badq, p);
+ }
+ }
+
+ /* block all solvables in the badq! */
+ for (j = 0; j < badq.count; j++)
+ {
+ p = badq.elements[j];
+ /* lock-step */
+ if (pool->implicitobsoleteusescolors)
+ {
+ Id p2;
+ int haveinstalled = 0;
+ queue_empty(&lsq);
+ FOR_PROVIDES(p2, pp, s->name)
+ {
+ Solvable *s2 = pool->solvables + p2;
+ if (p2 == p || s2->name != s->name || s2->evr != pool->solvables[p].evr || s2->arch == pool->solvables[p].arch)
+ continue;
+ a = s2->arch;
+ a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+ if (a && (a == 1 || ((a ^ bestarch) & 0xffff000) == 0))
+ {
+ queue_push(&lsq, p2);
+ if (installed && s2->repo == installed)
+ haveinstalled = 1;
+ }
+ }
+ if (installed && pool->solvables[p].repo == installed && !haveinstalled)
+ continue; /* installed package not in lock-step */
+ }
+ if (lsq.count < 2)
+ solver_addrule(solv, -p, lsq.count ? lsq.elements[0] : 0, 0);
+ else
+ solver_addrule(solv, -p, 0, pool_queuetowhatprovides(pool, &lsq));
+ }
+ }
+ queue_free(&lsq);
+ queue_free(&badq);
+ queue_free(&allowedarchs);
+ solv->infarchrules_end = solv->nrules;
+}
+
+static inline void
+disableinfarchrule(Solver *solv, Id name)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+ int i;
+ for (i = solv->infarchrules, r = solv->rules + i; i < solv->infarchrules_end; i++, r++)
+ {
+ if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
+ solver_disablerule(solv, r);
+ }
+}
+
+static inline void
+reenableinfarchrule(Solver *solv, Id name)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+ int i;
+ for (i = solv->infarchrules, r = solv->rules + i; i < solv->infarchrules_end; i++, r++)
+ {
+ if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
+ {
+ solver_enablerule(solv, r);
+ IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+ ***
+ *** Dup rule part
+ ***
+ *** Dup rules make sure a package is selected from the specified dup
+ *** repositories if an update candidate is included in one of them.
+ ***
+ ***/
+
+static inline void
+add_cleandeps_package(Solver *solv, Id p)
+{
+ if (!solv->cleandeps_updatepkgs)
+ {
+ solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->cleandeps_updatepkgs);
+ }
+ queue_pushunique(solv->cleandeps_updatepkgs, p);
+}
+
+static void
+solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
+{
+ Pool *pool = solv->pool;
+ Solvable *ps, *s = pool->solvables + p;
+ Repo *installed = solv->installed;
+ Id pi, pip, obs, *obsp;
+
+ MAPSET(&solv->dupinvolvedmap, p);
+ if (targeted)
+ MAPSET(&solv->dupmap, p);
+ FOR_PROVIDES(pi, pip, s->name)
+ {
+ ps = pool->solvables + pi;
+ if (ps->name != s->name)
+ continue;
+ MAPSET(&solv->dupinvolvedmap, pi);
+ if (targeted && ps->repo == installed && solv->obsoletes && solv->obsoletes[pi - installed->start])
+ {
+ Id *opp, pi2;
+ for (opp = solv->obsoletes_data + solv->obsoletes[pi - installed->start]; (pi2 = *opp++) != 0;)
+ if (pool->solvables[pi2].repo != installed)
+ MAPSET(&solv->dupinvolvedmap, pi2);
+ }
+ if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0)
+ {
+ if (!solv->bestupdatemap.size)
+ map_grow(&solv->bestupdatemap, installed->end - installed->start);
+ MAPSET(&solv->bestupdatemap, pi - installed->start);
+ }
+ if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
+ add_cleandeps_package(solv, pi);
+ if (!targeted && ps->repo != installed)
+ MAPSET(&solv->dupmap, pi);
+ }
+ if (s->repo == installed && solv->obsoletes && solv->obsoletes[p - installed->start])
+ {
+ Id *opp;
+ for (opp = solv->obsoletes_data + solv->obsoletes[p - installed->start]; (pi = *opp++) != 0;)
+ {
+ ps = pool->solvables + pi;
+ if (ps->repo == installed)
+ continue;
+ MAPSET(&solv->dupinvolvedmap, pi);
+ if (!targeted)
+ MAPSET(&solv->dupmap, pi);
+ }
+ }
+ if (targeted && s->repo != installed && s->obsoletes)
+ {
+ /* XXX: check obsoletes/provides combination */
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(pi, pip, obs)
+ {
+ Solvable *ps = pool->solvables + pi;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ MAPSET(&solv->dupinvolvedmap, pi);
+ if (targeted && ps->repo == installed && solv->obsoletes && solv->obsoletes[pi - installed->start])
+ {
+ Id *opp, pi2;
+ for (opp = solv->obsoletes_data + solv->obsoletes[pi - installed->start]; (pi2 = *opp++) != 0;)
+ if (pool->solvables[pi2].repo != installed)
+ MAPSET(&solv->dupinvolvedmap, pi2);
+ }
+ if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0)
+ {
+ if (!solv->bestupdatemap.size)
+ map_grow(&solv->bestupdatemap, installed->end - installed->start);
+ MAPSET(&solv->bestupdatemap, pi - installed->start);
+ }
+ if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
+ add_cleandeps_package(solv, pi);
+ }
+ }
+ }
+}
+
+void
+solver_createdupmaps(Solver *solv)
+{
+ Queue *job = &solv->job;
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ Id select, how, what, p, pp;
+ Solvable *s;
+ int i, targeted;
+
+ map_init(&solv->dupmap, pool->nsolvables);
+ map_init(&solv->dupinvolvedmap, pool->nsolvables);
+ for (i = 0; i < job->count; i += 2)
+ {
+ how = job->elements[i];
+ select = job->elements[i] & SOLVER_SELECTMASK;
+ what = job->elements[i + 1];
+ switch (how & SOLVER_JOBMASK)
+ {
+ case SOLVER_DISTUPGRADE:
+ if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Repo *repo;
+ if (what <= 0 || what > pool->nrepos)
+ break;
+ repo = pool_id2repo(pool, what);
+ if (!repo)
+ break;
+ if (repo != installed && !(how & SOLVER_TARGETED) && solv->noautotarget)
+ break;
+ targeted = repo != installed || (how & SOLVER_TARGETED) != 0;
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ if (repo != installed && !pool_installable(pool, s))
+ continue;
+ solver_addtodupmaps(solv, p, how, targeted);
+ }
+ }
+ else if (select == SOLVER_SOLVABLE_ALL)
+ {
+ FOR_POOL_SOLVABLES(p)
+ {
+ MAPSET(&solv->dupinvolvedmap, p);
+ if (installed && pool->solvables[p].repo != installed)
+ MAPSET(&solv->dupmap, p);
+ }
+ }
+ else
+ {
+ targeted = how & SOLVER_TARGETED ? 1 : 0;
+ if (installed && !targeted && !solv->noautotarget)
+ {
+ FOR_JOB_SELECT(p, pp, select, what)
+ if (pool->solvables[p].repo == installed)
+ break;
+ targeted = p == 0;
+ }
+ else if (!installed && !solv->noautotarget)
+ targeted = 1;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ Solvable *s = pool->solvables + p;
+ if (!s->repo)
+ continue;
+ if (s->repo != installed && !targeted)
+ continue;
+ if (s->repo != installed && !pool_installable(pool, s))
+ continue;
+ solver_addtodupmaps(solv, p, how, targeted);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE);
+}
+
+void
+solver_freedupmaps(Solver *solv)
+{
+ map_free(&solv->dupmap);
+ /* we no longer free solv->dupinvolvedmap as we need it in
+ * policy's priority pruning code. sigh. */
+}
+
+void
+solver_addduprules(Solver *solv, Map *addedmap)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ Id p, pp;
+ Solvable *s, *ps;
+ int first, i;
+ Rule *r;
+
+ solv->duprules = solv->nrules;
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
+ continue;
+ s = pool->solvables + i;
+ first = i;
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ ps = pool->solvables + p;
+ if (ps->name != s->name || !MAPTST(addedmap, p))
+ continue;
+ if (p == i)
+ first = 0;
+ if (first)
+ break;
+ if (!MAPTST(&solv->dupinvolvedmap, p))
+ continue;
+ if (installed && ps->repo == installed)
+ {
+ if (!solv->updatemap.size)
+ map_grow(&solv->updatemap, installed->end - installed->start);
+ MAPSET(&solv->updatemap, p - installed->start);
+ if (!MAPTST(&solv->dupmap, p))
+ {
+ Id ip, ipp;
+ /* is installed identical to a good one? */
+ FOR_PROVIDES(ip, ipp, ps->name)
+ {
+ Solvable *is = pool->solvables + ip;
+ if (!MAPTST(&solv->dupmap, ip))
+ continue;
+ if (is->evr == ps->evr && solvable_identical(ps, is))
+ break;
+ }
+ if (ip)
+ {
+ /* ok, found a good one. we may keep this package. */
+ MAPSET(&solv->dupmap, p); /* for best rules processing */
+ continue;
+ }
+ r = solv->rules + solv->updaterules + (p - installed->start);
+ if (!r->p)
+ r = solv->rules + solv->featurerules + (p - installed->start);
+ if (r->p && solv->specialupdaters && solv->specialupdaters[p - installed->start])
+ {
+ /* this is a multiversion orphan, we're good if an update is installed */
+ solver_addrule(solv, -p, 0, solv->specialupdaters[p - installed->start]);
+ continue;
+ }
+ solver_addrule(solv, -p, 0, 0); /* no match, sorry */
+ }
+ }
+ else if (!MAPTST(&solv->dupmap, p))
+ solver_addrule(solv, -p, 0, 0);
+ }
+ }
+ solv->duprules_end = solv->nrules;
+}
+
+
+static inline void
+disableduprule(Solver *solv, Id name)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+ int i;
+ for (i = solv->duprules, r = solv->rules + i; i < solv->duprules_end; i++, r++)
+ {
+ if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
+ solver_disablerule(solv, r);
+ }
+}
+
+static inline void
+reenableduprule(Solver *solv, Id name)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+ int i;
+ for (i = solv->duprules, r = solv->rules + i; i < solv->duprules_end; i++, r++)
+ {
+ if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
+ {
+ solver_enablerule(solv, r);
+ IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+ ***
+ *** Policy rule disabling/reenabling
+ ***
+ *** Disable all policy rules that conflict with our jobs. If a job
+ *** gets disabled later on, reenable the involved policy rules again.
+ ***
+ ***/
+
+#define DISABLE_UPDATE 1
+#define DISABLE_INFARCH 2
+#define DISABLE_DUP 3
+
+/*
+ * add all installed packages that package p obsoletes to Queue q.
+ * Package p is not installed. Also, we know that if
+ * solv->keepexplicitobsoletes is not set, p is not in the multiversion map.
+ * Entries may get added multiple times.
+ */
+static void
+add_obsoletes(Solver *solv, Id p, Queue *q)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ Id p2, pp2;
+ Solvable *s = pool->solvables + p;
+ Id obs, *obsp;
+ Id lastp2 = 0;
+
+ if (!solv->keepexplicitobsoletes || !(solv->multiversion.size && MAPTST(&solv->multiversion, p)))
+ {
+ FOR_PROVIDES(p2, pp2, s->name)
+ {
+ Solvable *ps = pool->solvables + p2;
+ if (ps->repo != installed)
+ continue;
+ if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
+ continue;
+ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ queue_push(q, p2);
+ lastp2 = p2;
+ }
+ }
+ if (!s->obsoletes)
+ return;
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ FOR_PROVIDES(p2, pp2, obs)
+ {
+ Solvable *ps = pool->solvables + p2;
+ if (ps->repo != installed)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ if (p2 == lastp2)
+ continue;
+ queue_push(q, p2);
+ lastp2 = p2;
+ }
+}
+
+/*
+ * Call add_obsoletes and intersect the result with the
+ * elements in Queue q starting at qstart.
+ * Assumes that it's the first call if qstart == q->count.
+ * May use auxillary map m for the intersection process, all
+ * elements of q starting at qstart must have their bit cleared.
+ * (This is also true after the function returns.)
+ */
+static void
+intersect_obsoletes(Solver *solv, Id p, Queue *q, int qstart, Map *m)
+{
+ int i, j;
+ int qcount = q->count;
+
+ add_obsoletes(solv, p, q);
+ if (qcount == qstart)
+ return; /* first call */
+ if (qcount == q->count)
+ j = qstart;
+ else if (qcount == qstart + 1)
+ {
+ /* easy if there's just one element */
+ j = qstart;
+ for (i = qcount; i < q->count; i++)
+ if (q->elements[i] == q->elements[qstart])
+ {
+ j++; /* keep the element */
+ break;
+ }
+ }
+ else if (!m->size && q->count - qstart <= 8)
+ {
+ /* faster than a map most of the time */
+ int k;
+ for (i = j = qstart; i < qcount; i++)
+ {
+ Id ip = q->elements[i];
+ for (k = qcount; k < q->count; k++)
+ if (q->elements[k] == ip)
+ {
+ q->elements[j++] = ip;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* for the really pathologic cases we use the map */
+ Repo *installed = solv->installed;
+ if (!m->size)
+ map_init(m, installed->end - installed->start);
+ for (i = qcount; i < q->count; i++)
+ MAPSET(m, q->elements[i] - installed->start);
+ for (i = j = qstart; i < qcount; i++)
+ if (MAPTST(m, q->elements[i] - installed->start))
+ {
+ MAPCLR(m, q->elements[i] - installed->start);
+ q->elements[j++] = q->elements[i];
+ }
+ }
+ queue_truncate(q, j);
+}
+
+static void
+jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
+{
+ Pool *pool = solv->pool;
+ Id select, p, pp;
+ Repo *installed;
+ Solvable *s;
+ int i, j, set, qstart;
+ Map omap;
+
+ installed = solv->installed;
+ select = how & SOLVER_SELECTMASK;
+ switch (how & SOLVER_JOBMASK)
+ {
+ case SOLVER_INSTALL:
+ set = how & SOLVER_SETMASK;
+ if (!(set & SOLVER_NOAUTOSET))
+ {
+ /* automatically add set bits by analysing the job */
+ if (select == SOLVER_SOLVABLE_NAME)
+ set |= SOLVER_SETNAME;
+ if (select == SOLVER_SOLVABLE)
+ set |= SOLVER_SETNAME | SOLVER_SETARCH | SOLVER_SETVENDOR | SOLVER_SETREPO | SOLVER_SETEVR;
+ else if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(what))
+ {
+ Reldep *rd = GETRELDEP(pool, what);
+ if (rd->flags == REL_EQ && select == SOLVER_SOLVABLE_NAME)
+ {
+ if (pool->disttype != DISTTYPE_DEB)
+ {
+ const char *rel = strrchr(pool_id2str(pool, rd->evr), '-');
+ set |= rel ? SOLVER_SETEVR : SOLVER_SETEV;
+ }
+ else
+ set |= SOLVER_SETEVR;
+ }
+ if (rd->flags <= 7 && ISRELDEP(rd->name))
+ rd = GETRELDEP(pool, rd->name);
+ if (rd->flags == REL_ARCH)
+ set |= SOLVER_SETARCH;
+ }
+ }
+ else
+ set &= ~SOLVER_NOAUTOSET;
+ if (!set)
+ return;
+ if ((set & SOLVER_SETARCH) != 0 && solv->infarchrules != solv->infarchrules_end)
+ {
+ if (select == SOLVER_SOLVABLE)
+ queue_push2(q, DISABLE_INFARCH, pool->solvables[what].name);
+ else
+ {
+ int qcnt = q->count;
+ /* does not work for SOLVER_SOLVABLE_ALL and SOLVER_SOLVABLE_REPO, but
+ they are not useful for SOLVER_INSTALL jobs anyway */
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ /* unify names */
+ for (i = qcnt; i < q->count; i += 2)
+ if (q->elements[i + 1] == s->name)
+ break;
+ if (i < q->count)
+ continue;
+ queue_push2(q, DISABLE_INFARCH, s->name);
+ }
+ }
+ }
+ if ((set & SOLVER_SETREPO) != 0 && solv->duprules != solv->duprules_end)
+ {
+ if (select == SOLVER_SOLVABLE)
+ queue_push2(q, DISABLE_DUP, pool->solvables[what].name);
+ else
+ {
+ int qcnt = q->count;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ /* unify names */
+ for (i = qcnt; i < q->count; i += 2)
+ if (q->elements[i + 1] == s->name)
+ break;
+ if (i < q->count)
+ continue;
+ queue_push2(q, DISABLE_DUP, s->name);
+ }
+ }
+ }
+ if (!installed || installed->end == installed->start)
+ return;
+ /* now the hard part: disable some update rules */
+
+ /* first check if we have multiversion or installed packages in the job */
+ i = j = 0;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ if (pool->solvables[p].repo == installed)
+ j = p;
+ else if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes)
+ return;
+ i++;
+ }
+ if (j) /* have installed packages */
+ {
+ /* this is for dupmap_all jobs, it can go away if we create
+ * duprules for them */
+ if (i == 1 && (set & SOLVER_SETREPO) != 0)
+ queue_push2(q, DISABLE_UPDATE, j);
+ return;
+ }
+
+ omap.size = 0;
+ qstart = q->count;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ intersect_obsoletes(solv, p, q, qstart, &omap);
+ if (q->count == qstart)
+ break;
+ }
+ if (omap.size)
+ map_free(&omap);
+
+ if (qstart == q->count)
+ return; /* nothing to prune */
+
+ /* convert result to (DISABLE_UPDATE, p) pairs */
+ i = q->count;
+ for (j = qstart; j < i; j++)
+ queue_push(q, q->elements[j]);
+ for (j = qstart; j < q->count; j += 2)
+ {
+ q->elements[j] = DISABLE_UPDATE;
+ q->elements[j + 1] = q->elements[i++];
+ }
+
+ /* now that we know which installed packages are obsoleted check each of them */
+ if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
+ return; /* all is set, nothing to do */
+
+ for (i = j = qstart; i < q->count; i += 2)
+ {
+ Solvable *is = pool->solvables + q->elements[i + 1];
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ int illegal = 0;
+ s = pool->solvables + p;
+ if ((set & SOLVER_SETEVR) != 0)
+ illegal |= POLICY_ILLEGAL_DOWNGRADE; /* ignore */
+ if ((set & SOLVER_SETNAME) != 0)
+ illegal |= POLICY_ILLEGAL_NAMECHANGE; /* ignore */
+ if ((set & SOLVER_SETARCH) != 0)
+ illegal |= POLICY_ILLEGAL_ARCHCHANGE; /* ignore */
+ if ((set & SOLVER_SETVENDOR) != 0)
+ illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore */
+ illegal = policy_is_illegal(solv, is, s, illegal);
+ if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0)
+ {
+ /* it's ok if the EV is different */
+ if (pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0)
+ illegal = 0;
+ }
+ if (illegal)
+ break;
+ }
+ if (!p)
+ {
+ /* no package conflicts with the update rule */
+ /* thus keep the DISABLE_UPDATE */
+ q->elements[j + 1] = q->elements[i + 1];
+ j += 2;
+ }
+ }
+ queue_truncate(q, j);
+ return;
+
+ case SOLVER_ERASE:
+ if (!installed)
+ break;
+ if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+ FOR_REPO_SOLVABLES(installed, p, s)
+ queue_push2(q, DISABLE_UPDATE, p);
+ FOR_JOB_SELECT(p, pp, select, what)
+ if (pool->solvables[p].repo == installed)
+ {
+ queue_push2(q, DISABLE_UPDATE, p);
+#ifdef ENABLE_LINKED_PKGS
+ if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
+ queue_push2(q, DISABLE_UPDATE, solv->instbuddy[p - installed->start]);
+#endif
+ }
+ return;
+ default:
+ return;
+ }
+}
+
+/* disable all policy rules that are in conflict with our job list */
+void
+solver_disablepolicyrules(Solver *solv)
+{
+ Queue *job = &solv->job;
+ int i, j;
+ Queue allq;
+ Rule *r;
+ Id lastjob = -1;
+ Id allqbuf[128];
+
+ queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
+
+ for (i = solv->jobrules; i < solv->jobrules_end; i++)
+ {
+ r = solv->rules + i;
+ if (r->d < 0) /* disabled? */
+ continue;
+ j = solv->ruletojob.elements[i - solv->jobrules];
+ if (j == lastjob)
+ continue;
+ lastjob = j;
+ jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
+ }
+ if (solv->cleandepsmap.size)
+ {
+ solver_createcleandepsmap(solv, &solv->cleandepsmap, 0);
+ for (i = solv->installed->start; i < solv->installed->end; i++)
+ if (MAPTST(&solv->cleandepsmap, i - solv->installed->start))
+ queue_push2(&allq, DISABLE_UPDATE, i);
+ }
+ MAPZERO(&solv->noupdate);
+ for (i = 0; i < allq.count; i += 2)
+ {
+ Id type = allq.elements[i], arg = allq.elements[i + 1];
+ switch(type)
+ {
+ case DISABLE_UPDATE:
+ disableupdaterule(solv, arg);
+ break;
+ case DISABLE_INFARCH:
+ disableinfarchrule(solv, arg);
+ break;
+ case DISABLE_DUP:
+ disableduprule(solv, arg);
+ break;
+ default:
+ break;
+ }
+ }
+ queue_free(&allq);
+}
+
+/* we just disabled job #jobidx, now reenable all policy rules that were
+ * disabled because of this job */
+void
+solver_reenablepolicyrules(Solver *solv, int jobidx)
+{
+ Queue *job = &solv->job;
+ int i, j, k, ai;
+ Queue q, allq;
+ Rule *r;
+ Id lastjob = -1;
+ Id qbuf[32], allqbuf[32];
+
+ queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
+ jobtodisablelist(solv, job->elements[jobidx - 1], job->elements[jobidx], &q);
+ if (!q.count)
+ {
+ queue_free(&q);
+ return;
+ }
+ /* now remove everything from q that is disabled by other jobs */
+
+ /* first remove cleandeps packages, they count as DISABLE_UPDATE */
+ if (solv->cleandepsmap.size)
+ {
+ solver_createcleandepsmap(solv, &solv->cleandepsmap, 0);
+ for (j = k = 0; j < q.count; j += 2)
+ {
+ if (q.elements[j] == DISABLE_UPDATE)
+ {
+ Id p = q.elements[j + 1];
+ if (p >= solv->installed->start && p < solv->installed->end && MAPTST(&solv->cleandepsmap, p - solv->installed->start))
+ continue; /* remove element from q */
+ }
+ q.elements[k++] = q.elements[j];
+ q.elements[k++] = q.elements[j + 1];
+ }
+ q.count = k;
+ if (!q.count)
+ {
+ queue_free(&q);
+ return;
+ }
+ }
+
+ /* now go through the disable list of all other jobs */
+ queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
+ for (i = solv->jobrules; i < solv->jobrules_end; i++)
+ {
+ r = solv->rules + i;
+ if (r->d < 0) /* disabled? */
+ continue;
+ j = solv->ruletojob.elements[i - solv->jobrules];
+ if (j == lastjob)
+ continue;
+ lastjob = j;
+ jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
+ if (!allq.count)
+ continue;
+ /* remove all elements in allq from q */
+ for (j = k = 0; j < q.count; j += 2)
+ {
+ Id type = q.elements[j], arg = q.elements[j + 1];
+ for (ai = 0; ai < allq.count; ai += 2)
+ if (allq.elements[ai] == type && allq.elements[ai + 1] == arg)
+ break;
+ if (ai < allq.count)
+ continue; /* found it in allq, remove element from q */
+ q.elements[k++] = q.elements[j];
+ q.elements[k++] = q.elements[j + 1];
+ }
+ q.count = k;
+ if (!q.count)
+ {
+ queue_free(&q);
+ queue_free(&allq);
+ return;
+ }
+ queue_empty(&allq);
+ }
+ queue_free(&allq);
+
+ /* now re-enable anything that's left in q */
+ for (j = 0; j < q.count; j += 2)
+ {
+ Id type = q.elements[j], arg = q.elements[j + 1];
+ switch(type)
+ {
+ case DISABLE_UPDATE:
+ reenableupdaterule(solv, arg);
+ break;
+ case DISABLE_INFARCH:
+ reenableinfarchrule(solv, arg);
+ break;
+ case DISABLE_DUP:
+ reenableduprule(solv, arg);
+ break;
+ }
+ }
+ queue_free(&q);
+}
+
+/* we just removed a package from the cleandeps map, now reenable all policy rules that were
+ * disabled because of this */
+void
+solver_reenablepolicyrules_cleandeps(Solver *solv, Id pkg)
+{
+ Queue *job = &solv->job;
+ int i, j;
+ Queue allq;
+ Rule *r;
+ Id lastjob = -1;
+ Id allqbuf[128];
+
+ queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
+ for (i = solv->jobrules; i < solv->jobrules_end; i++)
+ {
+ r = solv->rules + i;
+ if (r->d < 0) /* disabled? */
+ continue;
+ j = solv->ruletojob.elements[i - solv->jobrules];
+ if (j == lastjob)
+ continue;
+ lastjob = j;
+ jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
+ }
+ for (i = 0; i < allq.count; i += 2)
+ if (allq.elements[i] == DISABLE_UPDATE && allq.elements[i + 1] == pkg)
+ break;
+ if (i == allq.count)
+ reenableupdaterule(solv, pkg);
+ queue_free(&allq);
+}
+
+
+/***********************************************************************
+ ***
+ *** Rule info part, tell the user what the rule is about.
+ ***
+ ***/
+
+static void
+addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+
+ if (d)
+ {
+ assert(!p2 && d > 0);
+ if (!pool->whatprovidesdata[d])
+ d = 0;
+ else if (!pool->whatprovidesdata[d + 1])
+ {
+ p2 = pool->whatprovidesdata[d];
+ d = 0;
+ }
+ }
+
+ /* check if this creates the rule we're searching for */
+ r = solv->rules + solv->ruleinfoq->elements[0];
+ if (d)
+ {
+ /* three or more literals */
+ Id od = r->d < 0 ? -r->d - 1 : r->d;
+ if (p != r->p && !od)
+ return;
+ if (d != od)
+ {
+ Id *dp = pool->whatprovidesdata + d;
+ Id *odp = pool->whatprovidesdata + od;
+ while (*dp)
+ if (*dp++ != *odp++)
+ return;
+ if (*odp)
+ return;
+ }
+ if (p < 0 && pool->whatprovidesdata[d] < 0 && type == SOLVER_RULE_PKG_CONFLICTS)
+ p2 = pool->whatprovidesdata[d];
+ }
+ else
+ {
+ /* one or two literals */
+ Id op = p, op2 = p2;
+ if (op2 && op > op2) /* normalize */
+ {
+ Id o = op;
+ op = op2;
+ op2 = o;
+ }
+ if (r->p != op || r->w2 != op2 || (r->d && r->d != -1))
+ return;
+ if (type == SOLVER_RULE_PKG_CONFLICTS && !p2)
+ p2 = -SYSTEMSOLVABLE;
+ if (type == SOLVER_RULE_PKG_SAME_NAME)
+ {
+ p = op; /* we normalize same name order */
+ p2 = op2;
+ }
+ }
+ /* yep, rule matches. record info */
+ queue_push(solv->ruleinfoq, type);
+ queue_push(solv->ruleinfoq, p < 0 ? -p : 0);
+ queue_push(solv->ruleinfoq, p2 < 0 ? -p2 : 0);
+ queue_push(solv->ruleinfoq, dep);
+}
+
+static int
+solver_allruleinfos_cmp(const void *ap, const void *bp, void *dp)
+{
+ const Id *a = ap, *b = bp;
+ int r;
+
+ r = a[0] - b[0];
+ if (r)
+ return r;
+ r = a[1] - b[1];
+ if (r)
+ return r;
+ r = a[2] - b[2];
+ if (r)
+ return r;
+ r = a[3] - b[3];
+ if (r)
+ return r;
+ return 0;
+}
+
+static void
+getpkgruleinfos(Solver *solv, Rule *r, Queue *rq)
+{
+ Pool *pool = solv->pool;
+ Id l, pp;
+ if (r->p >= 0)
+ return;
+ queue_push(rq, r - solv->rules); /* push the rule we're interested in */
+ solv->ruleinfoq = rq;
+ FOR_RULELITERALS(l, pp, r)
+ {
+ if (l >= 0)
+ break;
+ solver_addpkgrulesforsolvable(solv, pool->solvables - l, 0);
+ }
+#ifdef ENABLE_LINKED_PKGS
+ FOR_RULELITERALS(l, pp, r)
+ {
+ if (l < 0)
+ {
+ if (l == r->p)
+ continue;
+ break;
+ }
+ if (!strchr(pool_id2str(pool, pool->solvables[l].name), ':') || !has_package_link(pool, pool->solvables + l))
+ break;
+ add_package_link(solv, pool->solvables + l, 0, 0);
+ }
+#endif
+ solv->ruleinfoq = 0;
+ queue_shift(rq);
+}
+
+int
+solver_allruleinfos(Solver *solv, Id rid, Queue *rq)
+{
+ Rule *r = solv->rules + rid;
+ int i, j;
+
+ queue_empty(rq);
+ if (rid <= 0 || rid >= solv->pkgrules_end)
+ {
+ Id type, from, to, dep;
+ type = solver_ruleinfo(solv, rid, &from, &to, &dep);
+ queue_push(rq, type);
+ queue_push(rq, from);
+ queue_push(rq, to);
+ queue_push(rq, dep);
+ return 1;
+ }
+ getpkgruleinfos(solv, r, rq);
+ /* now sort & unify em */
+ if (!rq->count)
+ return 0;
+ solv_sort(rq->elements, rq->count / 4, 4 * sizeof(Id), solver_allruleinfos_cmp, 0);
+ /* throw out identical entries */
+ for (i = j = 0; i < rq->count; i += 4)
+ {
+ if (j)
+ {
+ if (rq->elements[i] == rq->elements[j - 4] &&
+ rq->elements[i + 1] == rq->elements[j - 3] &&
+ rq->elements[i + 2] == rq->elements[j - 2] &&
+ rq->elements[i + 3] == rq->elements[j - 1])
+ continue;
+ }
+ rq->elements[j++] = rq->elements[i];
+ rq->elements[j++] = rq->elements[i + 1];
+ rq->elements[j++] = rq->elements[i + 2];
+ rq->elements[j++] = rq->elements[i + 3];
+ }
+ rq->count = j;
+ return j / 4;
+}
+
+SolverRuleinfo
+solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
+{
+ Pool *pool = solv->pool;
+ Rule *r = solv->rules + rid;
+ SolverRuleinfo type = SOLVER_RULE_UNKNOWN;
+
+ if (fromp)
+ *fromp = 0;
+ if (top)
+ *top = 0;
+ if (depp)
+ *depp = 0;
+ if (rid > 0 && rid < solv->pkgrules_end)
+ {
+ Queue rq;
+ int i;
+
+ if (r->p >= 0)
+ return SOLVER_RULE_PKG;
+ if (fromp)
+ *fromp = -r->p;
+ queue_init(&rq);
+ getpkgruleinfos(solv, r, &rq);
+ type = SOLVER_RULE_PKG;
+ for (i = 0; i < rq.count; i += 4)
+ {
+ Id qt, qo, qp, qd;
+ qt = rq.elements[i];
+ qp = rq.elements[i + 1];
+ qo = rq.elements[i + 2];
+ qd = rq.elements[i + 3];
+ if (type == SOLVER_RULE_PKG || type > qt)
+ {
+ type = qt;
+ if (fromp)
+ *fromp = qp;
+ if (top)
+ *top = qo;
+ if (depp)
+ *depp = qd;
+ }
+ }
+ queue_free(&rq);
+ return type;
+ }
+ if (rid >= solv->jobrules && rid < solv->jobrules_end)
+ {
+ Id jidx = solv->ruletojob.elements[rid - solv->jobrules];
+ if (fromp)
+ *fromp = jidx;
+ if (top)
+ *top = solv->job.elements[jidx];
+ if (depp)
+ *depp = solv->job.elements[jidx + 1];
+ if ((r->d == 0 || r->d == -1) && r->w2 == 0 && r->p == -SYSTEMSOLVABLE)
+ {
+ Id how = solv->job.elements[jidx];
+ if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_INSTALL|SOLVER_SOLVABLE_NAME))
+ return SOLVER_RULE_JOB_UNKNOWN_PACKAGE;
+ if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES))
+ return SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP;
+ if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_ERASE|SOLVER_SOLVABLE_NAME))
+ return SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM;
+ if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_ERASE|SOLVER_SOLVABLE_PROVIDES))
+ return SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM;
+ return SOLVER_RULE_JOB_UNSUPPORTED;
+ }
+ return SOLVER_RULE_JOB;
+ }
+ if (rid >= solv->updaterules && rid < solv->updaterules_end)
+ {
+ if (fromp)
+ *fromp = solv->installed->start + (rid - solv->updaterules);
+ return SOLVER_RULE_UPDATE;
+ }
+ if (rid >= solv->featurerules && rid < solv->featurerules_end)
+ {
+ if (fromp)
+ *fromp = solv->installed->start + (rid - solv->featurerules);
+ return SOLVER_RULE_FEATURE;
+ }
+ if (rid >= solv->duprules && rid < solv->duprules_end)
+ {
+ if (fromp)
+ *fromp = -r->p;
+ if (depp)
+ *depp = pool->solvables[-r->p].name;
+ return SOLVER_RULE_DISTUPGRADE;
+ }
+ if (rid >= solv->infarchrules && rid < solv->infarchrules_end)
+ {
+ if (fromp)
+ *fromp = -r->p;
+ if (depp)
+ *depp = pool->solvables[-r->p].name;
+ return SOLVER_RULE_INFARCH;
+ }
+ if (rid >= solv->bestrules && rid < solv->bestrules_end)
+ {
+ if (fromp && solv->bestrules_pkg[rid - solv->bestrules] > 0)
+ *fromp = solv->bestrules_pkg[rid - solv->bestrules];
+ return SOLVER_RULE_BEST;
+ }
+ if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
+ {
+ if (fromp)
+ *fromp = -r->p;
+ if (top)
+ {
+ /* first solvable is enough, we just need it for the name */
+ if (!r->d || r->d == -1)
+ *top = r->w2;
+ else
+ *top = pool->whatprovidesdata[r->d < 0 ? -r->d : r->d];
+ }
+ if (depp)
+ *depp = solv->yumobsrules_info[rid - solv->yumobsrules];
+ return SOLVER_RULE_YUMOBS;
+ }
+ if (rid >= solv->choicerules && rid < solv->choicerules_end)
+ {
+ return SOLVER_RULE_CHOICE;
+ }
+ if (rid >= solv->learntrules)
+ {
+ return SOLVER_RULE_LEARNT;
+ }
+ return SOLVER_RULE_UNKNOWN;
+}
+
+SolverRuleinfo
+solver_ruleclass(Solver *solv, Id rid)
+{
+ if (rid <= 0)
+ return SOLVER_RULE_UNKNOWN;
+ if (rid > 0 && rid < solv->pkgrules_end)
+ return SOLVER_RULE_PKG;
+ if (rid >= solv->jobrules && rid < solv->jobrules_end)
+ return SOLVER_RULE_JOB;
+ if (rid >= solv->updaterules && rid < solv->updaterules_end)
+ return SOLVER_RULE_UPDATE;
+ if (rid >= solv->featurerules && rid < solv->featurerules_end)
+ return SOLVER_RULE_FEATURE;
+ if (rid >= solv->duprules && rid < solv->duprules_end)
+ return SOLVER_RULE_DISTUPGRADE;
+ if (rid >= solv->infarchrules && rid < solv->infarchrules_end)
+ return SOLVER_RULE_INFARCH;
+ if (rid >= solv->bestrules && rid < solv->bestrules_end)
+ return SOLVER_RULE_BEST;
+ if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
+ return SOLVER_RULE_YUMOBS;
+ if (rid >= solv->choicerules && rid < solv->choicerules_end)
+ return SOLVER_RULE_CHOICE;
+ if (rid >= solv->learntrules && rid < solv->nrules)
+ return SOLVER_RULE_LEARNT;
+ return SOLVER_RULE_UNKNOWN;
+}
+
+void
+solver_ruleliterals(Solver *solv, Id rid, Queue *q)
+{
+ Pool *pool = solv->pool;
+ Id p, pp;
+ Rule *r;
+
+ queue_empty(q);
+ r = solv->rules + rid;
+ FOR_RULELITERALS(p, pp, r)
+ if (p != -SYSTEMSOLVABLE)
+ queue_push(q, p);
+ if (!q->count)
+ queue_push(q, -SYSTEMSOLVABLE); /* hmm, better to return an empty result? */
+}
+
+int
+solver_rule2jobidx(Solver *solv, Id rid)
+{
+ if (rid < solv->jobrules || rid >= solv->jobrules_end)
+ return 0;
+ return solv->ruletojob.elements[rid - solv->jobrules] + 1;
+}
+
+/* job rule introspection */
+Id
+solver_rule2job(Solver *solv, Id rid, Id *whatp)
+{
+ int idx;
+ if (rid < solv->jobrules || rid >= solv->jobrules_end)
+ {
+ if (whatp)
+ *whatp = 0;
+ return 0;
+ }
+ idx = solv->ruletojob.elements[rid - solv->jobrules];
+ if (whatp)
+ *whatp = solv->job.elements[idx + 1];
+ return solv->job.elements[idx];
+}
+
+/* update/feature rule introspection */
+Id
+solver_rule2solvable(Solver *solv, Id rid)
+{
+ if (rid >= solv->updaterules && rid < solv->updaterules_end)
+ return rid - solv->updaterules;
+ if (rid >= solv->featurerules && rid < solv->featurerules_end)
+ return rid - solv->featurerules;
+ return 0;
+}
+
+Id
+solver_rule2pkgrule(Solver *solv, Id rid)
+{
+ if (rid >= solv->choicerules && rid < solv->choicerules_end)
+ return solv->choicerules_ref[rid - solv->choicerules];
+ return 0;
+}
+
+static void
+solver_rule2rules_rec(Solver *solv, Id rid, Queue *q, Map *seen)
+{
+ int i;
+ Id rid2;
+
+ if (seen)
+ MAPSET(seen, rid);
+ for (i = solv->learnt_why.elements[rid - solv->learntrules]; (rid2 = solv->learnt_pool.elements[i]) != 0; i++)
+ {
+ if (seen)
+ {
+ if (MAPTST(seen, rid2))
+ continue;
+ if (rid2 >= solv->learntrules)
+ solver_rule2rules_rec(solv, rid2, q, seen);
+ continue;
+ }
+ queue_push(q, rid2);
+ }
+}
+
+/* learnt rule introspection */
+void
+solver_rule2rules(Solver *solv, Id rid, Queue *q, int recursive)
+{
+ queue_empty(q);
+ if (rid < solv->learntrules || rid >= solv->nrules)
+ return;
+ if (recursive)
+ {
+ Map seen;
+ map_init(&seen, solv->nrules);
+ solver_rule2rules_rec(solv, rid, q, &seen);
+ map_free(&seen);
+ }
+ else
+ solver_rule2rules_rec(solv, rid, q, 0);
+}
+
+
+/* check if the newest versions of pi still provides the dependency we're looking for */
+static int
+solver_choicerulecheck(Solver *solv, Id pi, Rule *r, Map *m, Queue *q)
+{
+ Pool *pool = solv->pool;
+ Rule *ur;
+ Id p, pp;
+ int i;
+
+ if (!q->count || q->elements[0] != pi)
+ {
+ if (q->count)
+ queue_empty(q);
+ ur = solv->rules + solv->updaterules + (pi - pool->installed->start);
+ if (!ur->p)
+ ur = solv->rules + solv->featurerules + (pi - pool->installed->start);
+ if (!ur->p)
+ return 0;
+ queue_push2(q, pi, 0);
+ FOR_RULELITERALS(p, pp, ur)
+ if (p > 0)
+ queue_push(q, p);
+ }
+ if (q->count == 2)
+ return 1;
+ if (q->count == 3)
+ {
+ p = q->elements[2];
+ return MAPTST(m, p) ? 0 : 1;
+ }
+ if (!q->elements[1])
+ {
+ for (i = 2; i < q->count; i++)
+ if (!MAPTST(m, q->elements[i]))
+ break;
+ if (i == q->count)
+ return 0; /* all provide it, no need to filter */
+ /* some don't provide it, have to filter */
+ queue_deleten(q, 0, 2);
+ policy_filter_unwanted(solv, q, POLICY_MODE_CHOOSE);
+ queue_unshift(q, 1); /* filter mark */
+ queue_unshift(q, pi);
+ }
+ for (i = 2; i < q->count; i++)
+ if (MAPTST(m, q->elements[i]))
+ return 0; /* at least one provides it */
+ return 1; /* none of the new packages provided it */
+}
+
+static inline void
+queue_removeelement(Queue *q, Id el)
+{
+ int i, j;
+ for (i = 0; i < q->count; i++)
+ if (q->elements[i] == el)
+ break;
+ if (i < q->count)
+ {
+ for (j = i++; i < q->count; i++)
+ if (q->elements[i] != el)
+ q->elements[j++] = q->elements[i];
+ queue_truncate(q, j);
+ }
+}
+
+void
+solver_addchoicerules(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Map m, mneg;
+ Rule *r;
+ Queue q, qi, qcheck;
+ int i, j, rid, havechoice;
+ Id p, d, pp;
+ Id p2, pp2;
+ Solvable *s, *s2;
+ Id lastaddedp, lastaddedd;
+ int lastaddedcnt;
+ unsigned int now;
+
+ solv->choicerules = solv->nrules;
+ if (!pool->installed)
+ {
+ solv->choicerules_end = solv->nrules;
+ return;
+ }
+ now = solv_timems(0);
+ solv->choicerules_ref = solv_calloc(solv->pkgrules_end, sizeof(Id));
+ queue_init(&q);
+ queue_init(&qi);
+ queue_init(&qcheck);
+ map_init(&m, pool->nsolvables);
+ map_init(&mneg, pool->nsolvables);
+ /* set up negative assertion map from infarch and dup rules */
+ for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
+ if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
+ MAPSET(&mneg, -r->p);
+ for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++)
+ if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
+ MAPSET(&mneg, -r->p);
+ lastaddedp = 0;
+ lastaddedd = 0;
+ lastaddedcnt = 0;
+ for (rid = 1; rid < solv->pkgrules_end ; rid++)
+ {
+ r = solv->rules + rid;
+ if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 <= 0))
+ continue; /* only look at requires rules */
+ /* solver_printrule(solv, SOLV_DEBUG_RESULT, r); */
+ queue_empty(&q);
+ queue_empty(&qi);
+ havechoice = 0;
+ FOR_RULELITERALS(p, pp, r)
+ {
+ if (p < 0)
+ continue;
+ s = pool->solvables + p;
+ if (!s->repo)
+ continue;
+ if (s->repo == pool->installed)
+ {
+ queue_push(&q, p);
+ continue;
+ }
+ /* check if this package is "blocked" by a installed package */
+ s2 = 0;
+ FOR_PROVIDES(p2, pp2, s->name)
+ {
+ s2 = pool->solvables + p2;
+ if (s2->repo != pool->installed)
+ continue;
+ if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
+ continue;
+ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
+ continue;
+ break;
+ }
+ if (p2)
+ {
+ /* found installed package p2 that we can update to p */
+ if (MAPTST(&mneg, p))
+ continue;
+ if (policy_is_illegal(solv, s2, s, 0))
+ continue;
+#if 0
+ if (solver_choicerulecheck(solv, p2, r, &m))
+ continue;
+ queue_push(&qi, p2);
+#else
+ queue_push2(&qi, p2, p);
+#endif
+ queue_push(&q, p);
+ continue;
+ }
+ if (s->obsoletes)
+ {
+ Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+ s2 = 0;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(p2, pp2, obs)
+ {
+ s2 = pool->solvables + p2;
+ if (s2->repo != pool->installed)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+ continue;
+ break;
+ }
+ if (p2)
+ break;
+ }
+ if (obs)
+ {
+ /* found installed package p2 that we can update to p */
+ if (MAPTST(&mneg, p))
+ continue;
+ if (policy_is_illegal(solv, s2, s, 0))
+ continue;
+#if 0
+ if (solver_choicerulecheck(solv, p2, r, &m))
+ continue;
+ queue_push(&qi, p2);
+#else
+ queue_push2(&qi, p2, p);
+#endif
+ queue_push(&q, p);
+ continue;
+ }
+ }
+ /* package p is independent of the installed ones */
+ havechoice = 1;
+ }
+ if (!havechoice || !q.count || !qi.count)
+ continue; /* no choice */
+
+ FOR_RULELITERALS(p, pp, r)
+ if (p > 0)
+ MAPSET(&m, p);
+
+ /* do extra checking */
+ for (i = j = 0; i < qi.count; i += 2)
+ {
+ p2 = qi.elements[i];
+ if (!p2)
+ continue;
+ if (solver_choicerulecheck(solv, p2, r, &m, &qcheck))
+ {
+ /* oops, remove element p from q */
+ queue_removeelement(&q, qi.elements[i + 1]);
+ continue;
+ }
+ qi.elements[j++] = p2;
+ }
+ queue_truncate(&qi, j);
+
+ if (!q.count || !qi.count)
+ {
+ FOR_RULELITERALS(p, pp, r)
+ if (p > 0)
+ MAPCLR(&m, p);
+ continue;
+ }
+
+
+ /* now check the update rules of the installed package.
+ * if all packages of the update rules are contained in
+ * the dependency rules, there's no need to set up the choice rule */
+ for (i = 0; i < qi.count; i++)
+ {
+ Rule *ur;
+ if (!qi.elements[i])
+ continue;
+ ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start);
+ if (!ur->p)
+ ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start);
+ if (!ur->p)
+ continue;
+ FOR_RULELITERALS(p, pp, ur)
+ if (!MAPTST(&m, p))
+ break;
+ if (p)
+ break;
+ for (j = i + 1; j < qi.count; j++)
+ if (qi.elements[i] == qi.elements[j])
+ qi.elements[j] = 0;
+ }
+ /* empty map again */
+ FOR_RULELITERALS(p, pp, r)
+ if (p > 0)
+ MAPCLR(&m, p);
+ if (i == qi.count)
+ {
+#if 0
+ printf("skipping choice ");
+ solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + rid);
+#endif
+ continue;
+ }
+
+ /* don't add identical rules */
+ if (lastaddedp == r->p && lastaddedcnt == q.count)
+ {
+ for (i = 0; i < q.count; i++)
+ if (q.elements[i] != pool->whatprovidesdata[lastaddedd + i])
+ break;
+ if (i == q.count)
+ continue; /* already added that one */
+ }
+ d = q.count ? pool_queuetowhatprovides(pool, &q) : 0;
+
+ lastaddedp = r->p;
+ lastaddedd = d;
+ lastaddedcnt = q.count;
+
+ solver_addrule(solv, r->p, 0, d);
+ queue_push(&solv->weakruleq, solv->nrules - 1);
+ solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
+#if 0
+ printf("OLD ");
+ solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + rid);
+ printf("WEAK CHOICE ");
+ solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + solv->nrules - 1);
+#endif
+ }
+ queue_free(&q);
+ queue_free(&qi);
+ queue_free(&qcheck);
+ map_free(&m);
+ map_free(&mneg);
+ solv->choicerules_end = solv->nrules;
+ /* shrink choicerules_ref */
+ solv->choicerules_ref = solv_realloc2(solv->choicerules_ref, solv->choicerules_end - solv->choicerules, sizeof(Id));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "choice rule creation took %d ms\n", solv_timems(now));
+}
+
+/* called when a choice rule is disabled by analyze_unsolvable. We also
+ * have to disable all other choice rules so that the best packages get
+ * picked */
+void
+solver_disablechoicerules(Solver *solv, Rule *r)
+{
+ Id rid, p, pp;
+ Pool *pool = solv->pool;
+ Map m;
+ Rule *or;
+
+ or = solv->rules + solv->choicerules_ref[(r - solv->rules) - solv->choicerules];
+ map_init(&m, pool->nsolvables);
+ FOR_RULELITERALS(p, pp, or)
+ if (p > 0)
+ MAPSET(&m, p);
+ FOR_RULELITERALS(p, pp, r)
+ if (p > 0)
+ MAPCLR(&m, p);
+ for (rid = solv->choicerules; rid < solv->choicerules_end; rid++)
+ {
+ r = solv->rules + rid;
+ if (r->d < 0)
+ continue;
+ or = solv->rules + solv->choicerules_ref[(r - solv->rules) - solv->choicerules];
+ FOR_RULELITERALS(p, pp, or)
+ if (p > 0 && MAPTST(&m, p))
+ break;
+ if (p)
+ solver_disablerule(solv, r);
+ }
+}
+
+static void
+prune_to_update_targets(Solver *solv, Id *cp, Queue *q)
+{
+ int i, j;
+ Id p, *cp2;
+ for (i = j = 0; i < q->count; i++)
+ {
+ p = q->elements[i];
+ for (cp2 = cp; *cp2; cp2++)
+ if (*cp2 == p)
+ {
+ q->elements[j++] = p;
+ break;
+ }
+ }
+ queue_truncate(q, j);
+}
+
+static void
+prune_to_dup_packages(Solver *solv, Id p, Queue *q)
+{
+ int i, j;
+ for (i = j = 0; i < q->count; i++)
+ {
+ Id p = q->elements[i];
+ if (MAPTST(&solv->dupmap, p))
+ q->elements[j++] = p;
+ }
+ queue_truncate(q, j);
+}
+
+void
+solver_addbestrules(Solver *solv, int havebestinstalljobs)
+{
+ Pool *pool = solv->pool;
+ Id p;
+ Solvable *s;
+ Repo *installed = solv->installed;
+ Queue q, q2;
+ Rule *r;
+ Queue r2pkg;
+ int i, oldcnt;
+
+ solv->bestrules = solv->nrules;
+ if (!installed)
+ {
+ solv->bestrules_end = solv->nrules;
+ return;
+ }
+ queue_init(&q);
+ queue_init(&q2);
+ queue_init(&r2pkg);
+
+ if (havebestinstalljobs)
+ {
+ for (i = 0; i < solv->job.count; i += 2)
+ {
+ if ((solv->job.elements[i] & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST))
+ {
+ int j;
+ Id p2, pp2;
+ for (j = 0; j < solv->ruletojob.count; j++)
+ if (solv->ruletojob.elements[j] == i)
+ break;
+ if (j == solv->ruletojob.count)
+ continue;
+ r = solv->rules + solv->jobrules + j;
+ queue_empty(&q);
+ FOR_RULELITERALS(p2, pp2, r)
+ if (p2 > 0)
+ queue_push(&q, p2);
+ if (!q.count)
+ continue; /* orphaned */
+ /* select best packages, just look at prio and version */
+ oldcnt = q.count;
+ policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
+ if (q.count == oldcnt)
+ continue; /* nothing filtered */
+ p2 = queue_shift(&q);
+ if (q.count < 2)
+ solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
+ else
+ solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
+ queue_push(&r2pkg, -(solv->jobrules + j));
+ }
+ }
+ }
+
+ if (solv->bestupdatemap_all || solv->bestupdatemap.size)
+ {
+ FOR_REPO_SOLVABLES(installed, p, s)
+ {
+ Id d, p2, pp2;
+ if (!solv->updatemap_all && (!solv->updatemap.size || !MAPTST(&solv->updatemap, p - installed->start)))
+ continue;
+ if (!solv->bestupdatemap_all && (!solv->bestupdatemap.size || !MAPTST(&solv->bestupdatemap, p - installed->start)))
+ continue;
+ queue_empty(&q);
+ if (solv->bestobeypolicy)
+ r = solv->rules + solv->updaterules + (p - installed->start);
+ else
+ {
+ r = solv->rules + solv->featurerules + (p - installed->start);
+ if (!r->p) /* identical to update rule? */
+ r = solv->rules + solv->updaterules + (p - installed->start);
+ }
+ if (solv->specialupdaters && (d = solv->specialupdaters[p - installed->start]) != 0 && r == solv->rules + solv->updaterules + (p - installed->start))
+ {
+ /* need to check specialupdaters */
+ if (r->p == p) /* be careful with the dup case */
+ queue_push(&q, p);
+ while ((p2 = pool->whatprovidesdata[d++]) != 0)
+ queue_push(&q, p2);
+ }
+ else
+ {
+ FOR_RULELITERALS(p2, pp2, r)
+ if (p2 > 0)
+ queue_push(&q, p2);
+ }
+ if (solv->update_targets && solv->update_targets->elements[p - installed->start])
+ prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q);
+ if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
+ prune_to_dup_packages(solv, p, &q);
+ /* select best packages, just look at prio and version */
+ policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
+ if (!q.count)
+ continue; /* orphaned */
+ if (solv->bestobeypolicy)
+ {
+ /* also filter the best of the feature rule packages and add them */
+ r = solv->rules + solv->featurerules + (p - installed->start);
+ if (r->p)
+ {
+ int j;
+ queue_empty(&q2);
+ FOR_RULELITERALS(p2, pp2, r)
+ if (p2 > 0)
+ queue_push(&q2, p2);
+ if (solv->update_targets && solv->update_targets->elements[p - installed->start])
+ prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q2);
+ if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
+ prune_to_dup_packages(solv, p, &q2);
+ policy_filter_unwanted(solv, &q2, POLICY_MODE_RECOMMEND);
+ for (j = 0; j < q2.count; j++)
+ queue_pushunique(&q, q2.elements[j]);
+ }
+ }
+ p2 = queue_shift(&q);
+ if (q.count < 2)
+ solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
+ else
+ solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
+ queue_push(&r2pkg, p);
+ }
+ }
+ if (r2pkg.count)
+ solv->bestrules_pkg = solv_memdup2(r2pkg.elements, r2pkg.count, sizeof(Id));
+ solv->bestrules_end = solv->nrules;
+ queue_free(&q);
+ queue_free(&q2);
+ queue_free(&r2pkg);
+}
+
+
+
+
+/* yumobs rule handling */
+
+static void
+find_obsolete_group(Solver *solv, Id obs, Queue *q)
+{
+ Pool *pool = solv->pool;
+ Queue qn;
+ Id p2, pp2, op, *opp, opp2;
+ int i, j, qnc, ncnt;
+
+ queue_empty(q);
+ FOR_PROVIDES(p2, pp2, obs)
+ {
+ Solvable *s2 = pool->solvables + p2;
+ if (s2->repo != pool->installed)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
+ continue;
+ /* we obsolete installed package s2 with obs. now find all other packages that have the same dep */
+ for (opp = solv->obsoletes_data + solv->obsoletes[p2 - solv->installed->start]; (op = *opp++) != 0;)
+ {
+ Solvable *os = pool->solvables + op;
+ Id obs2, *obsp2;
+ if (!os->obsoletes)
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
+ continue;
+ obsp2 = os->repo->idarraydata + os->obsoletes;
+ while ((obs2 = *obsp2++) != 0)
+ if (obs2 == obs)
+ break;
+ if (obs2)
+ queue_pushunique(q, op);
+ }
+ /* also search packages with the same name */
+ FOR_PROVIDES(op, opp2, s2->name)
+ {
+ Solvable *os = pool->solvables + op;
+ Id obs2, *obsp2;
+ if (os->name != s2->name)
+ continue;
+ if (!os->obsoletes)
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
+ continue;
+ obsp2 = os->repo->idarraydata + os->obsoletes;
+ while ((obs2 = *obsp2++) != 0)
+ if (obs2 == obs)
+ break;
+ if (obs2)
+ queue_pushunique(q, op);
+ }
+ }
+ /* find names so that we can build groups */
+ queue_init_clone(&qn, q);
+ prune_to_best_version(solv->pool, &qn);
+#if 0
+{
+ for (i = 0; i < qn.count; i++)
+ printf(" + %s\n", pool_solvid2str(pool, qn.elements[i]));
+}
+#endif
+ /* filter into name groups */
+ qnc = qn.count;
+ if (qnc == 1)
+ {
+ queue_free(&qn);
+ queue_empty(q);
+ return;
+ }
+ ncnt = 0;
+ for (i = 0; i < qnc; i++)
+ {
+ Id n = pool->solvables[qn.elements[i]].name;
+ int got = 0;
+ for (j = 0; j < q->count; j++)
+ {
+ Id p = q->elements[j];
+ if (pool->solvables[p].name == n)
+ {
+ queue_push(&qn, p);
+ got = 1;
+ }
+ }
+ if (got)
+ {
+ queue_push(&qn, 0);
+ ncnt++;
+ }
+ }
+ if (ncnt <= 1)
+ {
+ queue_empty(q);
+ }
+ else
+ {
+ queue_empty(q);
+ queue_insertn(q, 0, qn.count - qnc, qn.elements + qnc);
+ }
+ queue_free(&qn);
+}
+
+void
+solver_addyumobsrules(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ Id p, op, *opp;
+ Solvable *s;
+ Queue qo, qq, yumobsinfoq;
+ int i, j, k;
+ unsigned int now;
+
+ solv->yumobsrules = solv->nrules;
+ if (!installed || !solv->obsoletes)
+ {
+ solv->yumobsrules_end = solv->nrules;
+ return;
+ }
+ now = solv_timems(0);
+ queue_init(&qo);
+ FOR_REPO_SOLVABLES(installed, p, s)
+ {
+ if (!solv->obsoletes[p - installed->start])
+ continue;
+#if 0
+printf("checking yumobs for %s\n", pool_solvable2str(pool, s));
+#endif
+ queue_empty(&qo);
+ for (opp = solv->obsoletes_data + solv->obsoletes[p - installed->start]; (op = *opp++) != 0;)
+ {
+ Solvable *os = pool->solvables + op;
+ Id obs, *obsp = os->repo->idarraydata + os->obsoletes;
+ Id p2, pp2;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(p2, pp2, obs)
+ {
+ Solvable *s2 = pool->solvables + p2;
+ if (s2->repo != installed)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+ continue;
+ queue_pushunique(&qo, obs);
+ break;
+ }
+ }
+ }
+ }
+ if (!qo.count)
+ {
+ queue_free(&qo);
+ return;
+ }
+ queue_init(&yumobsinfoq);
+ queue_init(&qq);
+ for (i = 0; i < qo.count; i++)
+ {
+ int group, groupk, groupstart;
+ queue_empty(&qq);
+#if 0
+printf("investigating %s\n", pool_dep2str(pool, qo.elements[i]));
+#endif
+ find_obsolete_group(solv, qo.elements[i], &qq);
+#if 0
+printf("result:\n");
+for (j = 0; j < qq.count; j++)
+ if (qq.elements[j] == 0)
+ printf("---\n");
+ else
+ printf("%s\n", pool_solvid2str(pool, qq.elements[j]));
+#endif
+
+ if (!qq.count)
+ continue;
+ /* at least two goups, build rules */
+ group = 0;
+ for (j = 0; j < qq.count; j++)
+ {
+ p = qq.elements[j];
+ if (!p)
+ {
+ group++;
+ continue;
+ }
+ if (pool->solvables[p].repo == installed)
+ continue;
+ groupk = 0;
+ groupstart = 0;
+ for (k = 0; k < qq.count; k++)
+ {
+ Id pk = qq.elements[k];
+ if (pk)
+ continue;
+ if (group != groupk && k > groupstart)
+ {
+ /* add the rule */
+ if (k - groupstart == 1)
+ solver_addrule(solv, -p, qq.elements[groupstart], 0);
+ else
+ solver_addrule(solv, -p, 0, pool_ids2whatprovides(pool, qq.elements + groupstart, k - groupstart));
+ queue_push(&yumobsinfoq, qo.elements[i]);
+ }
+ groupstart = k + 1;
+ groupk++;
+ }
+ }
+ }
+ if (yumobsinfoq.count)
+ solv->yumobsrules_info = solv_memdup2(yumobsinfoq.elements, yumobsinfoq.count, sizeof(Id));
+ queue_free(&yumobsinfoq);
+ queue_free(&qq);
+ queue_free(&qo);
+ solv->yumobsrules_end = solv->nrules;
+ POOL_DEBUG(SOLV_DEBUG_STATS, "yumobs rule creation took %d ms\n", solv_timems(now));
+}
+
+#undef CLEANDEPSDEBUG
+
+/*
+ * This functions collects all packages that are looked at
+ * when a dependency is checked. We need it to "pin" installed
+ * packages when removing a supplemented package in createcleandepsmap.
+ * Here's an not uncommon example:
+ * A contains "Supplements: packageand(B, C)"
+ * B contains "Requires: A"
+ * Now if we remove C, the supplements is no longer true,
+ * thus we also remove A. Without the dep_pkgcheck function, we
+ * would now also remove B, but this is wrong, as adding back
+ * C doesn't make the supplements true again. Thus we "pin" B
+ * when we remove A.
+ * There's probably a better way to do this, but I haven't come
+ * up with it yet ;)
+ */
+static inline void
+dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q)
+{
+ Pool *pool = solv->pool;
+ Id p, pp;
+
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags >= 8)
+ {
+ if (rd->flags == REL_AND)
+ {
+ dep_pkgcheck(solv, rd->name, m, q);
+ dep_pkgcheck(solv, rd->evr, m, q);
+ return;
+ }
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+ return;
+ }
+ }
+ FOR_PROVIDES(p, pp, dep)
+ if (!m || MAPTST(m, p))
+ queue_push(q, p);
+}
+
+static int
+check_xsupp(Solver *solv, Queue *depq, Id dep)
+{
+ Pool *pool = solv->pool;
+ Id p, pp;
+
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags >= 8)
+ {
+ if (rd->flags == REL_AND)
+ {
+ if (!check_xsupp(solv, depq, rd->name))
+ return 0;
+ return check_xsupp(solv, depq, rd->evr);
+ }
+ if (rd->flags == REL_OR)
+ {
+ if (check_xsupp(solv, depq, rd->name))
+ return 1;
+ return check_xsupp(solv, depq, rd->evr);
+ }
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+#if 0
+ return solver_splitprovides(solv, rd->evr);
+#else
+ return 0;
+#endif
+ }
+ if (depq && rd->flags == REL_NAMESPACE)
+ {
+ int i;
+ for (i = 0; i < depq->count; i++)
+ if (depq->elements[i] == dep || depq->elements[i] == rd->name)
+ return 1;
+ }
+ }
+ FOR_PROVIDES(p, pp, dep)
+ if (p == SYSTEMSOLVABLE || pool->solvables[p].repo == solv->installed)
+ return 1;
+ return 0;
+}
+
+static inline int
+queue_contains(Queue *q, Id id)
+{
+ int i;
+ for (i = 0; i < q->count; i++)
+ if (q->elements[i] == id)
+ return 1;
+ return 0;
+}
+
+#ifdef ENABLE_COMPLEX_DEPS
+static void
+complex_cleandeps_remove(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq)
+{
+ int i;
+ Queue dq;
+ Id p;
+
+ queue_init(&dq);
+ i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ {
+ queue_free(&dq);
+ return;
+ }
+ for (i = 0; i < dq.count; i++)
+ {
+ for (; (p = dq.elements[i]) != 0; i++)
+ {
+ if (p < 0)
+ {
+ if (!MAPTST(installedm, -p))
+ break;
+ continue;
+ }
+ if (p != SYSTEMSOLVABLE && MAPTST(im, p))
+ {
+#ifdef CLEANDEPSDEBUG
+ printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+ queue_push(iq, p);
+ }
+ }
+ while (dq.elements[i])
+ i++;
+ }
+ queue_free(&dq);
+}
+
+static void
+complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq, Map *userinstalled)
+{
+ int i, blk;
+ Queue dq;
+ Id p;
+
+ queue_init(&dq);
+ i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ {
+ queue_free(&dq);
+ return;
+ }
+ for (i = 0; i < dq.count; i++)
+ {
+ blk = i;
+ for (; (p = dq.elements[i]) != 0; i++)
+ {
+ if (p < 0)
+ {
+ if (!MAPTST(installedm, -p))
+ break;
+ continue;
+ }
+ if (MAPTST(im, p))
+ break;
+ }
+ if (!p)
+ {
+ for (i = blk; (p = dq.elements[i]) != 0; i++)
+ {
+ if (p < 0)
+ continue;
+ if (!MAPTST(installedm, p))
+ continue;
+ if (p == ip || MAPTST(userinstalled, p - pool->installed->start))
+ continue;
+#ifdef CLEANDEPSDEBUG
+ printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+ MAPSET(im, p);
+ queue_push(iq, p);
+ }
+ }
+ while (dq.elements[i])
+ i++;
+ }
+ queue_free(&dq);
+}
+
+#endif
+
+/*
+ * Find all installed packages that are no longer
+ * needed regarding the current solver job.
+ *
+ * The algorithm is:
+ * - remove pass: remove all packages that could have
+ * been dragged in by the obsoleted packages.
+ * i.e. if package A is obsolete and contains "Requires: B",
+ * also remove B, as installing A will have pulled in B.
+ * after this pass, we have a set of still installed packages
+ * with broken dependencies.
+ * - add back pass:
+ * now add back all packages that the still installed packages
+ * require.
+ *
+ * The cleandeps packages are the packages removed in the first
+ * pass and not added back in the second pass.
+ *
+ * If we search for unneeded packages (unneeded is true), we
+ * simply remove all packages except the userinstalled ones in
+ * the first pass.
+ */
+static void
+solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ Queue *job = &solv->job;
+ Map userinstalled;
+ Map im;
+ Map installedm;
+ Rule *r;
+ Id rid, how, what, select;
+ Id p, pp, ip, jp;
+ Id req, *reqp, sup, *supp;
+ Solvable *s;
+ Queue iq, iqcopy, xsuppq;
+ int i;
+
+ map_empty(cleandepsmap);
+ if (!installed || installed->end == installed->start)
+ return;
+ map_init(&userinstalled, installed->end - installed->start);
+ map_init(&im, pool->nsolvables);
+ map_init(&installedm, pool->nsolvables);
+ queue_init(&iq);
+ queue_init(&xsuppq);
+
+ for (i = 0; i < job->count; i += 2)
+ {
+ how = job->elements[i];
+ if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
+ {
+ what = job->elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
+ if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+ FOR_REPO_SOLVABLES(installed, p, s)
+ MAPSET(&userinstalled, p - installed->start);
+ FOR_JOB_SELECT(p, pp, select, what)
+ if (pool->solvables[p].repo == installed)
+ MAPSET(&userinstalled, p - installed->start);
+ }
+ if ((how & (SOLVER_JOBMASK | SOLVER_SELECTMASK)) == (SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES))
+ {
+ what = job->elements[i + 1];
+ if (ISRELDEP(what))
+ {
+ Reldep *rd = GETRELDEP(pool, what);
+ if (rd->flags != REL_NAMESPACE)
+ continue;
+ if (rd->evr == 0)
+ {
+ queue_pushunique(&iq, rd->name);
+ continue;
+ }
+ FOR_PROVIDES(p, pp, what)
+ if (p)
+ break;
+ if (p)
+ continue;
+ queue_pushunique(&iq, what);
+ }
+ }
+ }
+
+ /* have special namespace cleandeps erases */
+ if (iq.count)
+ {
+ for (ip = installed->start; ip < installed->end; ip++)
+ {
+ s = pool->solvables + ip;
+ if (s->repo != installed)
+ continue;
+ if (!s->supplements)
+ continue;
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (ISRELDEP(sup) && check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup))
+ {
+#ifdef CLEANDEPSDEBUG
+ printf("xsupp %s from %s\n", pool_dep2str(pool, sup), pool_solvid2str(pool, ip));
+#endif
+ queue_pushunique(&xsuppq, sup);
+ }
+ }
+ queue_empty(&iq);
+ }
+
+ /* also add visible patterns to userinstalled for openSUSE */
+ if (1)
+ {
+ Dataiterator di;
+ dataiterator_init(&di, pool, 0, 0, SOLVABLE_ISVISIBLE, 0, 0);
+ while (dataiterator_step(&di))
+ {
+ Id *dp;
+ if (di.solvid <= 0)
+ continue;
+ s = pool->solvables + di.solvid;
+ if (!s->repo || !s->requires)
+ continue;
+ if (s->repo != installed && !pool_installable(pool, s))
+ continue;
+ if (strncmp(pool_id2str(pool, s->name), "pattern:", 8) != 0)
+ continue;
+ dp = s->repo->idarraydata + s->requires;
+ for (dp = s->repo->idarraydata + s->requires; *dp; dp++)
+ FOR_PROVIDES(p, pp, *dp)
+ if (pool->solvables[p].repo == installed)
+ {
+ if (strncmp(pool_id2str(pool, pool->solvables[p].name), "pattern", 7) != 0)
+ continue;
+ MAPSET(&userinstalled, p - installed->start);
+ }
+ }
+ dataiterator_free(&di);
+ }
+ if (1)
+ {
+ /* all products and their buddies are userinstalled */
+ for (p = installed->start; p < installed->end; p++)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->repo != installed)
+ continue;
+ if (!strncmp("product:", pool_id2str(pool, s->name), 8))
+ {
+ MAPSET(&userinstalled, p - installed->start);
+#ifdef ENABLE_LINKED_PKGS
+ if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
+ {
+ Id buddy = solv->instbuddy[p - installed->start];
+ if (buddy >= installed->start && buddy < installed->end)
+ MAPSET(&userinstalled, buddy - installed->start);
+ }
+#endif
+ }
+ }
+ }
+
+ /* add all positive elements (e.g. locks) to "userinstalled" */
+ for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
+ {
+ r = solv->rules + rid;
+ if (r->d < 0)
+ continue;
+ i = solv->ruletojob.elements[rid - solv->jobrules];
+ if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
+ continue;
+ FOR_RULELITERALS(p, jp, r)
+ if (p > 0 && pool->solvables[p].repo == installed)
+ MAPSET(&userinstalled, p - installed->start);
+ }
+
+ /* add all cleandeps candidates to iq */
+ for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
+ {
+ r = solv->rules + rid;
+ if (r->d < 0) /* disabled? */
+ continue;
+ if (r->d == 0 && r->p < 0 && r->w2 == 0) /* negative assertion (erase job)? */
+ {
+ p = -r->p;
+ if (pool->solvables[p].repo != installed)
+ continue;
+ MAPCLR(&userinstalled, p - installed->start);
+ if (unneeded)
+ continue;
+ i = solv->ruletojob.elements[rid - solv->jobrules];
+ how = job->elements[i];
+ if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS))
+ queue_push(&iq, p);
+ }
+ else if (r->p > 0) /* install job */
+ {
+ if (unneeded)
+ continue;
+ i = solv->ruletojob.elements[rid - solv->jobrules];
+ if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
+ {
+ /* check if the literals all obsolete some installed package */
+ Map om;
+ int iqstart;
+
+ /* just one installed literal */
+ if (r->d == 0 && r->w2 == 0 && pool->solvables[r->p].repo == installed)
+ continue;
+ /* multiversion is bad */
+ if (solv->multiversion.size && !solv->keepexplicitobsoletes)
+ {
+ FOR_RULELITERALS(p, jp, r)
+ if (MAPTST(&solv->multiversion, p))
+ break;
+ if (p)
+ continue;
+ }
+
+ om.size = 0;
+ iqstart = iq.count;
+ FOR_RULELITERALS(p, jp, r)
+ {
+ if (p < 0)
+ {
+ queue_truncate(&iq, iqstart); /* abort */
+ break;
+ }
+ if (pool->solvables[p].repo == installed)
+ {
+ if (iq.count == iqstart)
+ queue_push(&iq, p);
+ else
+ {
+ for (i = iqstart; i < iq.count; i++)
+ if (iq.elements[i] == p)
+ break;
+ queue_truncate(&iq, iqstart);
+ if (i < iq.count)
+ queue_push(&iq, p);
+ }
+ }
+ else
+ intersect_obsoletes(solv, p, &iq, iqstart, &om);
+ if (iq.count == iqstart)
+ break;
+ }
+ if (om.size)
+ map_free(&om);
+ }
+ }
+ }
+ queue_init_clone(&iqcopy, &iq);
+
+ if (!unneeded)
+ {
+ if (solv->cleandeps_updatepkgs)
+ for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
+ queue_push(&iq, solv->cleandeps_updatepkgs->elements[i]);
+ }
+
+ if (unneeded)
+ queue_empty(&iq); /* just in case... */
+
+ /* clear userinstalled bit for the packages we really want to delete/update */
+ for (i = 0; i < iq.count; i++)
+ {
+ p = iq.elements[i];
+ if (pool->solvables[p].repo != installed)
+ continue;
+ MAPCLR(&userinstalled, p - installed->start);
+ }
+
+ for (p = installed->start; p < installed->end; p++)
+ {
+ if (pool->solvables[p].repo != installed)
+ continue;
+ MAPSET(&installedm, p);
+ if (unneeded && !MAPTST(&userinstalled, p - installed->start))
+ continue;
+ MAPSET(&im, p);
+ }
+ MAPSET(&installedm, SYSTEMSOLVABLE);
+ MAPSET(&im, SYSTEMSOLVABLE);
+
+#ifdef CLEANDEPSDEBUG
+ printf("REMOVE PASS\n");
+#endif
+
+ for (;;)
+ {
+ if (!iq.count)
+ {
+ if (unneeded)
+ break;
+ /* supplements pass */
+ for (ip = installed->start; ip < installed->end; ip++)
+ {
+ if (!MAPTST(&installedm, ip))
+ continue;
+ s = pool->solvables + ip;
+ if (!s->supplements)
+ continue;
+ if (!MAPTST(&im, ip))
+ continue;
+ if (MAPTST(&userinstalled, ip - installed->start))
+ continue;
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (dep_possible(solv, sup, &im))
+ break;
+ if (!sup)
+ {
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (dep_possible(solv, sup, &installedm) || (xsuppq.count && queue_contains(&xsuppq, sup)))
+ {
+ /* no longer supplemented, also erase */
+ int iqcount = iq.count;
+ /* pin packages, see comment above dep_pkgcheck */
+ dep_pkgcheck(solv, sup, &im, &iq);
+ for (i = iqcount; i < iq.count; i++)
+ {
+ Id pqp = iq.elements[i];
+ if (pool->solvables[pqp].repo == installed)
+ MAPSET(&userinstalled, pqp - installed->start);
+ }
+ queue_truncate(&iq, iqcount);
+#ifdef CLEANDEPSDEBUG
+ printf("%s supplemented [%s]\n", pool_solvid2str(pool, ip), pool_dep2str(pool, sup));
+#endif
+ queue_push(&iq, ip);
+ }
+ }
+ }
+ if (!iq.count)
+ break; /* no supplementing package found, we're done */
+ }
+ ip = queue_shift(&iq);
+ s = pool->solvables + ip;
+ if (!MAPTST(&im, ip))
+ continue;
+ if (!MAPTST(&installedm, ip))
+ continue;
+ if (s->repo == installed && MAPTST(&userinstalled, ip - installed->start))
+ continue;
+ MAPCLR(&im, ip);
+#ifdef CLEANDEPSDEBUG
+ printf("removing %s\n", pool_solvable2str(pool, s));
+#endif
+ if (s->requires)
+ {
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ continue;
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, req))
+ {
+ complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
+ {
+#ifdef CLEANDEPSDEBUG
+ printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+ queue_push(&iq, p);
+ }
+ }
+ }
+ }
+ if (s->recommends)
+ {
+ reqp = s->repo->idarraydata + s->recommends;
+ while ((req = *reqp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, req))
+ {
+ complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
+ {
+#ifdef CLEANDEPSDEBUG
+ printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+ queue_push(&iq, p);
+ }
+ }
+ }
+ }
+ }
+
+ /* turn userinstalled into remove set for pruning */
+ map_empty(&userinstalled);
+ for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
+ {
+ r = solv->rules + rid;
+ if (r->p >= 0 || r->d != 0 || r->w2 != 0)
+ continue; /* disabled or not erase */
+ p = -r->p;
+ MAPCLR(&im, p);
+ if (pool->solvables[p].repo == installed)
+ MAPSET(&userinstalled, p - installed->start);
+ }
+ if (!unneeded && solv->cleandeps_updatepkgs)
+ {
+ for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
+ {
+ p = solv->cleandeps_updatepkgs->elements[i];
+ if (pool->solvables[p].repo == installed)
+ MAPSET(&userinstalled, p - installed->start);
+ }
+ }
+ MAPSET(&im, SYSTEMSOLVABLE); /* in case we cleared it above */
+ for (p = installed->start; p < installed->end; p++)
+ if (MAPTST(&im, p))
+ queue_push(&iq, p);
+ for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
+ {
+ r = solv->rules + rid;
+ if (r->d < 0)
+ continue;
+ FOR_RULELITERALS(p, jp, r)
+ if (p > 0)
+ queue_push(&iq, p);
+ }
+ /* also put directly addressed packages on the install queue
+ * so we can mark patterns as installed */
+ for (i = 0; i < job->count; i += 2)
+ {
+ how = job->elements[i];
+ if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
+ {
+ what = job->elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
+ if (select == SOLVER_SOLVABLE && pool->solvables[what].repo != installed)
+ queue_push(&iq, what);
+ }
+ }
+
+#ifdef CLEANDEPSDEBUG
+ printf("ADDBACK PASS\n");
+#endif
+ for (;;)
+ {
+ if (!iq.count)
+ {
+ /* supplements pass */
+ for (ip = installed->start; ip < installed->end; ip++)
+ {
+ if (!MAPTST(&installedm, ip))
+ continue;
+ if (MAPTST(&userinstalled, ip - installed->start))
+ continue;
+ s = pool->solvables + ip;
+ if (!s->supplements)
+ continue;
+ if (MAPTST(&im, ip))
+ continue;
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (dep_possible(solv, sup, &im))
+ break;
+ if (sup)
+ {
+#ifdef CLEANDEPSDEBUG
+ printf("%s supplemented\n", pool_solvid2str(pool, ip));
+#endif
+ MAPSET(&im, ip);
+ queue_push(&iq, ip);
+ }
+ }
+ if (!iq.count)
+ break;
+ }
+ ip = queue_shift(&iq);
+ s = pool->solvables + ip;
+#ifdef CLEANDEPSDEBUG
+ printf("adding back %s\n", pool_solvable2str(pool, s));
+#endif
+ if (s->requires)
+ {
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, req))
+ {
+ complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, req)
+ if (MAPTST(&im, p))
+ break;
+ if (p)
+ continue;
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (MAPTST(&installedm, p))
+ {
+ if (p == ip)
+ continue;
+ if (MAPTST(&userinstalled, p - installed->start))
+ continue;
+#ifdef CLEANDEPSDEBUG
+ printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+ MAPSET(&im, p);
+ queue_push(&iq, p);
+ }
+ }
+ }
+ }
+ if (s->recommends)
+ {
+ reqp = s->repo->idarraydata + s->recommends;
+ while ((req = *reqp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, req))
+ {
+ complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, req)
+ if (MAPTST(&im, p))
+ break;
+ if (p)
+ continue;
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (MAPTST(&installedm, p))
+ {
+ if (p == ip)
+ continue;
+ if (MAPTST(&userinstalled, p - installed->start))
+ continue;
+#ifdef CLEANDEPSDEBUG
+ printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+ MAPSET(&im, p);
+ queue_push(&iq, p);
+ }
+ }
+ }
+ }
+ }
+
+ queue_free(&iq);
+ /* make sure the updatepkgs and mistakes are not in the cleandeps map */
+ if (solv->cleandeps_updatepkgs)
+ for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
+ MAPSET(&im, solv->cleandeps_updatepkgs->elements[i]);
+ if (solv->cleandeps_mistakes)
+ for (i = 0; i < solv->cleandeps_mistakes->count; i++)
+ MAPSET(&im, solv->cleandeps_mistakes->elements[i]);
+ /* also remove original iq packages */
+ for (i = 0; i < iqcopy.count; i++)
+ MAPSET(&im, iqcopy.elements[i]);
+ queue_free(&iqcopy);
+ for (p = installed->start; p < installed->end; p++)
+ {
+ if (pool->solvables[p].repo != installed)
+ continue;
+ if (!MAPTST(&im, p))
+ MAPSET(cleandepsmap, p - installed->start);
+ }
+ map_free(&im);
+ map_free(&installedm);
+ map_free(&userinstalled);
+ queue_free(&xsuppq);
+#ifdef CLEANDEPSDEBUG
+ printf("=== final cleandeps map:\n");
+ for (p = installed->start; p < installed->end; p++)
+ if (MAPTST(cleandepsmap, p - installed->start))
+ printf(" - %s\n", pool_solvid2str(pool, p));
+#endif
+}
+
+
+struct trj_data {
+ Queue *edges;
+ Id *low;
+ Id idx;
+ Id nstack;
+ Id firstidx;
+};
+
+/* Tarjan's SCC algorithm, slightly modifed */
+static void
+trj_visit(struct trj_data *trj, Id node)
+{
+ Id *low = trj->low;
+ Queue *edges = trj->edges;
+ Id nnode, myidx, stackstart;
+ int i;
+
+ low[node] = myidx = trj->idx++;
+ low[(stackstart = trj->nstack++)] = node;
+ for (i = edges->elements[node]; (nnode = edges->elements[i]) != 0; i++)
+ {
+ Id l = low[nnode];
+ if (!l)
+ {
+ if (!edges->elements[edges->elements[nnode]])
+ {
+ trj->idx++;
+ low[nnode] = -1;
+ continue;
+ }
+ trj_visit(trj, nnode);
+ l = low[nnode];
+ }
+ if (l < 0)
+ continue;
+ if (l < trj->firstidx)
+ {
+ int k;
+ for (k = l; low[low[k]] == l; k++)
+ low[low[k]] = -1;
+ }
+ else if (l < low[node])
+ low[node] = l;
+ }
+ if (low[node] == myidx)
+ {
+ if (myidx != trj->firstidx)
+ myidx = -1;
+ for (i = stackstart; i < trj->nstack; i++)
+ low[low[i]] = myidx;
+ trj->nstack = stackstart;
+ }
+}
+
+#ifdef ENABLE_COMPLEX_DEPS
+static void
+complex_unneeded(Pool *pool, Id ip, Id req, Queue *edges, Map *cleandepsmap, Queue *unneededq)
+{
+ int i, j;
+ Queue dq;
+ Id p;
+
+ queue_init(&dq);
+ i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ {
+ queue_free(&dq);
+ return;
+ }
+ for (i = 0; i < dq.count; i++)
+ {
+ for (; (p = dq.elements[i]) != 0; i++)
+ {
+ if (p < 0)
+ {
+ if (pool->solvables[-p].repo != pool->installed)
+ break;
+ continue;
+ }
+ if (p == ip || pool->solvables[p].repo != pool->installed || !MAPTST(cleandepsmap, p - pool->installed->start))
+ continue;
+ for (j = 0; j < unneededq->count; j++)
+ if (p == unneededq->elements[j])
+ {
+ if (edges->elements[edges->count - 1] != j + 1)
+ queue_push(edges, j + 1);
+ break;
+ }
+ }
+ while (dq.elements[i])
+ i++;
+ }
+ queue_free(&dq);
+}
+#endif
+
+void
+solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
+{
+ Repo *installed = solv->installed;
+ int i;
+ Map cleandepsmap;
+
+ queue_empty(unneededq);
+ if (!installed || installed->end == installed->start)
+ return;
+
+ map_init(&cleandepsmap, installed->end - installed->start);
+ solver_createcleandepsmap(solv, &cleandepsmap, 1);
+ for (i = installed->start; i < installed->end; i++)
+ if (MAPTST(&cleandepsmap, i - installed->start))
+ queue_push(unneededq, i);
+
+ if (filtered && unneededq->count > 1)
+ {
+ Pool *pool = solv->pool;
+ Queue edges;
+ Id *nrequires;
+ Map installedm;
+ int j, pass, count = unneededq->count;
+ Id *low;
+
+ map_init(&installedm, pool->nsolvables);
+ for (i = installed->start; i < installed->end; i++)
+ if (pool->solvables[i].repo == installed)
+ MAPSET(&installedm, i);
+
+ nrequires = solv_calloc(count, sizeof(Id));
+ queue_init(&edges);
+ queue_prealloc(&edges, count * 4 + 10); /* pre-size */
+
+ /*
+ * Go through the solvables in the nodes queue and create edges for
+ * all requires/recommends/supplements between the nodes.
+ * The edges are stored in the edges queue, we add 1 to the node
+ * index so that nodes in the edges queue are != 0 and we can
+ * terminate the edge list with 0.
+ * Thus for node element 5, the edges are stored starting at
+ * edges.elements[6] and are 0-terminated.
+ */
+ /* leave first element zero to make things easier */
+ /* also add trailing zero */
+ queue_insertn(&edges, 0, 1 + count + 1, 0);
+
+ /* first requires and recommends */
+ for (i = 0; i < count; i++)
+ {
+ Solvable *s = pool->solvables + unneededq->elements[i];
+ int oldcount = edges.count;
+ edges.elements[i + 1] = oldcount;
+ for (pass = 0; pass < 2; pass++)
+ {
+ unsigned int off = pass == 0 ? s->requires : s->recommends;
+ Id p, pp, dep, *dp;
+ if (off)
+ for (dp = s->repo->idarraydata + off; (dep = *dp) != 0; dp++)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, dep))
+ {
+ complex_unneeded(pool, s - pool->solvables, dep, &edges, &cleandepsmap, unneededq);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, dep)
+ {
+ Solvable *sp = pool->solvables + p;
+ if (s == sp || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
+ continue;
+ for (j = 0; j < count; j++)
+ if (p == unneededq->elements[j])
+ {
+ if (edges.elements[edges.count - 1] != j + 1)
+ queue_push(&edges, j + 1);
+ }
+ }
+ }
+ if (pass == 0)
+ nrequires[i] = edges.count - oldcount;
+ }
+ queue_push(&edges, 0);
+ }
+#if 0
+ printf("requires + recommends\n");
+ for (i = 0; i < count; i++)
+ {
+ int j;
+ printf(" %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
+ for (j = edges.elements[i + 1]; edges.elements[j]; j++)
+ printf(" - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
+ }
+#endif
+
+ /* then add supplements */
+ for (i = 0; i < count; i++)
+ {
+ Solvable *s = pool->solvables + unneededq->elements[i];
+ if (s->supplements)
+ {
+ Id *dp;
+ int k;
+ for (dp = s->repo->idarraydata + s->supplements; *dp; dp++)
+ if (dep_possible(solv, *dp, &installedm))
+ {
+ Queue iq;
+ Id iqbuf[16];
+ queue_init_buffer(&iq, iqbuf, sizeof(iqbuf)/sizeof(*iqbuf));
+ dep_pkgcheck(solv, *dp, 0, &iq);
+ for (k = 0; k < iq.count; k++)
+ {
+ Id p = iq.elements[k];
+ Solvable *sp = pool->solvables + p;
+ if (p == unneededq->elements[i] || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
+ continue;
+ for (j = 0; j < count; j++)
+ if (p == unneededq->elements[j])
+ break;
+ /* now add edge from j + 1 to i + 1 */
+ queue_insert(&edges, edges.elements[j + 1] + nrequires[j], i + 1);
+ /* addapt following edge pointers */
+ for (j = j + 2; j < count + 1; j++)
+ edges.elements[j]++;
+ }
+ queue_free(&iq);
+ }
+ }
+ }
+#if 0
+ /* print result */
+ printf("+ supplements\n");
+ for (i = 0; i < count; i++)
+ {
+ int j;
+ printf(" %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
+ for (j = edges.elements[i + 1]; edges.elements[j]; j++)
+ printf(" - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
+ }
+#endif
+ map_free(&installedm);
+
+ /* now run SCC algo two times, first with requires+recommends+supplements,
+ * then again without the requires. We run it the second time to get rid
+ * of packages that got dragged in via recommends/supplements */
+ /*
+ * low will contain the result of the SCC search.
+ * it must be of at least size 2 * (count + 1) and
+ * must be zero initialized.
+ * The layout is:
+ * 0 low low ... low stack stack ...stack 0
+ * count count
+ */
+ low = solv_calloc(count + 1, 2 * sizeof(Id));
+ for (pass = 0; pass < 2; pass++)
+ {
+ struct trj_data trj;
+ if (pass)
+ {
+ memset(low, 0, (count + 1) * (2 * sizeof(Id)));
+ for (i = 0; i < count; i++)
+ {
+ edges.elements[i + 1] += nrequires[i];
+ if (!unneededq->elements[i])
+ low[i + 1] = -1; /* ignore this node */
+ }
+ }
+ trj.edges = &edges;
+ trj.low = low;
+ trj.idx = count + 1; /* stack starts here */
+ for (i = 1; i <= count; i++)
+ {
+ if (low[i])
+ continue;
+ if (edges.elements[edges.elements[i]])
+ {
+ trj.firstidx = trj.nstack = trj.idx;
+ trj_visit(&trj, i);
+ }
+ else
+ {
+ Id myidx = trj.idx++;
+ low[i] = myidx;
+ low[myidx] = i;
+ }
+ }
+ /* prune packages */
+ for (i = 0; i < count; i++)
+ if (low[i + 1] <= 0)
+ unneededq->elements[i] = 0;
+ }
+ solv_free(low);
+ solv_free(nrequires);
+ queue_free(&edges);
+
+ /* finally remove all pruned entries from unneededq */
+ for (i = j = 0; i < count; i++)
+ if (unneededq->elements[i])
+ unneededq->elements[j++] = unneededq->elements[i];
+ queue_truncate(unneededq, j);
+ }
+ map_free(&cleandepsmap);
+}
+
+
+void
+solver_breakorphans(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ int i, rid;
+ Map m;
+
+ if (!installed || solv->droporphanedmap_all)
+ return;
+ solv->brokenorphanrules = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->brokenorphanrules);
+ map_init(&m, installed->end - installed->start);
+ for (i = 0; i < solv->orphaned.count; i++)
+ {
+ Id p = solv->orphaned.elements[i];
+ if (pool->solvables[p].repo != installed)
+ continue;
+ if (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - installed->start))
+ continue;
+ MAPSET(&m, p - installed->start);
+ }
+ for (rid = 1; rid < solv->pkgrules_end ; rid++)
+ {
+ Id p, *dp;
+ Rule *r = solv->rules + rid;
+ /* ignore non-deps and simple conflicts */
+ if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
+ continue;
+ p = -r->p;
+ if (p < installed->start || p >= installed->end || !MAPTST(&m, p - installed->start))
+ {
+ /* need to check other literals */
+ if (r->d == 0 || r->d == -1)
+ continue;
+ for (dp = pool->whatprovidesdata + (r->d < 0 ? -r->d - 1 : r->d); *dp < 0; dp++)
+ {
+ p = -*dp;
+ if (p >= installed->start && p < installed->end && MAPTST(&m, p - installed->start))
+ break;
+ }
+ if (*dp >= 0)
+ continue;
+ }
+ /* ok, disable this rule */
+ queue_push(solv->brokenorphanrules, rid);
+ if (r->d >= 0)
+ solver_disablerule(solv, r);
+ }
+ map_free(&m);
+ if (!solv->brokenorphanrules->count)
+ {
+ queue_free(solv->brokenorphanrules);
+ solv->brokenorphanrules = solv_free(solv->brokenorphanrules);
+ }
+}
+
+void
+solver_check_brokenorphanrules(Solver *solv, Queue *dq)
+{
+ Pool *pool = solv->pool;
+ int i;
+ Id l, pp;
+
+ queue_empty(dq);
+ if (!solv->brokenorphanrules)
+ return;
+ for (i = 0; i < solv->brokenorphanrules->count; i++)
+ {
+ int rid = solv->brokenorphanrules->elements[i];
+ Rule *r = solv->rules + rid;
+ FOR_RULELITERALS(l, pp, r)
+ {
+ if (l < 0)
+ {
+ if (solv->decisionmap[-l] <= 0)
+ break;
+ }
+ else
+ {
+ if (solv->decisionmap[l] > 0 && pool->solvables[l].repo != solv->installed)
+ break;
+ }
+ }
+ if (l)
+ continue;
+ FOR_RULELITERALS(l, pp, r)
+ if (l > 0 && solv->decisionmap[l] == 0 && pool->solvables[l].repo != solv->installed)
+ queue_pushunique(dq, l);
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * rules.h
+ *
+ */
+
+#ifndef LIBSOLV_RULES_H
+#define LIBSOLV_RULES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------------------------------------
+ * 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
+ *
+ * There are a lot of rules, so the struct is kept as small as
+ * possible. Do not add new members unless there is no other way.
+ */
+
+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 */
+ /* in case of disabled rules: ~d, aka -d - 1 */
+ Id w1, w2; /* watches, literals not-yet-decided */
+ /* if !w2, assertion, not rule */
+ Id n1, n2; /* next rules in linked list, corresponding to w1, w2 */
+} Rule;
+
+
+typedef enum {
+ SOLVER_RULE_UNKNOWN = 0,
+ SOLVER_RULE_PKG = 0x100,
+ SOLVER_RULE_PKG_NOT_INSTALLABLE,
+ SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP,
+ SOLVER_RULE_PKG_REQUIRES,
+ SOLVER_RULE_PKG_SELF_CONFLICT,
+ SOLVER_RULE_PKG_CONFLICTS,
+ SOLVER_RULE_PKG_SAME_NAME,
+ SOLVER_RULE_PKG_OBSOLETES,
+ SOLVER_RULE_PKG_IMPLICIT_OBSOLETES,
+ SOLVER_RULE_PKG_INSTALLED_OBSOLETES,
+ SOLVER_RULE_UPDATE = 0x200,
+ SOLVER_RULE_FEATURE = 0x300,
+ SOLVER_RULE_JOB = 0x400,
+ SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP,
+ SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM,
+ SOLVER_RULE_JOB_UNKNOWN_PACKAGE,
+ SOLVER_RULE_JOB_UNSUPPORTED,
+ SOLVER_RULE_DISTUPGRADE = 0x500,
+ SOLVER_RULE_INFARCH = 0x600,
+ SOLVER_RULE_CHOICE = 0x700,
+ SOLVER_RULE_LEARNT = 0x800,
+ SOLVER_RULE_BEST = 0x900,
+ SOLVER_RULE_YUMOBS = 0xa00
+} SolverRuleinfo;
+
+#define SOLVER_RULE_TYPEMASK 0xff00
+
+struct _Solver;
+
+/*-------------------------------------------------------------------
+ * disable rule
+ */
+
+static inline void
+solver_disablerule(struct _Solver *solv, Rule *r)
+{
+ if (r->d >= 0)
+ r->d = -r->d - 1;
+}
+
+/*-------------------------------------------------------------------
+ * enable rule
+ */
+
+static inline void
+solver_enablerule(struct _Solver *solv, Rule *r)
+{
+ if (r->d < 0)
+ r->d = -r->d - 1;
+}
+
+extern Rule *solver_addrule(struct _Solver *solv, Id p, Id p2, Id d);
+extern void solver_unifyrules(struct _Solver *solv);
+extern int solver_rulecmp(struct _Solver *solv, Rule *r1, Rule *r2);
+extern void solver_shrinkrules(struct _Solver *solv, int nrules);
+
+/* pkg rules */
+extern void solver_addpkgrulesforsolvable(struct _Solver *solv, Solvable *s, Map *m);
+extern void solver_addpkgrulesforweak(struct _Solver *solv, Map *m);
+extern void solver_addpkgrulesforlinked(struct _Solver *solv, Map *m);
+extern void solver_addpkgrulesforupdaters(struct _Solver *solv, Solvable *s, Map *m, int allow_all);
+
+/* update/feature rules */
+extern void solver_addupdaterule(struct _Solver *solv, Solvable *s, int allow_all);
+
+/* infarch rules */
+extern void solver_addinfarchrules(struct _Solver *solv, Map *addedmap);
+
+/* dup rules */
+extern void solver_createdupmaps(struct _Solver *solv);
+extern void solver_freedupmaps(struct _Solver *solv);
+extern void solver_addduprules(struct _Solver *solv, Map *addedmap);
+
+/* choice rules */
+extern void solver_addchoicerules(struct _Solver *solv);
+extern void solver_disablechoicerules(struct _Solver *solv, Rule *r);
+
+/* best rules */
+extern void solver_addbestrules(struct _Solver *solv, int havebestinstalljobs);
+
+/* yumobs rules */
+extern void solver_addyumobsrules(struct _Solver *solv);
+
+/* policy rule disabling/reenabling */
+extern void solver_disablepolicyrules(struct _Solver *solv);
+extern void solver_reenablepolicyrules(struct _Solver *solv, int jobidx);
+extern void solver_reenablepolicyrules_cleandeps(struct _Solver *solv, Id pkg);
+
+/* rule info */
+extern int solver_allruleinfos(struct _Solver *solv, Id rid, Queue *rq);
+extern SolverRuleinfo solver_ruleinfo(struct _Solver *solv, Id rid, Id *fromp, Id *top, Id *depp);
+extern SolverRuleinfo solver_ruleclass(struct _Solver *solv, Id rid);
+extern void solver_ruleliterals(struct _Solver *solv, Id rid, Queue *q);
+extern int solver_rule2jobidx(struct _Solver *solv, Id rid);
+extern Id solver_rule2job(struct _Solver *solv, Id rid, Id *whatp);
+extern Id solver_rule2solvable(struct _Solver *solv, Id rid);
+extern void solver_rule2rules(struct _Solver *solv, Id rid, Queue *q, int recursive);
+extern Id solver_rule2pkgrule(struct _Solver *solv, Id rid);
+
+/* orphan handling */
+extern void solver_breakorphans(struct _Solver *solv);
+extern void solver_check_brokenorphanrules(struct _Solver *solv, Queue *dq);
+
+
+/* legacy */
+#define SOLVER_RULE_RPM SOLVER_RULE_PKG
+#define SOLVER_RULE_RPM_NOT_INSTALLABLE SOLVER_RULE_PKG_NOT_INSTALLABLE
+#define SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP
+#define SOLVER_RULE_RPM_PACKAGE_REQUIRES SOLVER_RULE_PKG_REQUIRES
+#define SOLVER_RULE_RPM_SELF_CONFLICT SOLVER_RULE_PKG_SELF_CONFLICT
+#define SOLVER_RULE_RPM_PACKAGE_CONFLICT SOLVER_RULE_PKG_CONFLICTS
+#define SOLVER_RULE_RPM_SAME_NAME SOLVER_RULE_PKG_SAME_NAME
+#define SOLVER_RULE_RPM_PACKAGE_OBSOLETES SOLVER_RULE_PKG_OBSOLETES
+#define SOLVER_RULE_RPM_IMPLICIT_OBSOLETES SOLVER_RULE_PKG_IMPLICIT_OBSOLETES
+#define SOLVER_RULE_RPM_INSTALLEDPKG_OBSOLETES SOLVER_RULE_PKG_INSTALLED_OBSOLETES
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * selection.c
+ *
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <fnmatch.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "selection.h"
+#include "solver.h"
+#include "evr.h"
+
+
+static int
+str2archid(Pool *pool, const char *arch)
+{
+ Id id;
+ if (!*arch)
+ return 0;
+ id = pool_str2id(pool, arch, 0);
+ if (!id || id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
+ return id;
+ if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
+ return 0;
+ return id;
+}
+
+static void
+selection_prune(Pool *pool, Queue *selection)
+{
+ int i, j;
+ Id p, pp;
+ for (i = j = 0; i < selection->count; i += 2)
+ {
+ Id select = selection->elements[i] & SOLVER_SELECTMASK;
+ p = 0;
+ if (select == SOLVER_SOLVABLE_ALL)
+ p = 1;
+ else if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Solvable *s;
+ Repo *repo = pool_id2repo(pool, selection->elements[i + 1]);
+ if (repo)
+ FOR_REPO_SOLVABLES(repo, p, s)
+ break;
+ }
+ else
+ {
+ FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1])
+ break;
+ }
+ if (!p)
+ continue;
+ selection->elements[j] = selection->elements[i];
+ selection->elements[j + 1] = selection->elements[i + 1];
+ j += 2;
+ }
+ queue_truncate(selection, j);
+}
+
+
+static int
+selection_solvables_sortcmp(const void *ap, const void *bp, void *dp)
+{
+ return *(const Id *)ap - *(const Id *)bp;
+}
+
+void
+selection_solvables(Pool *pool, Queue *selection, Queue *pkgs)
+{
+ int i, j;
+ Id p, pp, lastid;
+ queue_empty(pkgs);
+ for (i = 0; i < selection->count; i += 2)
+ {
+ Id select = selection->elements[i] & SOLVER_SELECTMASK;
+ if (select == SOLVER_SOLVABLE_ALL)
+ {
+ FOR_POOL_SOLVABLES(p)
+ queue_push(pkgs, p);
+ }
+ if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Solvable *s;
+ Repo *repo = pool_id2repo(pool, selection->elements[i + 1]);
+ if (repo)
+ FOR_REPO_SOLVABLES(repo, p, s)
+ queue_push(pkgs, p);
+ }
+ else
+ {
+ FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1])
+ queue_push(pkgs, p);
+ }
+ }
+ if (pkgs->count < 2)
+ return;
+ /* sort and unify */
+ solv_sort(pkgs->elements, pkgs->count, sizeof(Id), selection_solvables_sortcmp, NULL);
+ lastid = pkgs->elements[0];
+ for (i = j = 1; i < pkgs->count; i++)
+ if (pkgs->elements[i] != lastid)
+ pkgs->elements[j++] = lastid = pkgs->elements[i];
+ queue_truncate(pkgs, j);
+}
+
+static void
+selection_flatten(Pool *pool, Queue *selection)
+{
+ Queue q;
+ int i;
+ if (selection->count <= 2)
+ return;
+ for (i = 0; i < selection->count; i += 2)
+ if ((selection->elements[i] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
+ {
+ selection->elements[0] = selection->elements[i];
+ selection->elements[1] = selection->elements[i + 1];
+ queue_truncate(selection, 2);
+ return;
+ }
+ queue_init(&q);
+ selection_solvables(pool, selection, &q);
+ if (!q.count)
+ {
+ queue_empty(selection);
+ return;
+ }
+ queue_truncate(selection, 2);
+ if (q.count > 1)
+ {
+ selection->elements[0] = SOLVER_SOLVABLE_ONE_OF;
+ selection->elements[1] = pool_queuetowhatprovides(pool, &q);
+ }
+ else
+ {
+ selection->elements[0] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+ selection->elements[1] = q.elements[0];
+ }
+}
+
+static void
+selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
+{
+ int i;
+
+ for (i = 0; i < selection->count; i += 2)
+ {
+ Id select = selection->elements[i] & SOLVER_SELECTMASK;
+ Id id = selection->elements[i + 1];
+ if (select == SOLVER_SOLVABLE || select == SOLVER_SOLVABLE_ONE_OF)
+ {
+ /* done by selection_addsrc, currently implies SELECTION_NAME */
+ Queue q;
+ Id p, pp;
+ Id rel = 0, relname = 0;
+ int miss = 0;
+
+ queue_init(&q);
+ FOR_JOB_SELECT(p, pp, select, id)
+ {
+ Solvable *s = pool->solvables + p;
+ if (!rel || s->name != relname)
+ {
+ relname = s->name;
+ rel = pool_rel2id(pool, relname, relevr, relflags, 1);
+ }
+ if (pool_match_nevr(pool, s, rel))
+ queue_push(&q, p);
+ else
+ miss = 1;
+ }
+ if (miss)
+ {
+ if (q.count == 1)
+ {
+ selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+ selection->elements[i + 1] = q.elements[0];
+ }
+ else
+ {
+ selection->elements[i] = SOLVER_SOLVABLE_ONE_OF;
+ selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q);
+ }
+ }
+ queue_free(&q);
+ }
+ else if (select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES)
+ {
+ /* don't stack src reldeps */
+ if (relflags == REL_ARCH && (relevr == ARCH_SRC || relevr == ARCH_NOSRC) && ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ if (rd->flags == REL_ARCH && rd->evr == ARCH_SRC)
+ id = rd->name;
+ }
+ selection->elements[i + 1] = pool_rel2id(pool, id, relevr, relflags, 1);
+ }
+ else
+ continue; /* actually internal error */
+ if (relflags == REL_ARCH)
+ selection->elements[i] |= SOLVER_SETARCH;
+ if (relflags == REL_EQ && select != SOLVER_SOLVABLE_PROVIDES)
+ {
+ if (pool->disttype == DISTTYPE_DEB)
+ selection->elements[i] |= SOLVER_SETEVR; /* debian can't match version only like rpm */
+ else
+ {
+ const char *rel = strrchr(pool_id2str(pool, relevr), '-');
+ selection->elements[i] |= rel ? SOLVER_SETEVR : SOLVER_SETEV;
+ }
+ }
+ }
+ selection_prune(pool, selection);
+}
+
+static void
+selection_filter_installed(Pool *pool, Queue *selection)
+{
+ Queue q;
+ int i, j;
+
+ if (!pool->installed)
+ queue_empty(selection);
+ queue_init(&q);
+ for (i = j = 0; i < selection->count; i += 2)
+ {
+ Id select = selection->elements[i] & SOLVER_SELECTMASK;
+ Id id = selection->elements[i + 1];
+ if (select == SOLVER_SOLVABLE_ALL)
+ {
+ select = SOLVER_SOLVABLE_REPO;
+ id = pool->installed->repoid;
+ }
+ else if (select == SOLVER_SOLVABLE_REPO)
+ {
+ if (id != pool->installed->repoid)
+ select = 0;
+ }
+ else
+ {
+ int bad = 0;
+ Id p, pp;
+ queue_empty(&q);
+ FOR_JOB_SELECT(p, pp, select, id)
+ {
+ if (pool->solvables[p].repo != pool->installed)
+ bad = 1;
+ else
+ queue_push(&q, p);
+ }
+ if (bad || !q.count)
+ {
+ if (!q.count)
+ select = 0;
+ else if (q.count == 1)
+ {
+ select = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+ id = q.elements[0];
+ }
+ else
+ {
+ select = SOLVER_SOLVABLE_ONE_OF;
+ id = pool_queuetowhatprovides(pool, &q);
+ }
+ }
+ }
+ if (select)
+ {
+ selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SETREPO;
+ selection->elements[j++] = id;
+ }
+ }
+ queue_truncate(selection, j);
+ queue_free(&q);
+}
+
+static void
+selection_addsrc(Pool *pool, Queue *selection, int flags)
+{
+ Queue q;
+ Id p, name;
+ int i, havesrc;
+
+ if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+ return; /* sources can't be installed */
+ queue_init(&q);
+ for (i = 0; i < selection->count; i += 2)
+ {
+ if (selection->elements[i] != SOLVER_SOLVABLE_NAME)
+ continue;
+ name = selection->elements[i + 1];
+ havesrc = 0;
+ queue_empty(&q);
+ FOR_POOL_SOLVABLES(p)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->name != name)
+ continue;
+ if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+ {
+ if (pool_disabled_solvable(pool, s))
+ continue;
+ havesrc = 1;
+ }
+ else if (s->repo != pool->installed && !pool_installable(pool, s))
+ continue;
+ queue_push(&q, p);
+ }
+ if (!havesrc || !q.count)
+ continue;
+ if (q.count == 1)
+ {
+ selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+ selection->elements[i + 1] = q.elements[0];
+ }
+ else
+ {
+ selection->elements[i] = SOLVER_SOLVABLE_ONE_OF;
+ selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q);
+ }
+ }
+ queue_free(&q);
+}
+
+static inline const char *
+skipkind(const char *n)
+{
+ const char *s;
+ for (s = n; *s >= 'a' && *s <= 'z'; s++)
+ ;
+ if (*s == ':' && s != n)
+ return s + 1;
+ return n;
+}
+
+static inline void
+queue_pushunique2(Queue *q, Id id1, Id id2)
+{
+ int i;
+ for (i = 0; i < q->count; i += 2)
+ if (q->elements[i] == id1 && q->elements[i + 1] == id2)
+ return;
+ queue_push2(q, id1, id2);
+}
+
+static int
+selection_depglob_id(Pool *pool, Queue *selection, Id id, int flags)
+{
+ Id p, pp;
+ int match = 0;
+
+ FOR_PROVIDES(p, pp, id)
+ {
+ Solvable *s = pool->solvables + p;
+ if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+ continue;
+ match = 1;
+ if (s->name == id && (flags & SELECTION_NAME) != 0)
+ {
+ if ((flags & SELECTION_SOURCE_ONLY) != 0)
+ id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+ queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
+ if ((flags & SELECTION_WITH_SOURCE) != 0)
+ selection_addsrc(pool, selection, flags);
+ return SELECTION_NAME;
+ }
+ }
+ if ((flags & (SELECTION_SOURCE_ONLY | SELECTION_WITH_SOURCE)) != 0 && (flags & SELECTION_NAME) != 0)
+ {
+ /* src rpms don't have provides, so we must check every solvable */
+ FOR_POOL_SOLVABLES(p) /* slow path */
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->name == id && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC))
+ {
+ if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+ continue; /* just in case... src rpms can't be installed */
+ if (pool_disabled_solvable(pool, s))
+ continue;
+ if ((flags & SELECTION_SOURCE_ONLY) != 0)
+ id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+ queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
+ if ((flags & SELECTION_WITH_SOURCE) != 0)
+ selection_addsrc(pool, selection, flags);
+ return SELECTION_NAME;
+ }
+ }
+ }
+ if (match && (flags & SELECTION_PROVIDES) != 0)
+ {
+ queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
+ return SELECTION_PROVIDES;
+ }
+ return 0;
+}
+
+static int
+selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
+{
+ Id id, p, pp;
+ int match = 0;
+ int doglob = 0;
+ int nocase = 0;
+ int globflags = 0;
+
+ if ((flags & SELECTION_SOURCE_ONLY) != 0)
+ {
+ flags &= ~SELECTION_PROVIDES; /* sources don't provide anything */
+ flags &= ~SELECTION_WITH_SOURCE;
+ }
+
+ if (!(flags & (SELECTION_NAME|SELECTION_PROVIDES)))
+ return 0;
+
+ if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed)
+ return 0;
+
+ nocase = flags & SELECTION_NOCASE;
+ if (!nocase && !(flags & SELECTION_SKIP_KIND))
+ {
+ id = pool_str2id(pool, name, 0);
+ if (id)
+ {
+ /* the id is know, do the fast id matching using the whatprovides lookup */
+ int ret = selection_depglob_id(pool, selection, id, flags);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if ((flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0)
+ doglob = 1;
+
+ if (!nocase && !(flags & SELECTION_SKIP_KIND) && !doglob)
+ return 0; /* all done above in depglob_id */
+
+ if (doglob && nocase)
+ globflags = FNM_CASEFOLD;
+
+ if ((flags & SELECTION_NAME) != 0)
+ {
+ /* looks like a name glob. hard work. */
+ FOR_POOL_SOLVABLES(p)
+ {
+ Solvable *s = pool->solvables + p;
+ const char *n;
+ if (s->repo != pool->installed && !pool_installable(pool, s))
+ {
+ if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
+ continue;
+ if (pool_disabled_solvable(pool, s))
+ continue;
+ }
+ if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+ continue;
+ id = s->name;
+ n = pool_id2str(pool, id);
+ if (flags & SELECTION_SKIP_KIND)
+ n = skipkind(n);
+ if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
+ {
+ if ((flags & SELECTION_SOURCE_ONLY) != 0)
+ id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+ queue_pushunique2(selection, SOLVER_SOLVABLE_NAME, id);
+ match = 1;
+ }
+ }
+ if (match)
+ {
+ if ((flags & SELECTION_WITH_SOURCE) != 0)
+ selection_addsrc(pool, selection, flags);
+ return SELECTION_NAME;
+ }
+ }
+
+ if ((flags & SELECTION_PROVIDES))
+ {
+ /* looks like a dep glob. really hard work. */
+ for (id = 1; id < pool->ss.nstrings; id++)
+ {
+ const char *n;
+ if (!pool->whatprovides[id] || pool->whatprovides[id] == 1)
+ continue;
+ n = pool_id2str(pool, id);
+ if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
+ {
+ if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+ {
+ FOR_PROVIDES(p, pp, id)
+ if (pool->solvables[p].repo == pool->installed)
+ break;
+ if (!p)
+ continue;
+ }
+ queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
+ match = 1;
+ }
+ }
+ if (match)
+ return SELECTION_PROVIDES;
+ }
+ return 0;
+}
+
+static int
+selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags)
+{
+ int ret;
+ const char *r;
+ Id archid;
+
+ if ((ret = selection_depglob(pool, selection, name, flags)) != 0)
+ return ret;
+ if (!(flags & SELECTION_DOTARCH))
+ return 0;
+ /* check if there is an .arch suffix */
+ if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
+ {
+ char *rname = solv_strdup(name);
+ rname[r - name] = 0;
+ if (archid == ARCH_SRC || archid == ARCH_NOSRC)
+ flags |= SELECTION_SOURCE_ONLY;
+ if ((ret = selection_depglob(pool, selection, rname, flags)) != 0)
+ {
+ selection_filter_rel(pool, selection, REL_ARCH, archid);
+ solv_free(rname);
+ return ret | SELECTION_DOTARCH;
+ }
+ solv_free(rname);
+ }
+ return 0;
+}
+
+static int
+selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
+{
+ Dataiterator di;
+ Queue q;
+ int type;
+
+ /* all files in the file list start with a '/' */
+ if (*name != '/')
+ {
+ if (!(flags & SELECTION_GLOB))
+ return 0;
+ if (*name != '*' && *name != '[' && *name != '?')
+ return 0;
+ }
+ type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
+ if ((flags & SELECTION_NOCASE) != 0)
+ type |= SEARCH_NOCASE;
+ queue_init(&q);
+ dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
+ while (dataiterator_step(&di))
+ {
+ Solvable *s = pool->solvables + di.solvid;
+ if (!s->repo)
+ continue;
+ if (s->repo != pool->installed && !pool_installable(pool, s))
+ {
+ if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
+ continue;
+ if (pool_disabled_solvable(pool, s))
+ continue;
+ }
+ if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+ continue;
+ queue_push(&q, di.solvid);
+ dataiterator_skip_solvable(&di);
+ }
+ dataiterator_free(&di);
+ if (!q.count)
+ return 0;
+ if (q.count > 1)
+ queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
+ else
+ queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
+ queue_free(&q);
+ return SELECTION_FILELIST;
+}
+
+static char *
+splitrel(char *rname, char *r, int *rflagsp)
+{
+ int nend = r - rname;
+ int rflags = 0;
+ if (nend && *r == '=' && r[-1] == '!')
+ {
+ nend--;
+ r++;
+ rflags = REL_LT|REL_GT;
+ }
+ for (; *r; r++)
+ {
+ if (*r == '<')
+ rflags |= REL_LT;
+ else if (*r == '=')
+ rflags |= REL_EQ;
+ else if (*r == '>')
+ rflags |= REL_GT;
+ else
+ break;
+ }
+ while (*r && (*r == ' ' || *r == '\t'))
+ r++;
+ while (nend && (rname[nend - 1] == ' ' || rname[nend - 1] == '\t'))
+ nend--;
+ if (!*rname || !*r)
+ return 0;
+ *rflagsp = rflags;
+ rname[nend] = 0;
+ return r;
+}
+
+static int
+selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
+{
+ int ret, rflags = 0;
+ char *r, *rname;
+
+ /* relation case, support:
+ * depglob rel
+ * depglob.arch rel
+ */
+ rname = solv_strdup(name);
+ if ((r = strpbrk(rname, "<=>")) != 0)
+ {
+ if ((r = splitrel(rname, r, &rflags)) == 0)
+ {
+ solv_free(rname);
+ return 0;
+ }
+ }
+ if ((ret = selection_depglob_arch(pool, selection, rname, flags)) != 0)
+ {
+ if (rflags)
+ selection_filter_rel(pool, selection, rflags, pool_str2id(pool, r, 1));
+ solv_free(rname);
+ return ret | SELECTION_REL;
+ }
+ solv_free(rname);
+ return 0;
+}
+
+#if defined(MULTI_SEMANTICS)
+# define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE)
+#elif defined(DEBIAN)
+# define EVRCMP_DEPCMP EVRCMP_COMPARE
+#else
+# define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE
+#endif
+
+/* magic epoch promotion code, works only for SELECTION_NAME selections */
+static void
+selection_filter_evr(Pool *pool, Queue *selection, char *evr)
+{
+ int i, j;
+ Queue q;
+ Id qbuf[10];
+
+ queue_init(&q);
+ queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
+ for (i = j = 0; i < selection->count; i += 2)
+ {
+ Id select = selection->elements[i] & SOLVER_SELECTMASK;
+ Id id = selection->elements[i + 1];
+ Id p, pp;
+ const char *lastepoch = 0;
+ int lastepochlen = 0;
+
+ queue_empty(&q);
+ FOR_JOB_SELECT(p, pp, select, id)
+ {
+ Solvable *s = pool->solvables + p;
+ const char *sevr = pool_id2str(pool, s->evr);
+ const char *sp;
+ for (sp = sevr; *sp >= '0' && *sp <= '9'; sp++)
+ ;
+ if (*sp != ':')
+ sp = sevr;
+ /* compare vr part */
+ if (strcmp(evr, sp != sevr ? sp + 1 : sevr) != 0)
+ {
+ int r = pool_evrcmp_str(pool, sp != sevr ? sp + 1 : sevr, evr, EVRCMP_DEPCMP);
+ if (r == -1 || r == 1)
+ continue; /* solvable does not match vr */
+ }
+ queue_push(&q, p);
+ if (sp > sevr)
+ {
+ while (sevr < sp && *sevr == '0') /* normalize epoch */
+ sevr++;
+ }
+ if (!lastepoch)
+ {
+ lastepoch = sevr;
+ lastepochlen = sp - sevr;
+ }
+ else if (lastepochlen != sp - sevr || strncmp(lastepoch, sevr, lastepochlen) != 0)
+ lastepochlen = -1; /* multiple different epochs */
+ }
+ if (!lastepoch || lastepochlen == 0)
+ id = pool_str2id(pool, evr, 1); /* no match at all or zero epoch */
+ else if (lastepochlen >= 0)
+ {
+ /* found exactly one epoch, simply prepend */
+ char *evrx = solv_malloc(strlen(evr) + lastepochlen + 2);
+ strncpy(evrx, lastepoch, lastepochlen + 1);
+ strcpy(evrx + lastepochlen + 1, evr);
+ id = pool_str2id(pool, evrx, 1);
+ solv_free(evrx);
+ }
+ else
+ {
+ /* multiple epochs in multiple solvables, convert to list of solvables */
+ selection->elements[j] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF;
+ selection->elements[j + 1] = pool_queuetowhatprovides(pool, &q);
+ j += 2;
+ continue;
+ }
+ queue_empty(&q);
+ queue_push2(&q, selection->elements[i], selection->elements[i + 1]);
+ selection_filter_rel(pool, &q, REL_EQ, id);
+ if (!q.count)
+ continue; /* oops, no match */
+ selection->elements[j] = q.elements[0];
+ selection->elements[j + 1] = q.elements[1];
+ j += 2;
+ }
+ queue_truncate(selection, j);
+ queue_free(&q);
+}
+
+/* match the "canonical" name of the package */
+static int
+selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
+{
+ char *rname, *r, *r2;
+ Id archid = 0;
+ int ret;
+
+ /*
+ * nameglob-version
+ * nameglob-version.arch
+ * nameglob-version-release
+ * nameglob-version-release.arch
+ */
+ flags |= SELECTION_NAME;
+ flags &= ~SELECTION_PROVIDES;
+
+ if (pool->disttype == DISTTYPE_DEB)
+ {
+ if ((r = strchr(name, '_')) == 0)
+ return 0;
+ rname = solv_strdup(name); /* so we can modify it */
+ r = rname + (r - name);
+ *r++ = 0;
+ if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+ {
+ solv_free(rname);
+ return 0;
+ }
+ /* is there a vaild arch? */
+ if ((r2 = strrchr(r, '_')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
+ {
+ *r2 = 0; /* split off */
+ selection_filter_rel(pool, selection, REL_ARCH, archid);
+ }
+ selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
+ solv_free(rname);
+ return ret | SELECTION_CANON;
+ }
+
+ if (pool->disttype == DISTTYPE_HAIKU)
+ {
+ if ((r = strchr(name, '-')) == 0)
+ return 0;
+ rname = solv_strdup(name); /* so we can modify it */
+ r = rname + (r - name);
+ *r++ = 0;
+ if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+ {
+ solv_free(rname);
+ return 0;
+ }
+ /* is there a vaild arch? */
+ if ((r2 = strrchr(r, '-')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
+ {
+ *r2 = 0; /* split off */
+ selection_filter_rel(pool, selection, REL_ARCH, archid);
+ }
+ selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
+ solv_free(rname);
+ return ret | SELECTION_CANON;
+ }
+
+ if ((r = strrchr(name, '-')) == 0)
+ return 0;
+ rname = solv_strdup(name); /* so we can modify it */
+ r = rname + (r - name);
+ *r = 0;
+
+ /* split off potential arch part from version */
+ if ((r2 = strrchr(r + 1, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
+ *r2 = 0; /* found valid arch, split it off */
+ if (archid == ARCH_SRC || archid == ARCH_NOSRC)
+ flags |= SELECTION_SOURCE_ONLY;
+
+ /* try with just the version */
+ if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+ {
+ /* no luck, try with version-release */
+ if ((r2 = strrchr(rname, '-')) == 0)
+ {
+ solv_free(rname);
+ return 0;
+ }
+ *r = '-';
+ *r2 = 0;
+ r = r2;
+ if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+ {
+ solv_free(rname);
+ return 0;
+ }
+ }
+ if (archid)
+ selection_filter_rel(pool, selection, REL_ARCH, archid);
+ selection_filter_evr(pool, selection, r + 1); /* magic epoch promotion */
+ solv_free(rname);
+ return ret | SELECTION_CANON;
+}
+
+int
+selection_make(Pool *pool, Queue *selection, const char *name, int flags)
+{
+ int ret = 0;
+
+ queue_empty(selection);
+ if ((flags & SELECTION_FILELIST) != 0)
+ ret = selection_filelist(pool, selection, name, flags);
+ if (!ret && (flags & SELECTION_REL) != 0 && strpbrk(name, "<=>") != 0)
+ ret = selection_rel(pool, selection, name, flags);
+ if (!ret)
+ ret = selection_depglob_arch(pool, selection, name, flags);
+ if (!ret && (flags & SELECTION_CANON) != 0)
+ ret = selection_canon(pool, selection, name, flags);
+ if (selection->count && (flags & SELECTION_INSTALLED_ONLY) != 0)
+ selection_filter_installed(pool, selection);
+ if (ret && !selection->count)
+ ret = 0; /* no match -> always return zero */
+ if (ret && (flags & SELECTION_FLAT) != 0)
+ selection_flatten(pool, selection);
+ return ret;
+}
+
+static int
+matchdep(Pool *pool, Id id, char *rname, int rflags, char *revr, int flags)
+{
+ if (ISRELDEP(id))
+ {
+ Reldep *rd = GETRELDEP(pool, id);
+ if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH)
+ {
+ if (matchdep(pool, rd->name, rname, rflags, revr, flags))
+ return 1;
+ if (matchdep(pool, rd->evr, rname, rflags, revr, flags))
+ return 1;
+ return 0;
+ }
+ if (rd->flags == REL_ARCH)
+ return matchdep(pool, rd->name, rname, rflags, revr, flags);
+ if (!matchdep(pool, rd->name, rname, rflags, revr, flags))
+ return 0;
+ if (rflags)
+ {
+ /* XXX: need pool_match_flags_evr here */
+ if (!pool_match_dep(pool, pool_rel2id(pool, rd->name, pool_str2id(pool, revr, 1), rflags, 1), id))
+ return 0;
+ }
+ return 1;
+ }
+ if (flags & SELECTION_GLOB)
+ {
+ int globflags = (flags & SELECTION_NOCASE) != 0 ? FNM_CASEFOLD : 0;
+ return fnmatch(rname, pool_id2str(pool, id), globflags) == 0 ? 1 : 0;
+ }
+ if (flags & SELECTION_NOCASE)
+ return strcasecmp(rname, pool_id2str(pool, id)) == 0 ? 1 : 0;
+ return strcmp(rname, pool_id2str(pool, id)) == 0 ? 1 : 0;
+}
+
+/*
+ * select against the dependencies in keyname
+ * like SELECTION_REL and SELECTION_PROVIDES, but with the
+ * deps in keyname instead of provides.
+ */
+int
+selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker)
+{
+ char *rname, *r;
+ int rflags = 0;
+ Id p;
+ Queue q;
+
+ queue_empty(selection);
+ rname = solv_strdup(name);
+ if ((r = strpbrk(rname, "<=>")) != 0)
+ {
+ if ((r = splitrel(rname, r, &rflags)) == 0)
+ {
+ solv_free(rname);
+ return 0;
+ }
+ }
+ if ((flags & SELECTION_GLOB) != 0 && !strpbrk(name, "[*?") != 0)
+ flags &= ~SELECTION_GLOB;
+
+ queue_init(&q);
+ FOR_POOL_SOLVABLES(p)
+ {
+ Solvable *s = pool->solvables + p;
+ int i;
+
+ if (s->repo != pool->installed && !pool_installable(pool, s))
+ {
+ if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
+ continue;
+ if (pool_disabled_solvable(pool, s))
+ continue;
+ }
+ if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+ continue;
+ if ((s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) && !(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
+ continue;
+ queue_empty(&q);
+ repo_lookup_deparray(s->repo, p, keyname, &q, marker);
+ for (i = 0; i < q.count; i++)
+ {
+ Id id = q.elements[i];
+ if (matchdep(pool, id, rname, rflags, r, flags))
+ break;
+ }
+ if (i < q.count)
+ queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, p);
+ }
+ queue_free(&q);
+ solv_free(rname);
+ if (!selection->count)
+ return 0;
+ if ((flags & SELECTION_FLAT) != 0)
+ selection_flatten(pool, selection);
+ return SELECTION_PROVIDES;
+}
+
+static inline int
+pool_is_kind(Pool *pool, Id name, Id kind)
+{
+ const char *n;
+ if (!kind)
+ return 1;
+ n = pool_id2str(pool, name);
+ if (kind != 1)
+ {
+ const char *kn = pool_id2str(pool, kind);
+ int knl = strlen(kn);
+ return !strncmp(n, kn, knl) && n[knl] == ':' ? 1 : 0;
+ }
+ else
+ {
+ if (*n == ':')
+ return 1;
+ while(*n >= 'a' && *n <= 'z')
+ n++;
+ return *n == ':' ? 0 : 1;
+ }
+}
+
+void
+selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
+{
+ int i, j, miss;
+ Id p, pp, q1filled = 0;
+ Queue q1;
+ Map m2;
+ Id setflags = 0;
+
+ if (!sel1->count || !sel2->count)
+ {
+ queue_empty(sel1);
+ return;
+ }
+ if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
+ {
+ /* XXX: not 100% correct, but very useful */
+ p = sel1->elements[0] & ~(SOLVER_SELECTMASK | SOLVER_SETMASK); /* job & jobflags */
+ queue_free(sel1);
+ queue_init_clone(sel1, sel2);
+ for (i = 0; i < sel1->count; i += 2)
+ sel1->elements[i] = (sel1->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | p ;
+ return;
+ }
+ queue_init(&q1);
+ map_init(&m2, pool->nsolvables);
+ for (i = 0; i < sel2->count; i += 2)
+ {
+ Id select = sel2->elements[i] & SOLVER_SELECTMASK;
+ if (select == SOLVER_SOLVABLE_ALL)
+ {
+ queue_free(&q1);
+ map_free(&m2);
+ return;
+ }
+ if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Solvable *s;
+ Repo *repo = pool_id2repo(pool, sel2->elements[i + 1]);
+ if (repo)
+ FOR_REPO_SOLVABLES(repo, p, s)
+ map_set(&m2, p);
+ }
+ else
+ {
+ if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(sel2->elements[i + 1]))
+ {
+ Reldep *rd = GETRELDEP(pool, sel2->elements[i + 1]);
+ if (rd->flags == REL_ARCH && rd->name == 0)
+ {
+ /* special arch filter */
+ if (!q1filled++)
+ selection_solvables(pool, sel1, &q1);
+ for (j = 0; j < q1.count; j++)
+ {
+ Id p = q1.elements[j];
+ Solvable *s = pool->solvables + p;
+ if (s->arch == rd->evr || (rd->evr == ARCH_SRC && s->arch == ARCH_NOSRC))
+ map_set(&m2, p);
+ }
+ continue;
+ }
+ else if (rd->flags == REL_KIND && rd->name == 0)
+ {
+ /* special kind filter */
+ if (!q1filled++)
+ selection_solvables(pool, sel1, &q1);
+ for (j = 0; j < q1.count; j++)
+ {
+ Id p = q1.elements[j];
+ Solvable *s = pool->solvables + p;
+ if (pool_is_kind(pool, s->name, rd->evr))
+ map_set(&m2, p);
+ }
+ continue;
+ }
+ }
+ FOR_JOB_SELECT(p, pp, select, sel2->elements[i + 1])
+ map_set(&m2, p);
+ }
+ }
+ if (sel2->count == 2) /* XXX: AND all setmasks instead? */
+ setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET;
+ for (i = j = 0; i < sel1->count; i += 2)
+ {
+ Id select = sel1->elements[i] & SOLVER_SELECTMASK;
+ queue_empty(&q1);
+ miss = 0;
+ if (select == SOLVER_SOLVABLE_ALL)
+ {
+ FOR_POOL_SOLVABLES(p)
+ {
+ if (map_tst(&m2, p))
+ queue_push(&q1, p);
+ else
+ miss = 1;
+ }
+ }
+ else if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Solvable *s;
+ Repo *repo = pool_id2repo(pool, sel1->elements[i + 1]);
+ if (repo)
+ FOR_REPO_SOLVABLES(repo, p, s)
+ {
+ if (map_tst(&m2, p))
+ queue_push(&q1, p);
+ else
+ miss = 1;
+ }
+ }
+ else
+ {
+ FOR_JOB_SELECT(p, pp, select, sel1->elements[i + 1])
+ {
+ if (map_tst(&m2, p))
+ queue_pushunique(&q1, p);
+ else
+ miss = 1;
+ }
+ }
+ if (!q1.count)
+ continue;
+ if (!miss)
+ {
+ sel1->elements[j] = sel1->elements[i] | setflags;
+ sel1->elements[j + 1] = sel1->elements[i + 1];
+ }
+ else if (q1.count > 1)
+ {
+ sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags;
+ sel1->elements[j + 1] = pool_queuetowhatprovides(pool, &q1);
+ }
+ else
+ {
+ sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags;
+ sel1->elements[j + 1] = q1.elements[0];
+ }
+ j += 2;
+ }
+ queue_truncate(sel1, j);
+ queue_free(&q1);
+ map_free(&m2);
+}
+
+void
+selection_add(Pool *pool, Queue *sel1, Queue *sel2)
+{
+ int i;
+ for (i = 0; i < sel2->count; i++)
+ queue_push(sel1, sel2->elements[i]);
+}
+
+const char *
+pool_selection2str(Pool *pool, Queue *selection, Id flagmask)
+{
+ char *s;
+ const char *s2;
+ int i;
+ s = pool_tmpjoin(pool, 0, 0, 0);
+ for (i = 0; i < selection->count; i += 2)
+ {
+ Id how = selection->elements[i];
+ if (*s)
+ s = pool_tmpappend(pool, s, " + ", 0);
+ s2 = solver_select2str(pool, how & SOLVER_SELECTMASK, selection->elements[i + 1]);
+ s = pool_tmpappend(pool, s, s2, 0);
+ pool_freetmpspace(pool, s2);
+ how &= flagmask & SOLVER_SETMASK;
+ if (how)
+ {
+ int o = strlen(s);
+ s = pool_tmpappend(pool, s, " ", 0);
+ if (how & SOLVER_SETEV)
+ s = pool_tmpappend(pool, s, ",setev", 0);
+ if (how & SOLVER_SETEVR)
+ s = pool_tmpappend(pool, s, ",setevr", 0);
+ if (how & SOLVER_SETARCH)
+ s = pool_tmpappend(pool, s, ",setarch", 0);
+ if (how & SOLVER_SETVENDOR)
+ s = pool_tmpappend(pool, s, ",setvendor", 0);
+ if (how & SOLVER_SETREPO)
+ s = pool_tmpappend(pool, s, ",setrepo", 0);
+ if (how & SOLVER_NOAUTOSET)
+ s = pool_tmpappend(pool, s, ",noautoset", 0);
+ if (s[o + 1] != ',')
+ s = pool_tmpappend(pool, s, ",?", 0);
+ s[o + 1] = '[';
+ s = pool_tmpappend(pool, s, "]", 0);
+ }
+ }
+ return s;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * selection.h
+ *
+ */
+
+#ifndef LIBSOLV_SELECTION_H
+#define LIBSOLV_SELECTION_H
+
+#include "pool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SELECTION_NAME (1 << 0)
+#define SELECTION_PROVIDES (1 << 1)
+#define SELECTION_FILELIST (1 << 2)
+#define SELECTION_CANON (1 << 3)
+#define SELECTION_DOTARCH (1 << 4)
+#define SELECTION_REL (1 << 5)
+
+#define SELECTION_INSTALLED_ONLY (1 << 8)
+#define SELECTION_GLOB (1 << 9)
+#define SELECTION_FLAT (1 << 10)
+#define SELECTION_NOCASE (1 << 11)
+#define SELECTION_SOURCE_ONLY (1 << 12)
+#define SELECTION_WITH_SOURCE (1 << 13)
+#define SELECTION_SKIP_KIND (1 << 14)
+
+extern int selection_make(Pool *pool, Queue *selection, const char *name, int flags);
+extern int selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker);
+
+extern void selection_filter(Pool *pool, Queue *sel1, Queue *sel2);
+extern void selection_add(Pool *pool, Queue *sel1, Queue *sel2);
+extern void selection_solvables(Pool *pool, Queue *selection, Queue *pkgs);
+
+extern const char *pool_selection2str(Pool *pool, Queue *selection, Id flagmask);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it. This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Modified 07/2002
+By Ralph Giles <giles@ghostscript.com>
+Still 100% public domain
+modified for use with stdint types, autoconf
+code cleanup, removed attribution comments
+switched SHA1Final() argument order for consistency
+use SHA1_ prefix for public api
+move public api to sha1.h
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "sha1.h"
+
+
+static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+/* FIXME: can we do this in an endian-proof way? */
+#ifdef WORDS_BIGENDIAN
+#define blk0(i) block.l[i]
+#else
+#define blk0(i) (block.l[i] = (rol(block.l[i],24)&0xFF00FF00) \
+ |(rol(block.l[i],8)&0x00FF00FF))
+#endif
+#define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15] \
+ ^block.l[(i+2)&15]^block.l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
+{
+ uint32_t a, b, c, d, e;
+ typedef union {
+ uint8_t c[64];
+ uint32_t l[16];
+ } CHAR64LONG16;
+ CHAR64LONG16 block;
+
+ memcpy(&block, buffer, 64);
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+void solv_SHA1_Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+void solv_SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
+{
+ size_t i, j;
+
+#ifdef VERBOSE
+ SHAPrintContext(context, "before");
+#endif
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1_Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1_Transform(context->state, data + i);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+
+#ifdef VERBOSE
+ SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+void solv_SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
+{
+ uint32_t i;
+ uint8_t finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ solv_SHA1_Update(context, (uint8_t *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ solv_SHA1_Update(context, (uint8_t *)"\0", 1);
+ }
+ solv_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+ digest[i] = (uint8_t)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+
+ /* Wipe variables */
+ i = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(finalcount, 0, 8); /* SWR */
+}
--- /dev/null
+/* public api for steve reid's public domain SHA-1 implementation */
+/* this file is in the public domain */
+
+#include <stdint.h>
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ uint8_t buffer[64];
+} SHA1_CTX;
+
+#define SHA1_DIGEST_SIZE 20
+
+void solv_SHA1_Init(SHA1_CTX* context);
+void solv_SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
+void solv_SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
--- /dev/null
+/*
+ * FILE: sha2.c
+ * AUTHOR: Aaron D. Gifford <me@aarongifford.com>
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
+ */
+
+#include <sys/types.h>
+#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
+/* #include <assert.h> */ /* assert() */
+#include <stdio.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "sha2.h"
+
+
+/*
+ * ASSERT NOTE:
+ * Some sanity checking code is included using assert(). On my FreeBSD
+ * system, this additional code can be removed by compiling with NDEBUG
+ * defined. Check your own systems manpage on assert() to see how to
+ * compile WITHOUT the sanity checking code on your system.
+ *
+ * UNROLLED TRANSFORM LOOP NOTE:
+ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
+ * loop version for the hash transform rounds (defined using macros
+ * later in this file). Either define on the command line, for example:
+ *
+ * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
+ *
+ * or define below:
+ *
+ * #define SHA2_UNROLL_TRANSFORM
+ *
+ */
+
+ #define SHA2_UNROLL_TRANSFORM
+
+
+/*** SHA-256/384/512 Machine Architecture Definitions *****************/
+/*
+ * BYTE_ORDER NOTE:
+ *
+ * Please make sure that your system defines BYTE_ORDER. If your
+ * architecture is little-endian, make sure it also defines
+ * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
+ * equivilent.
+ *
+ * If your system does not define the above, then you can do so by
+ * hand like this:
+ *
+ * #define LITTLE_ENDIAN 1234
+ * #define BIG_ENDIAN 4321
+ *
+ * And for little-endian machines, add:
+ *
+ * #define BYTE_ORDER LITTLE_ENDIAN
+ *
+ * Or for big-endian machines:
+ *
+ * #define BYTE_ORDER BIG_ENDIAN
+ *
+ * The FreeBSD machine this was written on defines BYTE_ORDER
+ * appropriately by including <sys/types.h> (which in turn includes
+ * <machine/endian.h> where the appropriate definitions are actually
+ * made).
+ */
+
+/*
+ * Define the following sha2_* types to types of the correct length on
+ * the native archtecture. Most BSD systems and Linux define u_intXX_t
+ * types. Machines with very recent ANSI C headers, can use the
+ * uintXX_t definintions from inttypes.h by defining SHA2_USE_INTTYPES_H
+ * during compile or in the sha.h header file.
+ *
+ * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t
+ * will need to define these three typedefs below (and the appropriate
+ * ones in sha.h too) by hand according to their system architecture.
+ *
+ * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t
+ * types and pointing out recent ANSI C support for uintXX_t in inttypes.h.
+ */
+typedef uint8_t sha2_byte; /* Exactly 1 byte */
+typedef uint32_t sha2_word32; /* Exactly 4 bytes */
+typedef uint64_t sha2_word64; /* Exactly 8 bytes */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+/* NOTE: Most of these are in sha2.h */
+#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
+#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16)
+#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
+
+
+/*** ENDIAN REVERSAL MACROS *******************************************/
+#ifndef WORDS_BIGENDIAN
+#define REVERSE32(w,x) { \
+ sha2_word32 tmp = (w); \
+ tmp = (tmp >> 16) | (tmp << 16); \
+ (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
+}
+#define REVERSE64(w,x) { \
+ sha2_word64 tmp = (w); \
+ tmp = (tmp >> 32) | (tmp << 32); \
+ tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
+ ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
+ (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
+ ((tmp & 0x0000ffff0000ffffULL) << 16); \
+}
+#endif /* !WORDS_BIGENDIAN */
+
+/*
+ * Macro for incrementally adding the unsigned 64-bit integer n to the
+ * unsigned 128-bit integer (represented using a two-element array of
+ * 64-bit words):
+ */
+#define ADDINC128(w,n) { \
+ (w)[0] += (sha2_word64)(n); \
+ if ((w)[0] < (n)) { \
+ (w)[1]++; \
+ } \
+}
+
+/*
+ * Macros for copying blocks of memory and for zeroing out ranges
+ * of memory. Using these macros makes it easy to switch from
+ * using memset()/memcpy() and using bzero()/bcopy().
+ *
+ * Please define either SHA2_USE_MEMSET_MEMCPY or define
+ * SHA2_USE_BZERO_BCOPY depending on which function set you
+ * choose to use:
+ */
+#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
+/* Default to memset()/memcpy() if no option is specified */
+#define SHA2_USE_MEMSET_MEMCPY 1
+#endif
+#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
+/* Abort with an error if BOTH options are defined */
+#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
+#endif
+
+#ifdef SHA2_USE_MEMSET_MEMCPY
+#define MEMSET_BZERO(p,l) memset((p), 0, (l))
+#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
+#endif
+#ifdef SHA2_USE_BZERO_BCOPY
+#define MEMSET_BZERO(p,l) bzero((p), (l))
+#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
+#endif
+
+
+/*** THE SIX LOGICAL FUNCTIONS ****************************************/
+/*
+ * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
+ *
+ * NOTE: The naming of R and S appears backwards here (R is a SHIFT and
+ * S is a ROTATION) because the SHA-256/384/512 description document
+ * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
+ * same "backwards" definition.
+ */
+/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
+#define R(b,x) ((x) >> (b))
+/* 32-bit Rotate-right (used in SHA-256): */
+#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
+/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
+#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
+
+/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
+#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/* Four of six logical functions used in SHA-256: */
+#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
+#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
+#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
+#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
+
+/* Four of six logical functions used in SHA-384 and SHA-512: */
+#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
+#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
+#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
+#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
+
+/*** INTERNAL FUNCTION PROTOTYPES *************************************/
+/* NOTE: These should not be accessed directly from outside this
+ * library -- they are intended for private internal visibility/use
+ * only.
+ */
+static void SHA256_Last(SHA256_CTX*);
+static void SHA512_Last(SHA512_CTX*);
+static void SHA256_Transform(SHA256_CTX*, const sha2_word32*);
+static void SHA512_Transform(SHA512_CTX*, const sha2_word64*);
+
+
+/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
+/* Hash constant words K for SHA-256: */
+const static sha2_word32 K256[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+ 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+ 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+ 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+ 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+ 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+ 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+ 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+ 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+ 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+ 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+ 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+ 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Initial hash value H for SHA-256: */
+const static sha2_word32 sha256_initial_hash_value[8] = {
+ 0x6a09e667UL,
+ 0xbb67ae85UL,
+ 0x3c6ef372UL,
+ 0xa54ff53aUL,
+ 0x510e527fUL,
+ 0x9b05688cUL,
+ 0x1f83d9abUL,
+ 0x5be0cd19UL
+};
+
+/* Hash constant words K for SHA-384 and SHA-512: */
+const static sha2_word64 K512[80] = {
+ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+ 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+ 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+ 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+ 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+ 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+ 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+ 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+ 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+ 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+ 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+ 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+ 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+ 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+ 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+ 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+ 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+ 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+ 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+ 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+ 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+ 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+ 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+ 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+ 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+ 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+ 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+ 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+ 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+ 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+ 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+ 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+ 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+ 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+ 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+ 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+ 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+ 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+ 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+/* Initial hash value H for SHA-384 */
+const static sha2_word64 sha384_initial_hash_value[8] = {
+ 0xcbbb9d5dc1059ed8ULL,
+ 0x629a292a367cd507ULL,
+ 0x9159015a3070dd17ULL,
+ 0x152fecd8f70e5939ULL,
+ 0x67332667ffc00b31ULL,
+ 0x8eb44a8768581511ULL,
+ 0xdb0c2e0d64f98fa7ULL,
+ 0x47b5481dbefa4fa4ULL
+};
+
+/* Initial hash value H for SHA-512 */
+const static sha2_word64 sha512_initial_hash_value[8] = {
+ 0x6a09e667f3bcc908ULL,
+ 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL,
+ 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL,
+ 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL,
+ 0x5be0cd19137e2179ULL
+};
+
+/* Initial hash value H for SHA-224: */
+const static sha2_word32 sha224_initial_hash_value[8] = {
+ 0xc1059ed8UL,
+ 0x367cd507UL,
+ 0x3070dd17UL,
+ 0xf70e5939UL,
+ 0xffc00b31UL,
+ 0x68581511UL,
+ 0x64f98fa7UL,
+ 0xbefa4fa4UL
+};
+
+
+/*** SHA-256: *********************************************************/
+void solv_SHA256_Init(SHA256_CTX* context) {
+ if (context == (SHA256_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);
+ MEMSET_BZERO((char *)context->buffer, SHA256_BLOCK_LENGTH);
+ context->bitcount = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-256 round macros: */
+
+#ifndef WORDS_BIGENDIAN
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE32(*data++, W256[j]); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + W256[j]; \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+
+#else /* !WORDS_BIGENDIAN */
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + (W256[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* !WORDS_BIGENDIAN */
+
+#define ROUND256(a,b,c,d,e,f,g,h) \
+ s0 = W256[(j+1)&0x0f]; \
+ s0 = sigma0_256(s0); \
+ s1 = W256[(j+14)&0x0f]; \
+ s1 = sigma1_256(s1); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+static void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, *W256;
+ int j;
+
+ W256 = context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ /* Rounds 0 to 15 (unrolled): */
+ ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds to 64: */
+ do {
+ ROUND256(a,b,c,d,e,f,g,h);
+ ROUND256(h,a,b,c,d,e,f,g);
+ ROUND256(g,h,a,b,c,d,e,f);
+ ROUND256(f,g,h,a,b,c,d,e);
+ ROUND256(e,f,g,h,a,b,c,d);
+ ROUND256(d,e,f,g,h,a,b,c);
+ ROUND256(c,d,e,f,g,h,a,b);
+ ROUND256(b,c,d,e,f,g,h,a);
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+static void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, T2, *W256;
+ int j;
+
+ W256 = context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#ifndef WORDS_BIGENDIAN
+ /* Copy data while converting to host byte order */
+ REVERSE32(*data++,W256[j]);
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
+#else /* !WORDS_BIGENDIAN */
+ /* Apply the SHA-256 compression function to update a..h with copy */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
+#endif /* !WORDS_BIGENDIAN */
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W256[(j+1)&0x0f];
+ s0 = sigma0_256(s0);
+ s1 = W256[(j+14)&0x0f];
+ s1 = sigma1_256(s1);
+
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void solv_SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ /* assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0); */
+
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA256_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&((char *)context->buffer)[usedspace], data, freespace);
+ context->bitcount += freespace << 3;
+ len -= freespace;
+ data += freespace;
+ SHA256_Transform(context, context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&((char *)context->buffer)[usedspace], data, len);
+ context->bitcount += len << 3;
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA256_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ SHA256_Transform(context, (sha2_word32*)data);
+ context->bitcount += SHA256_BLOCK_LENGTH << 3;
+ len -= SHA256_BLOCK_LENGTH;
+ data += SHA256_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY((char *)context->buffer, data, len);
+ context->bitcount += len << 3;
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+static void SHA256_Last(SHA256_CTX* context) {
+ unsigned int usedspace;
+
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+#ifndef WORDS_BIGENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount,context->bitcount);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ ((char *)context->buffer)[usedspace++] = 0x80;
+
+ if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&((char *)context->buffer)[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA256_BLOCK_LENGTH) {
+ MEMSET_BZERO(&((char *)context->buffer)[usedspace], SHA256_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA256_Transform(context, context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO((char *)context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+ }
+ } else {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO((char *)context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *((char *)context->buffer) = 0x80;
+ }
+ /* Set the bit count: */
+ MEMCPY_BCOPY(&((char *)context->buffer)[SHA256_SHORT_BLOCK_LENGTH], (char *)(&context->bitcount), 8);
+
+ /* Final transform: */
+ SHA256_Transform(context, context->buffer);
+}
+
+void solv_SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
+ sha2_word32 *d = (sha2_word32*)digest;
+
+ /* Sanity check: */
+ /* assert(context != (SHA256_CTX*)0); */
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA256_Last(context);
+
+#ifndef WORDS_BIGENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE32(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Clean up state data: */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+
+/*** SHA-512: *********************************************************/
+void solv_SHA512_Init(SHA512_CTX* context) {
+ if (context == (SHA512_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO((char *)context->buffer, SHA512_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-512 round macros: */
+#ifndef WORDS_BIGENDIAN
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE64(*data++, W512[j]); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + W512[j]; \
+ (d) += T1, \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \
+ j++
+
+
+#else /* !WORDS_BIGENDIAN */
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + (W512[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* !WORDS_BIGENDIAN */
+
+#define ROUND512(a,b,c,d,e,f,g,h) \
+ s0 = W512[(j+1)&0x0f]; \
+ s0 = sigma0_512(s0); \
+ s1 = W512[(j+14)&0x0f]; \
+ s1 = sigma1_512(s1); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+static void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, *W512 = context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds up to 79: */
+ do {
+ ROUND512(a,b,c,d,e,f,g,h);
+ ROUND512(h,a,b,c,d,e,f,g);
+ ROUND512(g,h,a,b,c,d,e,f);
+ ROUND512(f,g,h,a,b,c,d,e);
+ ROUND512(e,f,g,h,a,b,c,d);
+ ROUND512(d,e,f,g,h,a,b,c);
+ ROUND512(c,d,e,f,g,h,a,b);
+ ROUND512(b,c,d,e,f,g,h,a);
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+static void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, T2, *W512 = context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#ifndef WORDS_BIGENDIAN
+ /* Convert TO host byte order */
+ REVERSE64(*data++, W512[j]);
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
+#else /* !WORDS_BIGENDIAN */
+ /* Apply the SHA-512 compression function to update a..h with copy */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
+#endif /* !WORDS_BIGENDIAN */
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W512[(j+1)&0x0f];
+ s0 = sigma0_512(s0);
+ s1 = W512[(j+14)&0x0f];
+ s1 = sigma1_512(s1);
+
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void solv_SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ /* assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0); */
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA512_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&((char *)context->buffer)[usedspace], data, freespace);
+ ADDINC128(context->bitcount, freespace << 3);
+ len -= freespace;
+ data += freespace;
+ SHA512_Transform(context, context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&((char *)context->buffer)[usedspace], data, len);
+ ADDINC128(context->bitcount, len << 3);
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA512_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ SHA512_Transform(context, (sha2_word64*)data);
+ ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
+ len -= SHA512_BLOCK_LENGTH;
+ data += SHA512_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY((char *)context->buffer, data, len);
+ ADDINC128(context->bitcount, len << 3);
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+static void SHA512_Last(SHA512_CTX* context) {
+ unsigned int usedspace;
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+#ifndef WORDS_BIGENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount[0],context->bitcount[0]);
+ REVERSE64(context->bitcount[1],context->bitcount[1]);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ ((char *)context->buffer)[usedspace++] = 0x80;
+
+ if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&((char *)context->buffer)[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA512_BLOCK_LENGTH) {
+ MEMSET_BZERO(&((char *)context->buffer)[usedspace], SHA512_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA512_Transform(context, context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO((char *)context->buffer, SHA512_BLOCK_LENGTH - 2);
+ }
+ } else {
+ /* Prepare for final transform: */
+ MEMSET_BZERO((char *)context->buffer, SHA512_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *((char *)context->buffer) = 0x80;
+ }
+ /* Store the length of input data (in bits): */
+ MEMCPY_BCOPY(&((char *)context->buffer)[SHA512_SHORT_BLOCK_LENGTH], (char *)(&context->bitcount[1]), 8);
+ MEMCPY_BCOPY(&((char *)context->buffer)[SHA512_SHORT_BLOCK_LENGTH + 8], (char *)(&context->bitcount[0]), 8);
+
+ /* Final transform: */
+ SHA512_Transform(context, context->buffer);
+}
+
+void solv_SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ /* assert(context != (SHA512_CTX*)0); */
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last(context);
+
+ /* Save the hash data for output: */
+#ifndef WORDS_BIGENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+
+/*** SHA-384: *********************************************************/
+void solv_SHA384_Init(SHA384_CTX* context) {
+ if (context == (SHA384_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO((char *)context->buffer, SHA384_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+void solv_SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) {
+ solv_SHA512_Update((SHA512_CTX*)context, data, len);
+}
+
+void solv_SHA384_Final(sha2_byte digest[], SHA384_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ /* assert(context != (SHA384_CTX*)0); */
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last((SHA512_CTX*)context);
+
+ /* Save the hash data for output: */
+#ifndef WORDS_BIGENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 6; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+
+/*** SHA-224: *********************************************************/
+
+void solv_SHA224_Init(SHA224_CTX* context) {
+ if (context == (SHA224_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha224_initial_hash_value, SHA256_DIGEST_LENGTH);
+ MEMSET_BZERO((char *)context->buffer, SHA224_BLOCK_LENGTH);
+ context->bitcount = 0;
+}
+
+void solv_SHA224_Update(SHA224_CTX* context, const sha2_byte* data, size_t len) {
+ solv_SHA256_Update((SHA256_CTX*)context, data, len);
+}
+
+void solv_SHA224_Final(sha2_byte digest[], SHA224_CTX* context) {
+ sha2_word32 *d = (sha2_word32*)digest;
+
+ /* Sanity check: */
+ /* assert(context != (SHA224_CTX*)0); */
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA256_Last(context);
+
+#ifndef WORDS_BIGENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 7; j++) {
+ REVERSE32(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA224_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Clean up state data: */
+ MEMSET_BZERO(context, sizeof(*context));
+}
--- /dev/null
+/*
+ * FILE: sha2.h
+ * AUTHOR: Aaron D. Gifford <me@aarongifford.com>
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
+ */
+
+#include <inttypes.h>
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+#define SHA224_BLOCK_LENGTH 64
+#define SHA224_DIGEST_LENGTH 28
+#define SHA256_BLOCK_LENGTH 64
+#define SHA256_DIGEST_LENGTH 32
+#define SHA384_BLOCK_LENGTH 128
+#define SHA384_DIGEST_LENGTH 48
+#define SHA512_BLOCK_LENGTH 128
+#define SHA512_DIGEST_LENGTH 64
+
+
+/*** SHA-256/384/512 Context Structures *******************************/
+/* NOTE: If your architecture does not define either u_intXX_t types or
+ * uintXX_t (from inttypes.h), you may need to define things by hand
+ * for your system:
+ */
+typedef struct _SHA256_CTX {
+ uint32_t state[8];
+ uint64_t bitcount;
+ uint32_t buffer[SHA256_BLOCK_LENGTH/4];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ uint64_t state[8];
+ uint64_t bitcount[2];
+ uint64_t buffer[SHA512_BLOCK_LENGTH/8];
+} SHA512_CTX;
+
+typedef SHA256_CTX SHA224_CTX;
+typedef SHA512_CTX SHA384_CTX;
+
+
+/*** SHA-224/256/384/512 Function Prototypes ******************************/
+void solv_SHA224_Init(SHA224_CTX *);
+void solv_SHA224_Update(SHA224_CTX*, const uint8_t*, size_t);
+void solv_SHA224_Final(uint8_t[SHA224_DIGEST_LENGTH], SHA224_CTX*);
+
+void solv_SHA256_Init(SHA256_CTX *);
+void solv_SHA256_Update(SHA256_CTX*, const uint8_t*, size_t);
+void solv_SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+
+void solv_SHA384_Init(SHA384_CTX*);
+void solv_SHA384_Update(SHA384_CTX*, const uint8_t*, size_t);
+void solv_SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+
+void solv_SHA512_Init(SHA512_CTX*);
+void solv_SHA512_Update(SHA512_CTX*, const uint8_t*, size_t);
+void solv_SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
--- /dev/null
+/*
+ * Copyright (c) 2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solvable.c
+ *
+ * set/retrieve data from solvables
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#include "policy.h"
+#include "poolvendor.h"
+#include "chksum.h"
+#include "linkedpkg.h"
+
+const char *
+pool_solvable2str(Pool *pool, Solvable *s)
+{
+ const char *n, *e, *a;
+ int nl, el, al;
+ char *p;
+ n = pool_id2str(pool, s->name);
+ e = pool_id2str(pool, s->evr);
+ /* XXX: may want to skip the epoch here */
+ a = pool_id2str(pool, s->arch);
+ nl = strlen(n);
+ el = strlen(e);
+ al = strlen(a);
+ if (pool->havedistepoch)
+ {
+ /* strip the distepoch from the evr */
+ const char *de = strrchr(e, '-');
+ if (de && (de = strchr(de, ':')) != 0)
+ el = de - e;
+ }
+ p = pool_alloctmpspace(pool, nl + el + al + 3);
+ strcpy(p, n);
+ if (el)
+ {
+ p[nl++] = '-';
+ strncpy(p + nl, e, el);
+ p[nl + el] = 0;
+ }
+ if (al)
+ {
+ p[nl + el] = pool->disttype == DISTTYPE_HAIKU ? '-' : '.';
+ strcpy(p + nl + el + 1, a);
+ }
+ return p;
+}
+
+Id
+solvable_lookup_type(Solvable *s, Id keyname)
+{
+ if (!s->repo)
+ return 0;
+ return repo_lookup_type(s->repo, s - s->repo->pool->solvables, keyname);
+}
+
+Id
+solvable_lookup_id(Solvable *s, Id keyname)
+{
+ if (!s->repo)
+ return 0;
+ return repo_lookup_id(s->repo, s - s->repo->pool->solvables, keyname);
+}
+
+int
+solvable_lookup_idarray(Solvable *s, Id keyname, Queue *q)
+{
+ if (!s->repo)
+ {
+ queue_empty(q);
+ return 0;
+ }
+ return repo_lookup_idarray(s->repo, s - s->repo->pool->solvables, keyname, q);
+}
+
+int
+solvable_lookup_deparray(Solvable *s, Id keyname, Queue *q, Id marker)
+{
+ if (!s->repo)
+ {
+ queue_empty(q);
+ return 0;
+ }
+ return repo_lookup_deparray(s->repo, s - s->repo->pool->solvables, keyname, q, marker);
+}
+
+static const char *
+solvable_lookup_str_joinarray(Solvable *s, Id keyname, const char *joinstr)
+{
+ Queue q;
+ Id qbuf[10];
+ char *str = 0;
+
+ queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
+ if (solvable_lookup_idarray(s, keyname, &q) && q.count)
+ {
+ Pool *pool = s->repo->pool;
+ int i;
+ str = pool_tmpjoin(pool, pool_id2str(pool, q.elements[0]), 0, 0);
+ for (i = 1; i < q.count; i++)
+ str = pool_tmpappend(pool, str, joinstr, pool_id2str(pool, q.elements[i]));
+ }
+ queue_free(&q);
+ return str;
+}
+
+const char *
+solvable_lookup_str(Solvable *s, Id keyname)
+{
+ const char *str;
+ if (!s->repo)
+ return 0;
+ str = repo_lookup_str(s->repo, s - s->repo->pool->solvables, keyname);
+ if (!str && (keyname == SOLVABLE_LICENSE || keyname == SOLVABLE_GROUP))
+ str = solvable_lookup_str_joinarray(s, keyname, ", ");
+ return str;
+}
+
+static const char *
+solvable_lookup_str_base(Solvable *s, Id keyname, Id basekeyname, int usebase)
+{
+ Pool *pool;
+ const char *str, *basestr;
+ Id p, pp, name;
+ Solvable *s2;
+ int pass;
+
+ if (!s->repo)
+ return 0;
+ pool = s->repo->pool;
+ str = solvable_lookup_str(s, keyname);
+ if (str || keyname == basekeyname)
+ return str;
+ basestr = solvable_lookup_str(s, basekeyname);
+ if (!basestr)
+ return 0;
+ /* search for a solvable with same name and same base that has the
+ * translation */
+ if (!pool->whatprovides)
+ return usebase ? basestr : 0;
+ name = s->name;
+ /* we do this in two passes, first same vendor, then all other vendors */
+ for (pass = 0; pass < 2; pass++)
+ {
+ FOR_PROVIDES(p, pp, name)
+ {
+ s2 = pool->solvables + p;
+ if (s2->name != name)
+ continue;
+ if ((s->vendor == s2->vendor) != (pass == 0))
+ continue;
+ str = solvable_lookup_str(s2, basekeyname);
+ if (!str || strcmp(str, basestr))
+ continue;
+ str = solvable_lookup_str(s2, keyname);
+ if (str)
+ return str;
+ }
+#ifdef ENABLE_LINKED_PKGS
+ /* autopattern/product translation magic */
+ if (pass)
+ {
+ const char *n = pool_id2str(pool, name);
+ if (*n == 'p')
+ {
+ if (!strncmp("pattern:", n, 8) && (name = find_autopattern_name(pool, s)) != 0)
+ pass = -1;
+ if (!strncmp("product:", n, 8) && (name = find_autoproduct_name(pool, s)) != 0)
+ pass = -1;
+ }
+ }
+#endif
+ }
+ return usebase ? basestr : 0;
+}
+
+const char *
+solvable_lookup_str_poollang(Solvable *s, Id keyname)
+{
+ Pool *pool;
+ int i, cols;
+ const char *str;
+ Id *row;
+
+ if (!s->repo)
+ return 0;
+ pool = s->repo->pool;
+ if (!pool->nlanguages)
+ return solvable_lookup_str(s, keyname);
+ cols = pool->nlanguages + 1;
+ if (!pool->languagecache)
+ {
+ pool->languagecache = solv_calloc(cols * ID_NUM_INTERNAL, sizeof(Id));
+ pool->languagecacheother = 0;
+ }
+ if (keyname >= ID_NUM_INTERNAL)
+ {
+ row = pool->languagecache + ID_NUM_INTERNAL * cols;
+ for (i = 0; i < pool->languagecacheother; i++, row += cols)
+ if (*row == keyname)
+ break;
+ if (i >= pool->languagecacheother)
+ {
+ pool->languagecache = solv_realloc2(pool->languagecache, pool->languagecacheother + 1, cols * sizeof(Id));
+ row = pool->languagecache + cols * (ID_NUM_INTERNAL + pool->languagecacheother++);
+ *row = keyname;
+ }
+ }
+ else
+ row = pool->languagecache + keyname * cols;
+ row++; /* skip keyname */
+ for (i = 0; i < pool->nlanguages; i++, row++)
+ {
+ if (!*row)
+ *row = pool_id2langid(pool, keyname, pool->languages[i], 1);
+ str = solvable_lookup_str_base(s, *row, keyname, 0);
+ if (str)
+ return str;
+ }
+ return solvable_lookup_str(s, keyname);
+}
+
+const char *
+solvable_lookup_str_lang(Solvable *s, Id keyname, const char *lang, int usebase)
+{
+ if (s->repo)
+ {
+ Id id = pool_id2langid(s->repo->pool, keyname, lang, 0);
+ if (id)
+ return solvable_lookup_str_base(s, id, keyname, usebase);
+ if (!usebase)
+ return 0;
+ }
+ return solvable_lookup_str(s, keyname);
+}
+
+unsigned long long
+solvable_lookup_num(Solvable *s, Id keyname, unsigned long long notfound)
+{
+ if (!s->repo)
+ return notfound;
+ return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, notfound);
+}
+
+unsigned int
+solvable_lookup_sizek(Solvable *s, Id keyname, unsigned int notfound)
+{
+ unsigned long long size;
+ if (!s->repo)
+ return notfound;
+ size = solvable_lookup_num(s, keyname, (unsigned long long)notfound << 10);
+ return (unsigned int)((size + 1023) >> 10);
+}
+
+int
+solvable_lookup_void(Solvable *s, Id keyname)
+{
+ if (!s->repo)
+ return 0;
+ return repo_lookup_void(s->repo, s - s->repo->pool->solvables, keyname);
+}
+
+int
+solvable_lookup_bool(Solvable *s, Id keyname)
+{
+ if (!s->repo)
+ return 0;
+ /* historic nonsense: there are two ways of storing a bool, as num == 1 or void. test both. */
+ if (repo_lookup_type(s->repo, s - s->repo->pool->solvables, keyname) == REPOKEY_TYPE_VOID)
+ return 1;
+ return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, 0) == 1;
+}
+
+const unsigned char *
+solvable_lookup_bin_checksum(Solvable *s, Id keyname, Id *typep)
+{
+ Repo *repo = s->repo;
+
+ if (!repo)
+ {
+ *typep = 0;
+ return 0;
+ }
+ return repo_lookup_bin_checksum(repo, s - repo->pool->solvables, keyname, typep);
+}
+
+const char *
+solvable_lookup_checksum(Solvable *s, Id keyname, Id *typep)
+{
+ const unsigned char *chk = solvable_lookup_bin_checksum(s, keyname, typep);
+ return chk ? pool_bin2hex(s->repo->pool, chk, solv_chksum_len(*typep)) : 0;
+}
+
+static inline const char *
+evrid2vrstr(Pool *pool, Id evrid)
+{
+ const char *p, *evr = pool_id2str(pool, evrid);
+ if (!evr)
+ return evr;
+ for (p = evr; *p >= '0' && *p <= '9'; p++)
+ ;
+ return p != evr && *p == ':' && p[1] ? p + 1 : evr;
+}
+
+const char *
+solvable_lookup_location(Solvable *s, unsigned int *medianrp)
+{
+ Pool *pool;
+ int l = 0;
+ char *loc;
+ const char *mediadir, *mediafile;
+
+ if (medianrp)
+ *medianrp = 0;
+ if (!s->repo)
+ return 0;
+ pool = s->repo->pool;
+ if (medianrp)
+ *medianrp = solvable_lookup_num(s, SOLVABLE_MEDIANR, 0);
+ if (solvable_lookup_void(s, SOLVABLE_MEDIADIR))
+ mediadir = pool_id2str(pool, s->arch);
+ else
+ mediadir = solvable_lookup_str(s, SOLVABLE_MEDIADIR);
+ if (mediadir)
+ l = strlen(mediadir) + 1;
+ if (solvable_lookup_void(s, SOLVABLE_MEDIAFILE))
+ {
+ const char *name, *evr, *arch;
+ name = pool_id2str(pool, s->name);
+ evr = evrid2vrstr(pool, s->evr);
+ arch = pool_id2str(pool, s->arch);
+ /* name-vr.arch.rpm */
+ loc = pool_alloctmpspace(pool, l + strlen(name) + strlen(evr) + strlen(arch) + 7);
+ if (mediadir)
+ sprintf(loc, "%s/%s-%s.%s.rpm", mediadir, name, evr, arch);
+ else
+ sprintf(loc, "%s-%s.%s.rpm", name, evr, arch);
+ }
+ else
+ {
+ mediafile = solvable_lookup_str(s, SOLVABLE_MEDIAFILE);
+ if (!mediafile)
+ return 0;
+ loc = pool_alloctmpspace(pool, l + strlen(mediafile) + 1);
+ if (mediadir)
+ sprintf(loc, "%s/%s", mediadir, mediafile);
+ else
+ strcpy(loc, mediafile);
+ }
+ return loc;
+}
+
+const char *
+solvable_get_location(Solvable *s, unsigned int *medianrp)
+{
+ const char *loc = solvable_lookup_location(s, medianrp);
+ if (medianrp && *medianrp == 0)
+ *medianrp = 1; /* compat, to be removed */
+ return loc;
+}
+
+const char *
+solvable_lookup_sourcepkg(Solvable *s)
+{
+ Pool *pool;
+ const char *evr, *name;
+ Id archid;
+
+ if (!s->repo)
+ return 0;
+ pool = s->repo->pool;
+ if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
+ name = pool_id2str(pool, s->name);
+ else
+ name = solvable_lookup_str(s, SOLVABLE_SOURCENAME);
+ if (!name)
+ return 0;
+ archid = solvable_lookup_id(s, SOLVABLE_SOURCEARCH);
+ if (solvable_lookup_void(s, SOLVABLE_SOURCEEVR))
+ evr = evrid2vrstr(pool, s->evr);
+ else
+ evr = solvable_lookup_str(s, SOLVABLE_SOURCEEVR);
+ if (archid == ARCH_SRC || archid == ARCH_NOSRC)
+ {
+ char *str;
+ str = pool_tmpjoin(pool, name, evr ? "-" : 0, evr);
+ str = pool_tmpappend(pool, str, ".", pool_id2str(pool, archid));
+ return pool_tmpappend(pool, str, ".rpm", 0);
+ }
+ else
+ return name; /* FIXME */
+}
+
+
+/*****************************************************************************/
+
+static inline Id dep2name(Pool *pool, Id dep)
+{
+ while (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ dep = rd->name;
+ }
+ return dep;
+}
+
+static int providedbyinstalled_multiversion(Pool *pool, Map *installed, Id n, Id con)
+{
+ Id p, pp;
+ Solvable *sn = pool->solvables + n;
+
+ FOR_PROVIDES(p, pp, sn->name)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->name != sn->name || s->arch != sn->arch)
+ continue;
+ if (!MAPTST(installed, p))
+ continue;
+ if (pool_match_nevr(pool, pool->solvables + p, con))
+ continue;
+ return 1; /* found installed package that doesn't conflict */
+ }
+ return 0;
+}
+
+static inline int providedbyinstalled(Pool *pool, Map *installed, Id dep, int ispatch, Map *multiversionmap)
+{
+ Id p, pp;
+ FOR_PROVIDES(p, pp, dep)
+ {
+ if (p == SYSTEMSOLVABLE)
+ return -1;
+ if (ispatch && !pool_match_nevr(pool, pool->solvables + p, dep))
+ continue;
+ if (ispatch && multiversionmap && multiversionmap->size && MAPTST(multiversionmap, p) && ISRELDEP(dep))
+ if (providedbyinstalled_multiversion(pool, installed, p, dep))
+ continue;
+ if (MAPTST(installed, p))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * solvable_trivial_installable_map - anwers is a solvable is installable
+ * without any other installs/deinstalls.
+ * The packages considered to be installed are provided via the
+ * installedmap bitmap. A additional "conflictsmap" bitmap providing
+ * information about the conflicts of the installed packages can be
+ * used for extra speed up. Provide a NULL pointer if you do not
+ * have this information.
+ * Both maps can be created with pool_create_state_maps() or
+ * solver_create_state_maps().
+ *
+ * returns:
+ * 1: solvable is installable without any other package changes
+ * 0: solvable is not installable
+ * -1: solvable is installable, but doesn't constrain any installed packages
+ */
+int
+solvable_trivial_installable_map(Solvable *s, Map *installedmap, Map *conflictsmap, Map *multiversionmap)
+{
+ Pool *pool = s->repo->pool;
+ Solvable *s2;
+ Id p, *dp;
+ Id *reqp, req;
+ Id *conp, con;
+ int r, interesting = 0;
+
+ if (conflictsmap && MAPTST(conflictsmap, s - pool->solvables))
+ return 0;
+ if (s->requires)
+ {
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ continue;
+ r = providedbyinstalled(pool, installedmap, req, 0, 0);
+ if (!r)
+ return 0;
+ if (r > 0)
+ interesting = 1;
+ }
+ }
+ if (s->conflicts)
+ {
+ int ispatch = 0;
+
+ if (!strncmp("patch:", pool_id2str(pool, s->name), 6))
+ ispatch = 1;
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ if (providedbyinstalled(pool, installedmap, con, ispatch, multiversionmap))
+ {
+ if (ispatch && solvable_is_irrelevant_patch(s, installedmap))
+ return -1;
+ return 0;
+ }
+ if (!interesting && ISRELDEP(con))
+ {
+ con = dep2name(pool, con);
+ if (providedbyinstalled(pool, installedmap, con, ispatch, multiversionmap))
+ interesting = 1;
+ }
+ }
+ if (ispatch && interesting && solvable_is_irrelevant_patch(s, installedmap))
+ interesting = 0;
+ }
+#if 0
+ if (s->repo)
+ {
+ Id *obsp, obs;
+ Repo *installed = 0;
+ if (s->obsoletes && s->repo != installed)
+ {
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ if (providedbyinstalled(pool, installedmap, obs, 0, 0))
+ return 0;
+ }
+ }
+ if (s->repo != installed)
+ {
+ Id pp;
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ s2 = pool->solvables + p;
+ if (s2->repo == installed && s2->name == s->name)
+ return 0;
+ }
+ }
+ }
+#endif
+ if (!conflictsmap)
+ {
+ int i;
+
+ p = s - pool->solvables;
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ if (!MAPTST(installedmap, i))
+ continue;
+ s2 = pool->solvables + i;
+ if (!s2->conflicts)
+ continue;
+ conp = s2->repo->idarraydata + s2->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ dp = pool_whatprovides_ptr(pool, con);
+ for (; *dp; dp++)
+ if (*dp == p)
+ return 0;
+ }
+ }
+ }
+ return interesting ? 1 : -1;
+}
+
+/*
+ * different interface for solvable_trivial_installable_map, where
+ * the information about the installed packages is provided
+ * by a queue.
+ */
+int
+solvable_trivial_installable_queue(Solvable *s, Queue *installed, Map *multiversionmap)
+{
+ Pool *pool = s->repo->pool;
+ int i;
+ Id p;
+ Map installedmap;
+ int r;
+
+ map_init(&installedmap, pool->nsolvables);
+ for (i = 0; i < installed->count; i++)
+ {
+ p = installed->elements[i];
+ if (p > 0) /* makes it work with decisionq */
+ MAPSET(&installedmap, p);
+ }
+ r = solvable_trivial_installable_map(s, &installedmap, 0, multiversionmap);
+ map_free(&installedmap);
+ return r;
+}
+
+/*
+ * different interface for solvable_trivial_installable_map, where
+ * the information about the installed packages is provided
+ * by a repo containing the installed solvables.
+ */
+int
+solvable_trivial_installable_repo(Solvable *s, Repo *installed, Map *multiversionmap)
+{
+ Pool *pool = s->repo->pool;
+ Id p;
+ Solvable *s2;
+ Map installedmap;
+ int r;
+
+ map_init(&installedmap, pool->nsolvables);
+ FOR_REPO_SOLVABLES(installed, p, s2)
+ MAPSET(&installedmap, p);
+ r = solvable_trivial_installable_map(s, &installedmap, 0, multiversionmap);
+ map_free(&installedmap);
+ return r;
+}
+
+/* FIXME: this mirrors policy_illegal_vendorchange */
+static int
+pool_illegal_vendorchange(Pool *pool, Solvable *s1, Solvable *s2)
+{
+ Id v1, v2;
+ Id vendormask1, vendormask2;
+
+ if (pool->custom_vendorcheck)
+ return pool->custom_vendorcheck(pool, s1, s2);
+ /* treat a missing vendor as empty string */
+ v1 = s1->vendor ? s1->vendor : ID_EMPTY;
+ v2 = s2->vendor ? s2->vendor : ID_EMPTY;
+ if (v1 == v2)
+ return 0;
+ vendormask1 = pool_vendor2mask(pool, v1);
+ if (!vendormask1)
+ return 1; /* can't match */
+ vendormask2 = pool_vendor2mask(pool, v2);
+ if ((vendormask1 & vendormask2) != 0)
+ return 0;
+ return 1; /* no class matches */
+}
+
+/* check if this patch is relevant according to the vendor. To bad that patches
+ * don't have a vendor, so we need to do some careful repo testing. */
+int
+solvable_is_irrelevant_patch(Solvable *s, Map *installedmap)
+{
+ Pool *pool = s->repo->pool;
+ Id con, *conp;
+ int hadpatchpackage = 0;
+
+ if (!s->conflicts)
+ return 0;
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ Reldep *rd;
+ Id p, pp, p2, pp2;
+ if (!ISRELDEP(con))
+ continue;
+ rd = GETRELDEP(pool, con);
+ if (rd->flags != REL_LT)
+ continue;
+ FOR_PROVIDES(p, pp, con)
+ {
+ Solvable *si;
+ if (!MAPTST(installedmap, p))
+ continue;
+ si = pool->solvables + p;
+ if (!pool_match_nevr(pool, si, con))
+ continue;
+ FOR_PROVIDES(p2, pp2, rd->name)
+ {
+ Solvable *s2 = pool->solvables + p2;
+ if (!pool_match_nevr(pool, s2, rd->name))
+ continue;
+ if (pool_match_nevr(pool, s2, con))
+ continue; /* does not fulfill patch */
+ if (s2->repo == s->repo)
+ {
+ hadpatchpackage = 1;
+ /* ok, we have a package from the patch repo that solves the conflict. check vendor */
+ if (si->vendor == s2->vendor)
+ return 0;
+ if (!pool_illegal_vendorchange(pool, si, s2))
+ return 0;
+ /* vendor change was illegal, ignore conflict */
+ }
+ }
+ }
+ }
+ /* if we didn't find a patchpackage don't claim that the patch is irrelevant */
+ if (!hadpatchpackage)
+ return 0;
+ return 1;
+}
+
+/*****************************************************************************/
+
+/*
+ * Create maps containing the state of each solvable. Input is a "installed" queue,
+ * it contains all solvable ids that are considered to be installed.
+ *
+ * The created maps can be used for solvable_trivial_installable_map(),
+ * pool_calc_duchanges(), pool_calc_installsizechange().
+ *
+ */
+void
+pool_create_state_maps(Pool *pool, Queue *installed, Map *installedmap, Map *conflictsmap)
+{
+ int i;
+ Solvable *s;
+ Id p, *dp;
+ Id *conp, con;
+
+ map_init(installedmap, pool->nsolvables);
+ if (conflictsmap)
+ map_init(conflictsmap, pool->nsolvables);
+ for (i = 0; i < installed->count; i++)
+ {
+ p = installed->elements[i];
+ if (p <= 0) /* makes it work with decisionq */
+ continue;
+ MAPSET(installedmap, p);
+ if (!conflictsmap)
+ continue;
+ s = pool->solvables + p;
+ if (!s->conflicts)
+ continue;
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ dp = pool_whatprovides_ptr(pool, con);
+ for (; *dp; dp++)
+ MAPSET(conflictsmap, *dp);
+ }
+ }
+}
+
+/* Tests if two solvables have identical content. Currently
+ * both solvables need to come from the same pool
+ */
+
+int
+solvable_identical(Solvable *s1, Solvable *s2)
+{
+ unsigned int bt1, bt2;
+ Id rq1, rq2;
+ Id *reqp;
+ if (s1->name != s2->name)
+ return 0;
+ if (s1->arch != s2->arch)
+ return 0;
+ if (s1->evr != s2->evr)
+ return 0;
+
+ /* check vendor, map missing vendor to empty string */
+ if ((s1->vendor ? s1->vendor : 1) != (s2->vendor ? s2->vendor : 1))
+ {
+ /* workaround for bug 881493 */
+ if (s1->repo && !strncmp(pool_id2str(s1->repo->pool, s1->name), "product:", 8))
+ return 1;
+ return 0;
+ }
+
+ /* looking good, try some fancier stuff */
+ /* might also look up the package checksum here */
+ bt1 = solvable_lookup_num(s1, SOLVABLE_BUILDTIME, 0);
+ bt2 = solvable_lookup_num(s2, SOLVABLE_BUILDTIME, 0);
+ if (bt1 && bt2)
+ {
+ if (bt1 != bt2)
+ return 0;
+ }
+ else
+ {
+ if (s1->repo)
+ {
+ /* workaround for bugs 881493 and 885830*/
+ const char *n = pool_id2str(s1->repo->pool, s1->name);
+ if (!strncmp(n, "product:", 8) || !strncmp(n, "application:", 12))
+ return 1;
+ }
+ /* look at requires in a last attempt to find recompiled packages */
+ rq1 = rq2 = 0;
+ if (s1->requires)
+ for (reqp = s1->repo->idarraydata + s1->requires; *reqp; reqp++)
+ rq1 ^= *reqp;
+ if (s2->requires)
+ for (reqp = s2->repo->idarraydata + s2->requires; *reqp; reqp++)
+ rq2 ^= *reqp;
+ if (rq1 != rq2)
+ return 0;
+ }
+ return 1;
+}
+
+/* return the self provide dependency of a solvable */
+Id
+solvable_selfprovidedep(Solvable *s)
+{
+ Pool *pool;
+ Reldep *rd;
+ Id prov, *provp;
+
+ if (!s->repo)
+ return s->name;
+ pool = s->repo->pool;
+ if (s->provides)
+ {
+ provp = s->repo->idarraydata + s->provides;
+ while ((prov = *provp++) != 0)
+ {
+ if (!ISRELDEP(prov))
+ continue;
+ rd = GETRELDEP(pool, prov);
+ if (rd->name == s->name && rd->evr == s->evr && rd->flags == REL_EQ)
+ return prov;
+ }
+ }
+ return pool_rel2id(pool, s->name, s->evr, REL_EQ, 1);
+}
+
+/* setter functions, simply call the repo variants */
+void
+solvable_set_id(Solvable *s, Id keyname, Id id)
+{
+ repo_set_id(s->repo, s - s->repo->pool->solvables, keyname, id);
+}
+
+void
+solvable_set_num(Solvable *s, Id keyname, unsigned long long num)
+{
+ repo_set_num(s->repo, s - s->repo->pool->solvables, keyname, num);
+}
+
+void
+solvable_set_str(Solvable *s, Id keyname, const char *str)
+{
+ repo_set_str(s->repo, s - s->repo->pool->solvables, keyname, str);
+}
+
+void
+solvable_set_poolstr(Solvable *s, Id keyname, const char *str)
+{
+ repo_set_poolstr(s->repo, s - s->repo->pool->solvables, keyname, str);
+}
+
+void
+solvable_add_poolstr_array(Solvable *s, Id keyname, const char *str)
+{
+ repo_add_poolstr_array(s->repo, s - s->repo->pool->solvables, keyname, str);
+}
+
+void
+solvable_add_idarray(Solvable *s, Id keyname, Id id)
+{
+ repo_add_idarray(s->repo, s - s->repo->pool->solvables, keyname, id);
+}
+
+void
+solvable_add_deparray(Solvable *s, Id keyname, Id dep, Id marker)
+{
+ repo_add_deparray(s->repo, s - s->repo->pool->solvables, keyname, dep, marker);
+}
+
+void
+solvable_set_idarray(Solvable *s, Id keyname, Queue *q)
+{
+ repo_set_idarray(s->repo, s - s->repo->pool->solvables, keyname, q);
+}
+
+void
+solvable_set_deparray(Solvable *s, Id keyname, Queue *q, Id marker)
+{
+ repo_set_deparray(s->repo, s - s->repo->pool->solvables, keyname, q, marker);
+}
+
+void
+solvable_unset(Solvable *s, Id keyname)
+{
+ repo_unset(s->repo, s - s->repo->pool->solvables, keyname);
+}
+
+/* return true if a dependency intersects dep in the keyname array */
+int
+solvable_matchesdep(Solvable *s, Id keyname, Id dep, int marker)
+{
+ int i;
+ Pool *pool = s->repo->pool;
+ Queue q;
+ queue_init(&q);
+ solvable_lookup_deparray(s, keyname, &q, marker);
+ for (i = 0; i < q.count; i++)
+ if (pool_match_dep(pool, q.elements[i], dep))
+ break;
+ i = i == q.count ? 0 : 1;
+ queue_free(&q);
+ return i;
+}
--- /dev/null
+/*
+ * Copyright (c) 2007-2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solvable.h
+ *
+ * A solvable represents an object with name-epoch:version-release.arch
+ * and dependencies
+ */
+
+#ifndef LIBSOLV_SOLVABLE_H
+#define LIBSOLV_SOLVABLE_H
+
+#include "pooltypes.h"
+#include "queue.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _Repo;
+
+typedef struct _Solvable {
+ Id name;
+ Id arch;
+ Id evr; /* epoch:version-release */
+ Id vendor;
+
+ struct _Repo *repo; /* repo we belong to */
+
+ /* dependencies are offsets into repo->idarraydata */
+ Offset provides; /* terminated with Id 0 */
+ Offset obsoletes;
+ Offset conflicts;
+
+ Offset requires;
+ Offset recommends;
+ Offset suggests;
+
+ Offset supplements;
+ Offset enhances;
+
+} Solvable;
+
+/* lookup functions */
+Id solvable_lookup_type(Solvable *s, Id keyname);
+Id solvable_lookup_id(Solvable *s, Id keyname);
+unsigned long long solvable_lookup_num(Solvable *s, Id keyname, unsigned long long notfound);
+unsigned int solvable_lookup_sizek(Solvable *s, Id keyname, unsigned int notfound);
+const char *solvable_lookup_str(Solvable *s, Id keyname);
+const char *solvable_lookup_str_poollang(Solvable *s, Id keyname);
+const char *solvable_lookup_str_lang(Solvable *s, Id keyname, const char *lang, int usebase);
+int solvable_lookup_bool(Solvable *s, Id keyname);
+int solvable_lookup_void(Solvable *s, Id keyname);
+const char *solvable_get_location(Solvable *s, unsigned int *medianrp); /* old name */
+const char *solvable_lookup_location(Solvable *s, unsigned int *medianrp);
+const char *solvable_lookup_sourcepkg(Solvable *s);
+const unsigned char *solvable_lookup_bin_checksum(Solvable *s, Id keyname, Id *typep);
+const char *solvable_lookup_checksum(Solvable *s, Id keyname, Id *typep);
+int solvable_lookup_idarray(Solvable *s, Id keyname, Queue *q);
+int solvable_lookup_deparray(Solvable *s, Id keyname, Queue *q, Id marker);
+
+/* setter functions */
+void solvable_set_id(Solvable *s, Id keyname, Id id);
+void solvable_set_num(Solvable *s, Id keyname, unsigned long long num);
+void solvable_set_str(Solvable *s, Id keyname, const char *str);
+void solvable_set_poolstr(Solvable *s, Id keyname, const char *str);
+void solvable_add_poolstr_array(Solvable *s, Id keyname, const char *str);
+void solvable_add_idarray(Solvable *s, Id keyname, Id id);
+void solvable_add_deparray(Solvable *s, Id keyname, Id dep, Id marker);
+void solvable_set_idarray(Solvable *s, Id keyname, Queue *q);
+void solvable_set_deparray(Solvable *s, Id keyname, Queue *q, Id marker);
+void solvable_unset(Solvable *s, Id keyname);
+
+int solvable_identical(Solvable *s1, Solvable *s2);
+Id solvable_selfprovidedep(Solvable *s);
+int solvable_matchesdep(Solvable *s, Id keyname, Id dep, int marker);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_SOLVABLE_H */
--- /dev/null
+/*
+ * Copyright (c) 2007-2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solver.c
+ *
+ * SAT based dependency solver
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "solver.h"
+#include "solver_private.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "util.h"
+#include "policy.h"
+#include "poolarch.h"
+#include "solverdebug.h"
+#include "cplxdeps.h"
+#include "linkedpkg.h"
+
+#define RULES_BLOCK 63
+
+
+/********************************************************************
+ *
+ * dependency check helpers
+ *
+ */
+
+/*-------------------------------------------------------------------
+ * handle split provides
+ *
+ * a splitprovides dep looks like
+ * namespace:splitprovides(pkg REL_WITH path)
+ * and is only true if pkg is installed and contains the specified path.
+ * we also make sure that pkg is selected for an update, otherwise the
+ * update would always be forced onto the user.
+ * Map m is the map used when called from dep_possible.
+ */
+
+static int
+solver_is_updating(Solver *solv, Id p)
+{
+ /* check if the update rule is true */
+ Pool *pool = solv->pool;
+ Rule *r;
+ Id l, pp;
+ if (solv->decisionmap[p] >= 0)
+ return 0; /* old package stayed */
+ r = solv->rules + solv->updaterules + (p - solv->installed->start);
+ FOR_RULELITERALS(l, pp, r)
+ if (l > 0 && l != p && solv->decisionmap[l] > 0)
+ return 1;
+ return 0;
+}
+
+int
+solver_splitprovides(Solver *solv, Id dep, Map *m)
+{
+ Pool *pool = solv->pool;
+ Id p, pp;
+ Reldep *rd;
+ Solvable *s;
+
+ if (!solv->dosplitprovides || !solv->installed)
+ return 0;
+ if (!ISRELDEP(dep))
+ return 0;
+ rd = GETRELDEP(pool, dep);
+ if (rd->flags != REL_WITH)
+ return 0;
+ /*
+ * things are a bit tricky here if pool->addedprovides == 1, because most split-provides are in
+ * a non-standard location. If we simply call pool_whatprovides, we'll drag in the complete
+ * file list. Instead we rely on pool_addfileprovides ignoring the addfileprovidesfiltered flag
+ * for installed packages and check the lazywhatprovidesq (ignoring the REL_WITH part, but
+ * we filter the package name further down anyway).
+ */
+ if (pool->addedfileprovides == 1 && !ISRELDEP(rd->evr) && !pool->whatprovides[rd->evr])
+ pp = pool_searchlazywhatprovidesq(pool, rd->evr);
+ else
+ pp = pool_whatprovides(pool, dep);
+ while ((p = pool->whatprovidesdata[pp++]) != 0)
+ {
+ /* here we have packages that provide the correct name and contain the path,
+ * now do extra filtering */
+ s = pool->solvables + p;
+ if (s->repo != solv->installed || s->name != rd->name)
+ continue;
+ /* check if the package is updated. if m is set, we're called from dep_possible */
+ if (m || solver_is_updating(solv, p))
+ return 1;
+ }
+ return 0;
+}
+
+
+/* mirrors solver_dep_fulfilled, but returns 2 if a new package
+ * was involved */
+static int
+solver_dep_fulfilled_alreadyinstalled(Solver *solv, Id dep)
+{
+ Pool *pool = solv->pool;
+ Id p, pp;
+ int r;
+
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags == REL_COND)
+ {
+ int r1, r2;
+ if (ISRELDEP(rd->evr))
+ {
+ Reldep *rd2 = GETRELDEP(pool, rd->evr);
+ if (rd2->flags == REL_ELSE)
+ {
+ r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name);
+ if (r1)
+ {
+ r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+ return r2 && r1 == 2 ? 2 : r2;
+ }
+ return solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr);
+ }
+ }
+ r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+ r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
+ if (!r1 && !r2)
+ return 0;
+ return r1 == 2 ? 2 : 1;
+ }
+ if (rd->flags == REL_AND)
+ {
+ int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+ if (!r1)
+ return 0;
+ r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
+ if (!r2)
+ return 0;
+ return r1 == 2 || r2 == 2 ? 2 : 1;
+ }
+ if (rd->flags == REL_OR)
+ {
+ int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+ r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
+ if (!r1 && !r2)
+ return 0;
+ return r1 == 2 || r2 == 2 ? 2 : 1;
+ }
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+ return solver_splitprovides(solv, rd->evr, 0);
+ if (rd->flags == REL_NAMESPACE && solv->installsuppdepq)
+ {
+ Queue *q = solv->installsuppdepq;
+ int i;
+ for (i = 0; i < q->count; i++)
+ if (q->elements[i] == dep || q->elements[i] == rd->name)
+ return 2;
+ }
+ }
+ r = 0;
+ FOR_PROVIDES(p, pp, dep)
+ if (solv->decisionmap[p] > 0)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->repo && s->repo != solv->installed)
+ return 2;
+ r = 1;
+ }
+ return r;
+}
+
+static int
+solver_is_supplementing_alreadyinstalled(Solver *solv, Solvable *s)
+{
+ Id sup, *supp;
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (solver_dep_fulfilled_alreadyinstalled(solv, sup) == 2)
+ return 1;
+ return 0;
+}
+
+static Id
+autouninstall(Solver *solv, Id *problem)
+{
+ Pool *pool = solv->pool;
+ int i;
+ int lastfeature = 0, lastupdate = 0;
+ Id v;
+ Id extraflags = -1;
+ Map *m = 0;
+
+ if (!solv->allowuninstall && !solv->allowuninstall_all)
+ {
+ if (!solv->allowuninstallmap.size)
+ return 0; /* why did we get called? */
+ m = &solv->allowuninstallmap;
+ }
+ for (i = 0; (v = problem[i]) != 0; i++)
+ {
+ if (v < 0)
+ extraflags &= solv->job.elements[-v - 1];
+ if (v >= solv->updaterules && v < solv->updaterules_end)
+ {
+ Rule *r;
+ if (m && !MAPTST(m, v - solv->updaterules))
+ continue;
+ /* check if identical to feature rule, we don't like that (except for orphans) */
+ r = solv->rules + solv->featurerules + (v - solv->updaterules);
+ if (!r->p)
+ {
+ /* update rule == feature rule */
+ if (v > lastfeature)
+ lastfeature = v;
+ /* prefer orphaned packages in dup mode */
+ if (solv->keep_orphans)
+ {
+ r = solv->rules + v;
+ if (!r->d && r->p == (solv->installed->start + (v - solv->updaterules)))
+ {
+ lastfeature = v;
+ lastupdate = 0;
+ break;
+ }
+ }
+ continue;
+ }
+ if (v > lastupdate)
+ lastupdate = v;
+ }
+ }
+ if (!lastupdate && !lastfeature)
+ return 0;
+ v = lastupdate ? lastupdate : lastfeature;
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "allowuninstall disabling ");
+ solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + v);
+ solver_disableproblem(solv, v);
+ if (extraflags != -1 && (extraflags & SOLVER_CLEANDEPS) != 0 && solv->cleandepsmap.size)
+ {
+ /* add the package to the updatepkgs list, this will automatically turn
+ * on cleandeps mode */
+ Id p = solv->rules[v].p;
+ if (!solv->cleandeps_updatepkgs)
+ {
+ solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->cleandeps_updatepkgs);
+ }
+ if (p > 0)
+ {
+ int oldupdatepkgscnt = solv->cleandeps_updatepkgs->count;
+ queue_pushunique(solv->cleandeps_updatepkgs, p);
+ if (solv->cleandeps_updatepkgs->count != oldupdatepkgscnt)
+ solver_disablepolicyrules(solv);
+ }
+ }
+ return v;
+}
+
+/************************************************************************/
+
+/*
+ * enable/disable learnt rules
+ *
+ * we have enabled or disabled some of our rules. We now reenable all
+ * of our learnt rules except the ones that were learnt from rules that
+ * are now disabled.
+ */
+static void
+enabledisablelearntrules(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+ Id why, *whyp;
+ int i;
+
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "enabledisablelearntrules called\n");
+ for (i = solv->learntrules, r = solv->rules + i; i < solv->nrules; i++, r++)
+ {
+ whyp = solv->learnt_pool.elements + solv->learnt_why.elements[i - solv->learntrules];
+ while ((why = *whyp++) != 0)
+ {
+ assert(why > 0 && why < i);
+ if (solv->rules[why].d < 0)
+ break;
+ }
+ /* why != 0: we found a disabled rule, disable the learnt rule */
+ if (why && r->d >= 0)
+ {
+ IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "disabling ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
+ }
+ solver_disablerule(solv, r);
+ }
+ else if (!why && r->d < 0)
+ {
+ IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "re-enabling ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
+ }
+ solver_enablerule(solv, r);
+ }
+ }
+}
+
+
+/*
+ * make assertion rules into decisions
+ *
+ * Go through rules and add direct assertions to the decisionqueue.
+ * If we find a conflict, disable rules and add them to problem queue.
+ */
+
+static void
+makeruledecisions(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ int i, ri, ii;
+ Rule *r, *rr;
+ Id v, vv;
+ int decisionstart;
+ int record_proof = 1;
+ int oldproblemcount;
+ int havedisabled = 0;
+
+ /* The system solvable is always installed first */
+ assert(solv->decisionq.count == 0);
+ queue_push(&solv->decisionq, SYSTEMSOLVABLE);
+ queue_push(&solv->decisionq_why, 0);
+ solv->decisionmap[SYSTEMSOLVABLE] = 1; /* installed at level '1' */
+
+ decisionstart = solv->decisionq.count;
+ for (;;)
+ {
+ /* if we needed to re-run, back up decisions to decisionstart */
+ while (solv->decisionq.count > decisionstart)
+ {
+ v = solv->decisionq.elements[--solv->decisionq.count];
+ --solv->decisionq_why.count;
+ vv = v > 0 ? v : -v;
+ solv->decisionmap[vv] = 0;
+ }
+
+ /* note that the ruleassertions queue is ordered */
+ for (ii = 0; ii < solv->ruleassertions.count; ii++)
+ {
+ ri = solv->ruleassertions.elements[ii];
+ r = solv->rules + ri;
+
+ if (havedisabled && ri >= solv->learntrules)
+ {
+ /* just started with learnt rule assertions. If we have disabled
+ * some rules, adapt the learnt rule status */
+ enabledisablelearntrules(solv);
+ havedisabled = 0;
+ }
+
+ if (r->d < 0 || !r->p || r->w2) /* disabled, dummy or no assertion */
+ continue;
+
+ /* do weak rules in phase 2 */
+ if (ri < solv->learntrules && solv->weakrulemap.size && MAPTST(&solv->weakrulemap, ri))
+ continue;
+
+ v = r->p;
+ vv = v > 0 ? v : -v;
+
+ if (!solv->decisionmap[vv]) /* if not yet decided */
+ {
+ queue_push(&solv->decisionq, v);
+ queue_push(&solv->decisionq_why, r - solv->rules);
+ solv->decisionmap[vv] = v > 0 ? 1 : -1;
+ IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
+ {
+ Solvable *s = solv->pool->solvables + vv;
+ if (v < 0)
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (assertion)\n", pool_solvable2str(solv->pool, s));
+ else
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing %s (assertion)\n", pool_solvable2str(solv->pool, s));
+ }
+ continue;
+ }
+
+ /* check against previous decision: is there a conflict? */
+ if (v > 0 && solv->decisionmap[vv] > 0) /* ok to install */
+ continue;
+ if (v < 0 && solv->decisionmap[vv] < 0) /* ok to remove */
+ continue;
+
+ /*
+ * found a conflict!
+ *
+ * The rule (r) we're currently processing says something
+ * different (v = r->p) than a previous decision (decisionmap[abs(v)])
+ * on this literal
+ */
+
+ if (ri >= solv->learntrules)
+ {
+ /* conflict with a learnt rule */
+ /* can happen when packages cannot be installed for multiple reasons. */
+ /* we disable the learnt rule in this case */
+ /* (XXX: we should really call analyze_unsolvable_rule here!) */
+ solver_disablerule(solv, r);
+ continue;
+ }
+
+ /*
+ * find the decision which is the "opposite" of the rule
+ */
+ for (i = 0; i < solv->decisionq.count; i++)
+ if (solv->decisionq.elements[i] == -v)
+ break;
+ assert(i < solv->decisionq.count); /* assert that we found it */
+ oldproblemcount = solv->problems.count;
+
+ /*
+ * conflict with system solvable ?
+ */
+ if (v == -SYSTEMSOLVABLE)
+ {
+ if (record_proof)
+ {
+ queue_push(&solv->problems, solv->learnt_pool.count);
+ queue_push(&solv->learnt_pool, ri);
+ queue_push(&solv->learnt_pool, 0);
+ }
+ else
+ queue_push(&solv->problems, 0);
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with system solvable, disabling rule #%d\n", ri);
+ if (ri >= solv->jobrules && ri < solv->jobrules_end)
+ v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
+ else
+ v = ri;
+ queue_push(&solv->problems, v);
+ queue_push(&solv->problems, 0);
+ if (v >= solv->featurerules && v < solv->updaterules_end)
+ {
+ if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+ if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+ {
+ solv->problems.count = oldproblemcount;
+ havedisabled = 1;
+ break; /* start over */
+ }
+ }
+ solver_disableproblem(solv, v);
+ havedisabled = 1;
+ break; /* start over */
+ }
+
+ assert(solv->decisionq_why.elements[i] > 0);
+
+ /*
+ * conflict with a pkg rule ?
+ */
+ if (solv->decisionq_why.elements[i] < solv->pkgrules_end)
+ {
+ if (record_proof)
+ {
+ queue_push(&solv->problems, solv->learnt_pool.count);
+ queue_push(&solv->learnt_pool, ri);
+ queue_push(&solv->learnt_pool, solv->decisionq_why.elements[i]);
+ queue_push(&solv->learnt_pool, 0);
+ }
+ else
+ queue_push(&solv->problems, 0);
+ assert(v > 0 || v == -SYSTEMSOLVABLE);
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with pkg rule, disabling rule #%d\n", ri);
+ if (ri >= solv->jobrules && ri < solv->jobrules_end)
+ v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
+ else
+ v = ri;
+ queue_push(&solv->problems, v);
+ queue_push(&solv->problems, 0);
+ if (v >= solv->featurerules && v < solv->updaterules_end)
+ {
+ if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+ if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+ {
+ solv->problems.count = oldproblemcount;
+ havedisabled = 1;
+ break; /* start over */
+ }
+ }
+ solver_disableproblem(solv, v);
+ havedisabled = 1;
+ break; /* start over */
+ }
+
+ /*
+ * conflict with another job or update/feature rule
+ */
+
+ /* record proof */
+ if (record_proof)
+ {
+ queue_push(&solv->problems, solv->learnt_pool.count);
+ queue_push(&solv->learnt_pool, ri);
+ queue_push(&solv->learnt_pool, solv->decisionq_why.elements[i]);
+ queue_push(&solv->learnt_pool, 0);
+ }
+ else
+ queue_push(&solv->problems, 0);
+
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflicting update/job assertions over literal %d\n", vv);
+
+ /*
+ * push all of our rules (can only be feature or job rules)
+ * asserting this literal on the problem stack
+ */
+ for (i = solv->featurerules, rr = solv->rules + i; i < solv->learntrules; i++, rr++)
+ {
+ if (rr->d < 0 /* disabled */
+ || rr->w2) /* or no assertion */
+ continue;
+ if (rr->p != vv /* not affecting the literal */
+ && rr->p != -vv)
+ continue;
+ if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, i)) /* weak: silently ignore */
+ continue;
+
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, " - disabling rule #%d\n", i);
+ solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + i);
+
+ v = i;
+ if (i >= solv->jobrules && i < solv->jobrules_end)
+ v = -(solv->ruletojob.elements[i - solv->jobrules] + 1);
+ queue_push(&solv->problems, v);
+ }
+ queue_push(&solv->problems, 0);
+
+ if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+ if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+ {
+ solv->problems.count = oldproblemcount;
+ havedisabled = 1;
+ break; /* start over */
+ }
+
+ for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
+ solver_disableproblem(solv, solv->problems.elements[i]);
+ havedisabled = 1;
+ break; /* start over */
+ }
+ if (ii < solv->ruleassertions.count)
+ continue;
+
+ /*
+ * phase 2: now do the weak assertions
+ */
+ if (!solv->weakrulemap.size)
+ break; /* no weak rules, no phase 2 */
+ for (ii = 0; ii < solv->ruleassertions.count; ii++)
+ {
+ ri = solv->ruleassertions.elements[ii];
+ r = solv->rules + ri;
+ if (r->d < 0 || r->w2) /* disabled or no assertion */
+ continue;
+ if (ri >= solv->learntrules || !MAPTST(&solv->weakrulemap, ri)) /* skip non-weak */
+ continue;
+ v = r->p;
+ vv = v > 0 ? v : -v;
+
+ if (!solv->decisionmap[vv]) /* if not yet decided */
+ {
+ queue_push(&solv->decisionq, v);
+ queue_push(&solv->decisionq_why, r - solv->rules);
+ solv->decisionmap[vv] = v > 0 ? 1 : -1;
+ IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
+ {
+ Solvable *s = solv->pool->solvables + vv;
+ if (v < 0)
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (weak assertion)\n", pool_solvable2str(solv->pool, s));
+ else
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing %s (weak assertion)\n", pool_solvable2str(solv->pool, s));
+ }
+ continue;
+ }
+ /* check against previous decision: is there a conflict? */
+ if (v > 0 && solv->decisionmap[vv] > 0)
+ continue;
+ if (v < 0 && solv->decisionmap[vv] < 0)
+ continue;
+
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "assertion conflict, but I am weak, disabling ");
+ solver_printrule(solv, SOLV_DEBUG_UNSOLVABLE, r);
+
+ if (ri >= solv->jobrules && ri < solv->jobrules_end)
+ v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
+ else
+ v = ri;
+ solver_disableproblem(solv, v);
+ if (v < 0)
+ solver_reenablepolicyrules(solv, -v);
+ havedisabled = 1;
+ break; /* start over */
+ }
+ if (ii == solv->ruleassertions.count)
+ break; /* finished! */
+ }
+}
+
+
+/********************************************************************/
+/* watches */
+
+
+/*-------------------------------------------------------------------
+ * makewatches
+ *
+ * initial setup for all watches
+ */
+
+static void
+makewatches(Solver *solv)
+{
+ Rule *r;
+ int i;
+ int nsolvables = solv->pool->nsolvables;
+
+ solv_free(solv->watches);
+ /* lower half for removals, upper half for installs */
+ solv->watches = solv_calloc(2 * nsolvables, sizeof(Id));
+ for (i = 1, r = solv->rules + solv->nrules - 1; i < solv->nrules; i++, r--)
+ {
+ if (!r->w2) /* assertions do not need watches */
+ continue;
+
+ /* see addwatches_rule(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 a new learned rule)
+ * sets up watches for a single rule
+ *
+ * see also makewatches() above.
+ */
+
+static inline void
+addwatches_rule(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
+ */
+
+
+/* shortcuts to check if a literal (positive or negative) assignment
+ * evaluates to 'true' or 'false'
+ */
+#define DECISIONMAP_TRUE(p) ((p) > 0 ? (decisionmap[p] > 0) : (decisionmap[-p] < 0))
+#define DECISIONMAP_FALSE(p) ((p) > 0 ? (decisionmap[p] < 0) : (decisionmap[-p] > 0))
+#define DECISIONMAP_UNDEF(p) (decisionmap[(p) > 0 ? (p) : -(p)] == 0)
+
+/*-------------------------------------------------------------------
+ *
+ * propagate
+ *
+ * make decision and propagate to all rules
+ *
+ * Evaluate each term affected by the decision (linked through watches).
+ * If we find unit rules we make new decisions based on them.
+ *
+ * return : 0 = everything is OK
+ * rule = conflict found in this rule
+ */
+
+static Rule *
+propagate(Solver *solv, int level)
+{
+ Pool *pool = solv->pool;
+ Id *rp, *next_rp; /* rule pointer, next rule pointer in linked list */
+ Rule *r; /* rule */
+ Id p, pkg, other_watch;
+ Id *dp;
+ Id *decisionmap = solv->decisionmap;
+ Id *watches = solv->watches + pool->nsolvables; /* place ptr in middle */
+
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "----- propagate -----\n");
+
+ /* foreach non-propagated decision */
+ while (solv->propagate_index < solv->decisionq.count)
+ {
+ /*
+ * 'pkg' was just decided
+ * negate because our watches trigger if literal goes FALSE
+ */
+ pkg = -solv->decisionq.elements[solv->propagate_index++];
+
+ IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
+ {
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "propagate for decision %d level %d\n", -pkg, level);
+ solver_printruleelement(solv, SOLV_DEBUG_PROPAGATE, 0, -pkg);
+ }
+
+ /* foreach rule where 'pkg' is now FALSE */
+ for (rp = watches + pkg; *rp; rp = next_rp)
+ {
+ r = solv->rules + *rp;
+ if (r->d < 0)
+ {
+ /* rule is disabled, goto next */
+ if (pkg == r->w1)
+ next_rp = &r->n1;
+ else
+ next_rp = &r->n2;
+ continue;
+ }
+
+ IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
+ {
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE," watch triggered ");
+ solver_printrule(solv, SOLV_DEBUG_PROPAGATE, r);
+ }
+
+ /*
+ * 'pkg' was just decided (was set to FALSE), so this rule
+ * may now be unit.
+ */
+ /* find the other watch */
+ if (pkg == r->w1)
+ {
+ other_watch = r->w2;
+ next_rp = &r->n1;
+ }
+ else
+ {
+ other_watch = r->w1;
+ next_rp = &r->n2;
+ }
+
+ /*
+ * if the other watch is true we have nothing to do
+ */
+ if (DECISIONMAP_TRUE(other_watch))
+ continue;
+
+ /*
+ * The other literal is FALSE or UNDEF
+ *
+ */
+
+ if (r->d)
+ {
+ /* Not a binary clause, try to move our watch.
+ *
+ * Go over all literals and find one that is
+ * not other_watch
+ * and not FALSE
+ *
+ * (TRUE is also ok, in that case the rule is fulfilled)
+ * As speed matters here we do not use the FOR_RULELITERALS
+ * macro.
+ */
+ if (r->p /* we have a 'p' */
+ && r->p != other_watch /* which is not watched */
+ && !DECISIONMAP_FALSE(r->p)) /* and not FALSE */
+ {
+ p = r->p;
+ }
+ else /* go find a 'd' to make 'true' */
+ {
+ /* foreach p in 'd'
+ we just iterate sequentially, doing it in another order just changes the order of decisions, not the decisions itself
+ */
+ for (dp = pool->whatprovidesdata + r->d; (p = *dp++) != 0;)
+ {
+ if (p != other_watch /* which is not watched */
+ && !DECISIONMAP_FALSE(p)) /* and not FALSE */
+ break;
+ }
+ }
+
+ if (p)
+ {
+ /*
+ * if we found some p that is UNDEF or TRUE, move
+ * watch to it
+ */
+ IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
+ {
+ if (p > 0)
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> move w%d to %s\n", (pkg == r->w1 ? 1 : 2), pool_solvid2str(pool, p));
+ else
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> move w%d to !%s\n", (pkg == r->w1 ? 1 : 2), pool_solvid2str(pool, -p));
+ }
+
+ *rp = *next_rp;
+ next_rp = 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;
+ }
+ /* search failed, thus all unwatched literals are FALSE */
+
+ } /* not binary */
+
+ /*
+ * unit clause found, set literal other_watch to TRUE
+ */
+
+ if (DECISIONMAP_FALSE(other_watch)) /* check if literal is FALSE */
+ return r; /* eek, a conflict! */
+
+ IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
+ {
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " unit ");
+ solver_printrule(solv, SOLV_DEBUG_PROPAGATE, r);
+ }
+
+ if (other_watch > 0)
+ decisionmap[other_watch] = level; /* install! */
+ else
+ decisionmap[-other_watch] = -level; /* remove! */
+
+ queue_push(&solv->decisionq, other_watch);
+ queue_push(&solv->decisionq_why, r - solv->rules);
+
+ IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
+ {
+ if (other_watch > 0)
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> decided to install %s\n", pool_solvid2str(pool, other_watch));
+ else
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> decided to conflict %s\n", pool_solvid2str(pool, -other_watch));
+ }
+
+ } /* foreach rule involving 'pkg' */
+
+ } /* while we have non-decided decisions */
+
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "----- propagate end-----\n");
+
+ return 0; /* all is well */
+}
+
+
+/********************************************************************/
+/* Analysis */
+
+/*-------------------------------------------------------------------
+ *
+ * revert
+ * revert decisionq to a level
+ */
+
+static void
+revert(Solver *solv, int level)
+{
+ Pool *pool = solv->pool;
+ 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;
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "reverting decision %d at %d\n", v, solv->decisionmap[vv]);
+ solv->decisionmap[vv] = 0;
+ solv->decisionq.count--;
+ solv->decisionq_why.count--;
+ solv->propagate_index = solv->decisionq.count;
+ }
+ while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] >= level)
+ solv->branches.count -= solv->branches.elements[solv->branches.count - 2];
+ if (solv->recommends_index > solv->decisionq.count)
+ solv->recommends_index = -1; /* rebuild recommends/suggests maps */
+ if (solv->decisionq.count < solv->decisioncnt_jobs)
+ solv->decisioncnt_jobs = 0;
+ if (solv->decisionq.count < solv->decisioncnt_update)
+ solv->decisioncnt_update = 0;
+ if (solv->decisionq.count < solv->decisioncnt_keep)
+ solv->decisioncnt_keep = 0;
+ if (solv->decisionq.count < solv->decisioncnt_resolve)
+ solv->decisioncnt_resolve = 0;
+ if (solv->decisionq.count < solv->decisioncnt_weak)
+ solv->decisioncnt_weak= 0;
+ if (solv->decisionq.count < solv->decisioncnt_orphan)
+ solv->decisioncnt_orphan = 0;
+}
+
+/*-------------------------------------------------------------------
+ *
+ * watch2onhighest - put watch2 on literal with highest level
+ */
+
+static inline void
+watch2onhighest(Solver *solv, Rule *r)
+{
+ int l, wl = 0;
+ Id d, v, *dp;
+
+ d = r->d < 0 ? -r->d - 1 : r->d;
+ if (!d)
+ return; /* binary rule, both watches are set */
+ dp = solv->pool->whatprovidesdata + 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;
+ }
+ }
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * analyze
+ * and learn
+ */
+
+static int
+analyze(Solver *solv, int level, Rule *c, Rule **lrp)
+{
+ Pool *pool = solv->pool;
+ Queue q;
+ Rule *r;
+ Id q_buf[8];
+ int rlevel = 1;
+ Map seen; /* global? */
+ Id p = 0, pp, v, vv, why;
+ int l, i, idx;
+ int num = 0, l1num = 0;
+ int learnt_why = solv->learnt_pool.count;
+ Id *decisionmap = solv->decisionmap;
+
+ queue_init_buffer(&q, q_buf, sizeof(q_buf)/sizeof(*q_buf));
+
+ POOL_DEBUG(SOLV_DEBUG_ANALYZE, "ANALYZE at %d ----------------------\n", level);
+ map_init(&seen, pool->nsolvables);
+ idx = solv->decisionq.count;
+ for (;;)
+ {
+ IF_POOLDEBUG (SOLV_DEBUG_ANALYZE)
+ solver_printruleclass(solv, SOLV_DEBUG_ANALYZE, c);
+ queue_push(&solv->learnt_pool, c - solv->rules);
+ FOR_RULELITERALS(v, pp, c)
+ {
+ if (DECISIONMAP_TRUE(v)) /* the one true literal */
+ continue;
+ vv = v > 0 ? v : -v;
+ if (MAPTST(&seen, vv))
+ continue;
+ MAPSET(&seen, vv); /* mark that we also need to look at this literal */
+ l = solv->decisionmap[vv];
+ if (l < 0)
+ l = -l;
+ if (l == 1)
+ l1num++; /* need to do this one in level1 pass */
+ else if (l == level)
+ num++; /* need to do this one as well */
+ else
+ {
+ queue_push(&q, v); /* not level1 or conflict level, add to new rule */
+ if (l > rlevel)
+ rlevel = l;
+ }
+ }
+l1retry:
+ if (!num && !--l1num)
+ break; /* all literals done */
+
+ /* find the next literal to investigate */
+ /* (as num + l1num > 0, we know that we'll always find one) */
+ for (;;)
+ {
+ assert(idx > 0);
+ v = solv->decisionq.elements[--idx];
+ vv = v > 0 ? v : -v;
+ if (MAPTST(&seen, vv))
+ break;
+ }
+ MAPCLR(&seen, vv);
+
+ if (num && --num == 0)
+ {
+ /* done with normal literals, now start level 1 literal processing */
+ p = -v; /* so that v doesn't get lost */
+ if (!l1num)
+ break;
+ POOL_DEBUG(SOLV_DEBUG_ANALYZE, "got %d involved level 1 decisions\n", l1num);
+ /* clear non-l1 bits from seen map */
+ for (i = 0; i < q.count; i++)
+ {
+ v = q.elements[i];
+ MAPCLR(&seen, v > 0 ? v : -v);
+ }
+ /* only level 1 marks left in seen map */
+ l1num++; /* as l1retry decrements it */
+ goto l1retry;
+ }
+
+ why = solv->decisionq_why.elements[idx];
+ if (why <= 0) /* just in case, maybe for SYSTEMSOLVABLE */
+ goto l1retry;
+ c = solv->rules + why;
+ }
+ map_free(&seen);
+ assert(p != 0);
+ assert(rlevel > 0 && rlevel < level);
+ IF_POOLDEBUG (SOLV_DEBUG_ANALYZE)
+ {
+ POOL_DEBUG(SOLV_DEBUG_ANALYZE, "learned rule for level %d (am %d)\n", rlevel, level);
+ solver_printruleelement(solv, SOLV_DEBUG_ANALYZE, 0, p);
+ for (i = 0; i < q.count; i++)
+ solver_printruleelement(solv, SOLV_DEBUG_ANALYZE, 0, q.elements[i]);
+ }
+ /* push end marker on learnt reasons stack */
+ queue_push(&solv->learnt_pool, 0);
+ solv->stats_learned++;
+
+ POOL_DEBUG(SOLV_DEBUG_ANALYZE, "reverting decisions (level %d -> %d)\n", level, rlevel);
+ level = rlevel;
+ revert(solv, level);
+ if (q.count < 2)
+ {
+ Id d = q.count ? q.elements[0] : 0;
+ queue_free(&q);
+ r = solver_addrule(solv, p, d, 0);
+ }
+ else
+ {
+ Id d = pool_queuetowhatprovides(pool, &q);
+ queue_free(&q);
+ r = solver_addrule(solv, p, 0, d);
+ }
+ assert(solv->learnt_why.count == (r - solv->rules) - solv->learntrules);
+ queue_push(&solv->learnt_why, learnt_why);
+ if (r->w2)
+ {
+ /* needs watches */
+ watch2onhighest(solv, r);
+ addwatches_rule(solv, r);
+ }
+ else
+ {
+ /* rule is an assertion */
+ queue_push(&solv->ruleassertions, r - solv->rules);
+ }
+ *lrp = r;
+ return level;
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * solver_reset
+ *
+ * reset all solver decisions
+ * called after rules have been enabled/disabled
+ */
+
+void
+solver_reset(Solver *solv)
+{
+ int i;
+ Id v;
+
+ /* rewind all decisions */
+ for (i = solv->decisionq.count - 1; i >= 0; i--)
+ {
+ v = solv->decisionq.elements[i];
+ solv->decisionmap[v > 0 ? v : -v] = 0;
+ }
+ queue_empty(&solv->decisionq_why);
+ queue_empty(&solv->decisionq);
+ solv->recommends_index = -1;
+ solv->propagate_index = 0;
+ solv->decisioncnt_update = solv->decisioncnt_keep = solv->decisioncnt_resolve = solv->decisioncnt_weak = solv->decisioncnt_orphan = 0;
+ queue_empty(&solv->branches);
+
+ /* adapt learnt rule status to new set of enabled/disabled rules */
+ enabledisablelearntrules(solv);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * analyze_unsolvable_rule
+ *
+ * recursion helper used by analyze_unsolvable
+ */
+
+static void
+analyze_unsolvable_rule(Solver *solv, Rule *r, Id *lastweakp, Map *rseen)
+{
+ Pool *pool = solv->pool;
+ int i;
+ Id why = r - solv->rules;
+
+ IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE)
+ solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, r);
+ if (solv->learntrules && why >= solv->learntrules)
+ {
+ if (MAPTST(rseen, why - solv->learntrules))
+ return;
+ MAPSET(rseen, why - solv->learntrules);
+ for (i = solv->learnt_why.elements[why - solv->learntrules]; solv->learnt_pool.elements[i]; i++)
+ if (solv->learnt_pool.elements[i] > 0)
+ analyze_unsolvable_rule(solv, solv->rules + solv->learnt_pool.elements[i], lastweakp, rseen);
+ return;
+ }
+ if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, why))
+ if (!*lastweakp || why > *lastweakp)
+ *lastweakp = why;
+ /* do not add pkg rules to problem */
+ if (why < solv->pkgrules_end)
+ return;
+ /* turn rule into problem */
+ if (why >= solv->jobrules && why < solv->jobrules_end)
+ why = -(solv->ruletojob.elements[why - solv->jobrules] + 1);
+ /* normalize dup/infarch rules */
+ if (why > solv->infarchrules && why < solv->infarchrules_end)
+ {
+ Id name = pool->solvables[-solv->rules[why].p].name;
+ while (why > solv->infarchrules && pool->solvables[-solv->rules[why - 1].p].name == name)
+ why--;
+ }
+ if (why > solv->duprules && why < solv->duprules_end)
+ {
+ Id name = pool->solvables[-solv->rules[why].p].name;
+ while (why > solv->duprules && pool->solvables[-solv->rules[why - 1].p].name == name)
+ why--;
+ }
+
+ /* return if problem already countains our rule */
+ if (solv->problems.count)
+ {
+ for (i = solv->problems.count - 1; i >= 0; i--)
+ if (solv->problems.elements[i] == 0) /* end of last problem reached? */
+ break;
+ else if (solv->problems.elements[i] == why)
+ return;
+ }
+ queue_push(&solv->problems, why);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * analyze_unsolvable (called from setpropagatelearn)
+ *
+ * We know that the problem is not solvable. Record all involved
+ * rules (i.e. the "proof") into solv->learnt_pool.
+ * Record the learnt pool index and all non-pkg rules into
+ * solv->problems. (Our solutions to fix the problems are to
+ * disable those rules.)
+ *
+ * If the proof contains at least one weak rule, we disable the
+ * last of them.
+ *
+ * Otherwise we return -1 if disablerules is not set or disable
+ * _all_ of the problem rules and return 0.
+ *
+ * return: 0 - disabled some rules, try again
+ * -1 - hopeless
+ */
+
+static int
+analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+ Map involved; /* global to speed things up? */
+ Map rseen;
+ Id pp, v, vv, why;
+ int i, idx;
+ Id *decisionmap = solv->decisionmap;
+ int oldproblemcount;
+ int oldlearntpoolcount;
+ Id lastweak;
+ int record_proof = 1;
+
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "ANALYZE UNSOLVABLE ----------------------\n");
+ solv->stats_unsolvable++;
+ oldproblemcount = solv->problems.count;
+ oldlearntpoolcount = solv->learnt_pool.count;
+
+ /* make room for proof index */
+ /* must update it later, as analyze_unsolvable_rule would confuse
+ * it with a rule index if we put the real value in already */
+ queue_push(&solv->problems, 0);
+
+ r = cr;
+ map_init(&involved, pool->nsolvables);
+ map_init(&rseen, solv->learntrules ? solv->nrules - solv->learntrules : 0);
+ if (record_proof)
+ queue_push(&solv->learnt_pool, r - solv->rules);
+ lastweak = 0;
+ analyze_unsolvable_rule(solv, r, &lastweak, &rseen);
+ FOR_RULELITERALS(v, pp, r)
+ {
+ if (DECISIONMAP_TRUE(v)) /* the one true literal */
+ continue;
+ vv = v > 0 ? v : -v;
+ MAPSET(&involved, vv);
+ }
+ idx = solv->decisionq.count;
+ while (idx > 0)
+ {
+ v = solv->decisionq.elements[--idx];
+ vv = v > 0 ? v : -v;
+ if (!MAPTST(&involved, vv) || vv == SYSTEMSOLVABLE)
+ continue;
+ why = solv->decisionq_why.elements[idx];
+ assert(why > 0);
+ if (record_proof)
+ queue_push(&solv->learnt_pool, why);
+ r = solv->rules + why;
+ analyze_unsolvable_rule(solv, r, &lastweak, &rseen);
+ FOR_RULELITERALS(v, pp, r)
+ {
+ if (DECISIONMAP_TRUE(v)) /* the one true literal */
+ continue;
+ vv = v > 0 ? v : -v;
+ MAPSET(&involved, vv);
+ }
+ }
+ map_free(&involved);
+ map_free(&rseen);
+ queue_push(&solv->problems, 0); /* mark end of this problem */
+
+ if (lastweak)
+ {
+ /* disable last weak rule */
+ solv->problems.count = oldproblemcount;
+ solv->learnt_pool.count = oldlearntpoolcount;
+ if (lastweak >= solv->jobrules && lastweak < solv->jobrules_end)
+ v = -(solv->ruletojob.elements[lastweak - solv->jobrules] + 1);
+ else
+ v = lastweak;
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "disabling ");
+ solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + lastweak);
+ if (lastweak >= solv->choicerules && lastweak < solv->choicerules_end)
+ solver_disablechoicerules(solv, solv->rules + lastweak);
+ solver_disableproblem(solv, v);
+ if (v < 0)
+ solver_reenablepolicyrules(solv, -v);
+ solver_reset(solv);
+ return 0;
+ }
+
+ if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+ if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+ {
+ solv->problems.count = oldproblemcount;
+ solv->learnt_pool.count = oldlearntpoolcount;
+ solver_reset(solv);
+ return 0;
+ }
+
+ /* finish proof */
+ if (record_proof)
+ {
+ queue_push(&solv->learnt_pool, 0);
+ solv->problems.elements[oldproblemcount] = oldlearntpoolcount;
+ }
+
+ /* + 2: index + trailing zero */
+ if (disablerules && oldproblemcount + 2 < solv->problems.count)
+ {
+ for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
+ solver_disableproblem(solv, solv->problems.elements[i]);
+ /* XXX: might want to enable all weak rules again */
+ solver_reset(solv);
+ return 0;
+ }
+ POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "UNSOLVABLE\n");
+ return -1;
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * setpropagatelearn
+ *
+ * add free decision (solvable to install) to decisionq
+ * increase level and propagate decision
+ * return if no conflict.
+ *
+ * in conflict case, analyze conflict rule, add resulting
+ * rule to learnt rule set, make decision from learnt
+ * rule (always unit) and re-propagate.
+ *
+ * returns the new solver level or -1 if unsolvable
+ *
+ */
+
+static int
+setpropagatelearn(Solver *solv, int level, Id decision, int disablerules, Id ruleid)
+{
+ Pool *pool = solv->pool;
+ Rule *r, *lr;
+
+ if (decision)
+ {
+ level++;
+ if (decision > 0)
+ solv->decisionmap[decision] = level;
+ else
+ solv->decisionmap[-decision] = -level;
+ queue_push(&solv->decisionq, decision);
+ queue_push(&solv->decisionq_why, -ruleid); /* <= 0 -> free decision */
+ }
+ assert(ruleid >= 0 && level > 0);
+ for (;;)
+ {
+ r = propagate(solv, level);
+ if (!r)
+ break;
+ if (level == 1)
+ return analyze_unsolvable(solv, r, disablerules);
+ POOL_DEBUG(SOLV_DEBUG_ANALYZE, "conflict with rule #%d\n", (int)(r - solv->rules));
+ level = analyze(solv, level, r, &lr);
+ /* the new rule is unit by design */
+ decision = lr->p;
+ solv->decisionmap[decision > 0 ? decision : -decision] = decision > 0 ? level : -level;
+ queue_push(&solv->decisionq, decision);
+ queue_push(&solv->decisionq_why, lr - solv->rules);
+ IF_POOLDEBUG (SOLV_DEBUG_ANALYZE)
+ {
+ POOL_DEBUG(SOLV_DEBUG_ANALYZE, "decision: ");
+ solver_printruleelement(solv, SOLV_DEBUG_ANALYZE, 0, decision);
+ POOL_DEBUG(SOLV_DEBUG_ANALYZE, "new rule: ");
+ solver_printrule(solv, SOLV_DEBUG_ANALYZE, lr);
+ }
+ }
+ return level;
+}
+
+static void
+reorder_dq_for_jobrules(Solver *solv, int level, Queue *dq)
+{
+ Pool *pool = solv->pool;
+ int i, j, haveone = 0, dqcount = dq->count;
+ int decisionqcount = solv->decisionq.count;
+ Id p;
+ Solvable *s;
+
+ /* at the time we process jobrules the installed packages are not kept yet */
+ /* reorder so that "future-supplemented" packages come first */
+ FOR_REPO_SOLVABLES(solv->installed, p, s)
+ {
+ if (MAPTST(&solv->noupdate, p - solv->installed->start))
+ continue;
+ if (solv->decisionmap[p] == 0)
+ {
+ if (s->recommends || s->suggests)
+ queue_push(&solv->decisionq, p);
+ solv->decisionmap[p] = level + 1;
+ haveone = 1;
+ }
+ }
+ if (!haveone)
+ return;
+ policy_update_recommendsmap(solv);
+ for (i = 0; i < dqcount; i++)
+ {
+ p = dq->elements[i];
+ if (!(pool->solvables[p].repo == solv->installed || MAPTST(&solv->suggestsmap, p) || solver_is_enhancing(solv, pool->solvables + p)))
+ {
+ queue_push(dq, p);
+ dq->elements[i] = 0;
+ }
+ }
+ dqcount = dq->count;
+ for (i = 0; i < dqcount; i++)
+ {
+ p = dq->elements[i];
+ if (p && !(pool->solvables[p].repo == solv->installed || MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
+ {
+ queue_push(dq, p);
+ dq->elements[i] = 0;
+ }
+ }
+ for (i = j = 0; i < dq->count; i++)
+ if (dq->elements[i])
+ dq->elements[j++] = dq->elements[i];
+ queue_truncate(dq, j);
+ FOR_REPO_SOLVABLES(solv->installed, p, s)
+ if (solv->decisionmap[p] == level + 1)
+ solv->decisionmap[p] = 0;
+ if (solv->decisionq.count != decisionqcount)
+ {
+ solv->recommends_index = -1;
+ queue_truncate(&solv->decisionq, decisionqcount);
+ }
+}
+
+/*-------------------------------------------------------------------
+ *
+ * branch handling
+ */
+
+static void
+createbranch(Solver *solv, int level, Queue *dq, Id p, Id data)
+{
+ Pool *pool = solv->pool;
+ int i;
+ IF_POOLDEBUG (SOLV_DEBUG_POLICY)
+ {
+ POOL_DEBUG (SOLV_DEBUG_POLICY, "creating a branch:\n");
+ for (i = 0; i < dq->count; i++)
+ POOL_DEBUG (SOLV_DEBUG_POLICY, " - %s\n", pool_solvid2str(pool, dq->elements[i]));
+ }
+ queue_push(&solv->branches, -dq->elements[0]);
+ for (i = 1; i < dq->count; i++)
+ queue_push(&solv->branches, dq->elements[i]);
+ queue_push2(&solv->branches, p, data);
+ queue_push2(&solv->branches, dq->count + 4, level);
+}
+
+static int
+takebranch(Solver *solv, int pos, int end, const char *msg, int disablerules)
+{
+ Pool *pool = solv->pool;
+ int level;
+ Id p, why;
+#if 0
+ {
+ int i;
+ printf("branch group level %d [%d-%d] %d %d:\n", solv->branches.elements[end - 1], start, end, solv->branches.elements[end - 4], solv->branches.elements[end - 3]);
+ for (i = end - solv->branches.elements[end - 2]; i < end - 4; i++)
+ printf("%c %c%s\n", i == pos ? 'x' : ' ', solv->branches.elements[i] >= 0 ? ' ' : '-', pool_solvid2str(pool, solv->branches.elements[i] >= 0 ? solv->branches.elements[i] : -solv->branches.elements[i]));
+ }
+#endif
+ level = solv->branches.elements[end - 1];
+ p = solv->branches.elements[pos];
+ solv->branches.elements[pos] = -p;
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "%s %d -> %d with %s\n", msg, solv->decisionmap[p], level, pool_solvid2str(pool, p));
+ /* hack: set level to zero so that revert does not remove the branch */
+ solv->branches.elements[end - 1] = 0;
+ revert(solv, level);
+ solv->branches.elements[end - 1] = level;
+ /* hack: revert simply sets the count, so we can still access the reverted elements */
+ why = -solv->decisionq_why.elements[solv->decisionq_why.count];
+ assert(why >= 0);
+ return setpropagatelearn(solv, level, p, disablerules, why);
+}
+
+/*-------------------------------------------------------------------
+ *
+ * select and install
+ *
+ * install best package from the queue. We add an extra package, inst, if
+ * provided. See comment in weak install section.
+ *
+ * returns the new solver level or -1 if unsolvable
+ *
+ */
+
+static int
+selectandinstall(Solver *solv, int level, Queue *dq, int disablerules, Id ruleid)
+{
+ Pool *pool = solv->pool;
+ Id p;
+
+ if (dq->count > 1)
+ policy_filter_unwanted(solv, dq, POLICY_MODE_CHOOSE);
+ /* if we're resolving job rules and didn't resolve the installed packages yet,
+ * do some special supplements ordering */
+ if (dq->count > 1 && ruleid >= solv->jobrules && ruleid < solv->jobrules_end && solv->installed && !solv->focus_installed)
+ reorder_dq_for_jobrules(solv, level, dq);
+ /* if we have multiple candidates we open a branch */
+ if (dq->count > 1)
+ createbranch(solv, level, dq, 0, ruleid);
+ p = dq->elements[0];
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "installing %s\n", pool_solvid2str(pool, p));
+ return setpropagatelearn(solv, level, p, disablerules, ruleid);
+}
+
+
+/********************************************************************/
+/* Main solver interface */
+
+
+/*-------------------------------------------------------------------
+ *
+ * solver_create
+ * create solver structure
+ *
+ * pool: all available solvables
+ * installed: installed Solvables
+ *
+ *
+ * Upon solving, rules are created to flag the Solvables
+ * of the 'installed' Repo as installed.
+ */
+
+Solver *
+solver_create(Pool *pool)
+{
+ Solver *solv;
+ solv = (Solver *)solv_calloc(1, sizeof(Solver));
+ solv->pool = pool;
+ solv->installed = pool->installed;
+
+ solv->allownamechange = 1;
+
+ solv->dup_allowdowngrade = 1;
+ solv->dup_allownamechange = 1;
+ solv->dup_allowarchchange = 1;
+ solv->dup_allowvendorchange = 1;
+
+ solv->keepexplicitobsoletes = pool->noobsoletesmultiversion ? 0 : 1;
+
+ queue_init(&solv->ruletojob);
+ queue_init(&solv->decisionq);
+ queue_init(&solv->decisionq_why);
+ queue_init(&solv->problems);
+ queue_init(&solv->orphaned);
+ queue_init(&solv->learnt_why);
+ queue_init(&solv->learnt_pool);
+ queue_init(&solv->branches);
+ queue_init(&solv->weakruleq);
+ queue_init(&solv->ruleassertions);
+ queue_init(&solv->addedmap_deduceq);
+
+ queue_push(&solv->learnt_pool, 0); /* so that 0 does not describe a proof */
+
+ map_init(&solv->recommendsmap, pool->nsolvables);
+ map_init(&solv->suggestsmap, pool->nsolvables);
+ map_init(&solv->noupdate, solv->installed ? solv->installed->end - solv->installed->start : 0);
+ solv->recommends_index = 0;
+
+ solv->decisionmap = (Id *)solv_calloc(pool->nsolvables, sizeof(Id));
+ solv->nrules = 1;
+ solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
+ memset(solv->rules, 0, sizeof(Rule));
+
+ return solv;
+}
+
+
+static int
+resolve_jobrules(Solver *solv, int level, int disablerules, Queue *dq)
+{
+ Pool *pool = solv->pool;
+ int oldlevel = level;
+ int i, olevel;
+ Rule *r;
+
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving job rules\n");
+ if (!solv->decisioncnt_jobs)
+ solv->decisioncnt_jobs = solv->decisionq.count;
+ for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++)
+ {
+ Id l, pp;
+ if (r->d < 0) /* ignore disabled rules */
+ continue;
+ queue_empty(dq);
+ FOR_RULELITERALS(l, pp, r)
+ {
+ if (l < 0)
+ {
+ if (solv->decisionmap[-l] <= 0)
+ break;
+ }
+ else
+ {
+ if (solv->decisionmap[l] > 0)
+ break;
+ if (solv->decisionmap[l] == 0)
+ queue_push(dq, l);
+ }
+ }
+ if (l || !dq->count)
+ continue;
+ /* prune to installed if not updating */
+ if (dq->count > 1 && solv->installed && !solv->updatemap_all &&
+ !(solv->job.elements[solv->ruletojob.elements[i - solv->jobrules]] & SOLVER_ORUPDATE))
+ {
+ int j, k;
+ for (j = k = 0; j < dq->count; j++)
+ {
+ Solvable *s = pool->solvables + dq->elements[j];
+ if (s->repo == solv->installed)
+ {
+ dq->elements[k++] = dq->elements[j];
+ if (solv->updatemap.size && MAPTST(&solv->updatemap, dq->elements[j] - solv->installed->start))
+ {
+ k = 0; /* package wants to be updated, do not prune */
+ break;
+ }
+ }
+ }
+ if (k)
+ dq->count = k;
+ }
+ olevel = level;
+ level = selectandinstall(solv, level, dq, disablerules, i);
+ if (level <= olevel)
+ {
+ if (level == olevel)
+ {
+ i--;
+ r--;
+ continue; /* try something else */
+ }
+ if (level < oldlevel)
+ return level;
+ /* redo from start of jobrules */
+ i = solv->jobrules - 1;
+ r = solv->rules + i;
+ }
+ }
+ return level;
+}
+
+/*-------------------------------------------------------------------
+ *
+ * solver_free
+ */
+
+static inline void
+queuep_free(Queue **qp)
+{
+ if (!*qp)
+ return;
+ queue_free(*qp);
+ *qp = solv_free(*qp);
+}
+
+static inline void
+map_zerosize(Map *m)
+{
+ if (m->size)
+ {
+ map_free(m);
+ map_init(m, 0);
+ }
+}
+
+void
+solver_free(Solver *solv)
+{
+ queue_free(&solv->job);
+ queue_free(&solv->ruletojob);
+ queue_free(&solv->decisionq);
+ queue_free(&solv->decisionq_why);
+ queue_free(&solv->learnt_why);
+ queue_free(&solv->learnt_pool);
+ queue_free(&solv->problems);
+ queue_free(&solv->solutions);
+ queue_free(&solv->orphaned);
+ queue_free(&solv->branches);
+ queue_free(&solv->weakruleq);
+ queue_free(&solv->ruleassertions);
+ queue_free(&solv->addedmap_deduceq);
+ queuep_free(&solv->cleandeps_updatepkgs);
+ queuep_free(&solv->cleandeps_mistakes);
+ queuep_free(&solv->update_targets);
+ queuep_free(&solv->installsuppdepq);
+ queuep_free(&solv->recommendscplxq);
+ queuep_free(&solv->suggestscplxq);
+ queuep_free(&solv->brokenorphanrules);
+
+ map_free(&solv->recommendsmap);
+ map_free(&solv->suggestsmap);
+ map_free(&solv->noupdate);
+ map_free(&solv->weakrulemap);
+ map_free(&solv->multiversion);
+
+ map_free(&solv->updatemap);
+ map_free(&solv->bestupdatemap);
+ map_free(&solv->fixmap);
+ map_free(&solv->dupmap);
+ map_free(&solv->dupinvolvedmap);
+ map_free(&solv->droporphanedmap);
+ map_free(&solv->cleandepsmap);
+ map_free(&solv->allowuninstallmap);
+
+ solv_free(solv->decisionmap);
+ solv_free(solv->rules);
+ solv_free(solv->watches);
+ solv_free(solv->obsoletes);
+ solv_free(solv->obsoletes_data);
+ solv_free(solv->specialupdaters);
+ solv_free(solv->choicerules_ref);
+ solv_free(solv->bestrules_pkg);
+ solv_free(solv->yumobsrules_info);
+ solv_free(solv->instbuddy);
+ solv_free(solv);
+}
+
+int
+solver_get_flag(Solver *solv, int flag)
+{
+ switch (flag)
+ {
+ case SOLVER_FLAG_ALLOW_DOWNGRADE:
+ return solv->allowdowngrade;
+ case SOLVER_FLAG_ALLOW_NAMECHANGE:
+ return solv->allownamechange;
+ case SOLVER_FLAG_ALLOW_ARCHCHANGE:
+ return solv->allowarchchange;
+ case SOLVER_FLAG_ALLOW_VENDORCHANGE:
+ return solv->allowvendorchange;
+ case SOLVER_FLAG_ALLOW_UNINSTALL:
+ return solv->allowuninstall;
+ case SOLVER_FLAG_NO_UPDATEPROVIDE:
+ return solv->noupdateprovide;
+ case SOLVER_FLAG_SPLITPROVIDES:
+ return solv->dosplitprovides;
+ case SOLVER_FLAG_IGNORE_RECOMMENDED:
+ return solv->dontinstallrecommended;
+ case SOLVER_FLAG_ADD_ALREADY_RECOMMENDED:
+ return solv->addalreadyrecommended;
+ case SOLVER_FLAG_NO_INFARCHCHECK:
+ return solv->noinfarchcheck;
+ case SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES:
+ return solv->keepexplicitobsoletes;
+ case SOLVER_FLAG_BEST_OBEY_POLICY:
+ return solv->bestobeypolicy;
+ case SOLVER_FLAG_NO_AUTOTARGET:
+ return solv->noautotarget;
+ case SOLVER_FLAG_DUP_ALLOW_DOWNGRADE:
+ return solv->dup_allowdowngrade;
+ case SOLVER_FLAG_DUP_ALLOW_NAMECHANGE:
+ return solv->dup_allownamechange;
+ case SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE:
+ return solv->dup_allowarchchange;
+ case SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE:
+ return solv->dup_allowvendorchange;
+ case SOLVER_FLAG_KEEP_ORPHANS:
+ return solv->keep_orphans;
+ case SOLVER_FLAG_BREAK_ORPHANS:
+ return solv->break_orphans;
+ case SOLVER_FLAG_FOCUS_INSTALLED:
+ return solv->focus_installed;
+ case SOLVER_FLAG_YUM_OBSOLETES:
+ return solv->do_yum_obsoletes;
+ case SOLVER_FLAG_NEED_UPDATEPROVIDE:
+ return solv->needupdateprovide;
+ default:
+ break;
+ }
+ return -1;
+}
+
+int
+solver_set_flag(Solver *solv, int flag, int value)
+{
+ int old = solver_get_flag(solv, flag);
+ switch (flag)
+ {
+ case SOLVER_FLAG_ALLOW_DOWNGRADE:
+ solv->allowdowngrade = value;
+ break;
+ case SOLVER_FLAG_ALLOW_NAMECHANGE:
+ solv->allownamechange = value;
+ break;
+ case SOLVER_FLAG_ALLOW_ARCHCHANGE:
+ solv->allowarchchange = value;
+ break;
+ case SOLVER_FLAG_ALLOW_VENDORCHANGE:
+ solv->allowvendorchange = value;
+ break;
+ case SOLVER_FLAG_ALLOW_UNINSTALL:
+ solv->allowuninstall = value;
+ break;
+ case SOLVER_FLAG_NO_UPDATEPROVIDE:
+ solv->noupdateprovide = value;
+ break;
+ case SOLVER_FLAG_SPLITPROVIDES:
+ solv->dosplitprovides = value;
+ break;
+ case SOLVER_FLAG_IGNORE_RECOMMENDED:
+ solv->dontinstallrecommended = value;
+ break;
+ case SOLVER_FLAG_ADD_ALREADY_RECOMMENDED:
+ solv->addalreadyrecommended = value;
+ break;
+ case SOLVER_FLAG_NO_INFARCHCHECK:
+ solv->noinfarchcheck = value;
+ break;
+ case SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES:
+ solv->keepexplicitobsoletes = value;
+ break;
+ case SOLVER_FLAG_BEST_OBEY_POLICY:
+ solv->bestobeypolicy = value;
+ break;
+ case SOLVER_FLAG_NO_AUTOTARGET:
+ solv->noautotarget = value;
+ break;
+ case SOLVER_FLAG_DUP_ALLOW_DOWNGRADE:
+ solv->dup_allowdowngrade = value;
+ break;
+ case SOLVER_FLAG_DUP_ALLOW_NAMECHANGE:
+ solv->dup_allownamechange = value;
+ break;
+ case SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE:
+ solv->dup_allowarchchange = value;
+ break;
+ case SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE:
+ solv->dup_allowvendorchange = value;
+ break;
+ case SOLVER_FLAG_KEEP_ORPHANS:
+ solv->keep_orphans = value;
+ break;
+ case SOLVER_FLAG_BREAK_ORPHANS:
+ solv->break_orphans = value;
+ break;
+ case SOLVER_FLAG_FOCUS_INSTALLED:
+ solv->focus_installed = value;
+ break;
+ case SOLVER_FLAG_YUM_OBSOLETES:
+ solv->do_yum_obsoletes = value;
+ break;
+ case SOLVER_FLAG_NEED_UPDATEPROVIDE:
+ solv->needupdateprovide = value;
+ break;
+ default:
+ break;
+ }
+ return old;
+}
+
+static int
+cleandeps_check_mistakes(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+ Id p, pp;
+ int i;
+ int mademistake = 0;
+
+ if (!solv->cleandepsmap.size)
+ return 0;
+ /* check for mistakes */
+ for (i = solv->installed->start; i < solv->installed->end; i++)
+ {
+ if (!MAPTST(&solv->cleandepsmap, i - solv->installed->start))
+ continue;
+ r = solv->rules + solv->featurerules + (i - solv->installed->start);
+ /* a mistake is when the featurerule is true but the updaterule is false */
+ if (!r->p)
+ continue;
+ FOR_RULELITERALS(p, pp, r)
+ if (p > 0 && solv->decisionmap[p] > 0)
+ break;
+ if (!p)
+ continue; /* feature rule is not true */
+ r = solv->rules + solv->updaterules + (i - solv->installed->start);
+ if (!r->p)
+ continue;
+ FOR_RULELITERALS(p, pp, r)
+ if (p > 0 && solv->decisionmap[p] > 0)
+ break;
+ if (p)
+ continue; /* update rule is true */
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps mistake: ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLVER, r);
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "feature rule: ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLVER, solv->rules + solv->featurerules + (i - solv->installed->start));
+ if (!solv->cleandeps_mistakes)
+ {
+ solv->cleandeps_mistakes = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->cleandeps_mistakes);
+ }
+ queue_push(solv->cleandeps_mistakes, i);
+ MAPCLR(&solv->cleandepsmap, i - solv->installed->start);
+ solver_reenablepolicyrules_cleandeps(solv, i);
+ mademistake = 1;
+ }
+ return mademistake;
+}
+
+static void
+prune_to_update_targets(Solver *solv, Id *cp, Queue *q)
+{
+ int i, j;
+ Id p, *cp2;
+ for (i = j = 0; i < q->count; i++)
+ {
+ p = q->elements[i];
+ for (cp2 = cp; *cp2; cp2++)
+ if (*cp2 == p)
+ {
+ q->elements[j++] = p;
+ break;
+ }
+ }
+ queue_truncate(q, j);
+}
+
+#ifdef ENABLE_COMPLEX_DEPS
+
+static void
+add_complex_recommends(Solver *solv, Id rec, Queue *dq, Map *dqmap)
+{
+ Pool *pool = solv->pool;
+ int oldcnt = dq->count;
+ int cutcnt, blkcnt;
+ Id p;
+ int i, j;
+
+#if 0
+ printf("ADD_COMPLEX_RECOMMENDS %s\n", pool_dep2str(pool, rec));
+#endif
+ i = pool_normalize_complex_dep(pool, rec, dq, CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ return;
+ cutcnt = dq->count;
+ for (i = oldcnt; i < cutcnt; i++)
+ {
+ blkcnt = dq->count;
+ for (; (p = dq->elements[i]) != 0; i++)
+ {
+ if (p < 0)
+ {
+ if (solv->decisionmap[-p] <= 0)
+ break;
+ continue;
+ }
+ if (solv->decisionmap[p] > 0)
+ {
+ queue_truncate(dq, blkcnt);
+ break;
+ }
+ if (dqmap)
+ {
+ if (!MAPTST(dqmap, p))
+ continue;
+ }
+ else
+ {
+ if (solv->decisionmap[p] < 0)
+ continue;
+ if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
+ continue;
+ }
+ queue_push(dq, p);
+ }
+ while (dq->elements[i])
+ i++;
+ }
+ queue_deleten(dq, oldcnt, cutcnt - oldcnt);
+ /* unify */
+ if (dq->count != oldcnt)
+ {
+ for (j = oldcnt; j < dq->count; j++)
+ {
+ p = dq->elements[j];
+ for (i = 0; i < j; i++)
+ if (dq->elements[i] == p)
+ {
+ dq->elements[j] = 0;
+ break;
+ }
+ }
+ for (i = j = oldcnt; j < dq->count; j++)
+ if (dq->elements[j])
+ dq->elements[i++] = dq->elements[j];
+ queue_truncate(dq, i);
+ }
+#if 0
+ printf("RETURN:\n");
+ for (i = oldcnt; i < dq->count; i++)
+ printf(" - %s\n", pool_solvid2str(pool, dq->elements[i]));
+#endif
+}
+
+static void
+do_complex_recommendations(Solver *solv, Id rec, Map *m, int noselected)
+{
+ Pool *pool = solv->pool;
+ Queue dq;
+ Id p;
+ int i, blk;
+
+#if 0
+ printf("DO_COMPLEX_RECOMMENDATIONS %s\n", pool_dep2str(pool, rec));
+#endif
+ queue_init(&dq);
+ i = pool_normalize_complex_dep(pool, rec, &dq, CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ {
+ queue_free(&dq);
+ return;
+ }
+ for (i = 0; i < dq.count; i++)
+ {
+ blk = i;
+ for (; (p = dq.elements[i]) != 0; i++)
+ {
+ if (p < 0)
+ {
+ if (solv->decisionmap[-p] <= 0)
+ break;
+ continue;
+ }
+ if (solv->decisionmap[p] > 0)
+ {
+ if (noselected)
+ break;
+ MAPSET(m, p);
+ for (i++; (p = dq.elements[i]) != 0; i++)
+ if (p > 0 && solv->decisionmap[p] > 0)
+ MAPSET(m, p);
+ p = 1;
+ break;
+ }
+ }
+ if (!p)
+ {
+ for (i = blk; (p = dq.elements[i]) != 0; i++)
+ if (p > 0)
+ MAPSET(m, p);
+ }
+ while (dq.elements[i])
+ i++;
+ }
+ queue_free(&dq);
+}
+
+#endif
+
+/*-------------------------------------------------------------------
+ *
+ * solver_run_sat
+ *
+ * all rules have been set up, now actually run the solver
+ *
+ */
+
+void
+solver_run_sat(Solver *solv, int disablerules, int doweak)
+{
+ Queue dq; /* local decisionqueue */
+ Queue dqs; /* local decisionqueue for supplements */
+ int systemlevel;
+ int level, olevel;
+ Rule *r;
+ int i, j, n;
+ Solvable *s;
+ Pool *pool = solv->pool;
+ Id p, pp, *dp, postponed;
+ int minimizationsteps;
+ int installedpos = solv->installed ? solv->installed->start : 0;
+
+ IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
+ {
+ POOL_DEBUG (SOLV_DEBUG_RULE_CREATION, "number of rules: %d\n", solv->nrules);
+ for (i = 1; i < solv->nrules; i++)
+ solver_printruleclass(solv, SOLV_DEBUG_RULE_CREATION, solv->rules + i);
+ }
+
+ /* start SAT algorithm */
+ level = 0;
+ systemlevel = level + 1;
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "solving...\n");
+
+ queue_init(&dq);
+ queue_init(&dqs);
+
+ /*
+ * here's the main loop:
+ * 1) decide assertion rules and propagate
+ * 2) fulfill jobs
+ * 3) try to keep installed packages
+ * 4) fulfill all unresolved rules
+ * 5) install recommended packages
+ * 6) minimalize solution if we had choices
+ * if we encounter a problem, we rewind to a safe level and restart
+ * with step 1
+ */
+
+ minimizationsteps = 0;
+ for (;;)
+ {
+ /*
+ * initial propagation of the assertions
+ */
+ if (level <= 0)
+ {
+ if (level < 0)
+ break;
+ makeruledecisions(solv);
+ level = 1;
+ if (!disablerules && solv->problems.count)
+ {
+ level = -1;
+ break;
+ }
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "initial propagate (propagate_index: %d; size decisionq: %d)...\n", solv->propagate_index, solv->decisionq.count);
+ if ((r = propagate(solv, level)) != 0)
+ {
+ level = analyze_unsolvable(solv, r, disablerules);
+ continue;
+ }
+ systemlevel = level + 1;
+ }
+
+ /*
+ * resolve jobs first (unless focus_installed is set)
+ */
+ if (level < systemlevel && !solv->focus_installed)
+ {
+ olevel = level;
+ level = resolve_jobrules(solv, level, disablerules, &dq);
+ if (level < olevel)
+ continue;
+ systemlevel = level + 1;
+ }
+
+
+ /*
+ * installed packages
+ */
+ if (!solv->decisioncnt_update)
+ solv->decisioncnt_update = solv->decisionq.count;
+ if (level < systemlevel && solv->installed && solv->installed->nsolvables && !solv->installed->disabled)
+ {
+ Repo *installed = solv->installed;
+ int pass;
+
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving installed packages\n");
+ /* we use two passes if we need to update packages
+ * to create a better user experience */
+ for (pass = solv->updatemap.size ? 0 : 1; pass < 2; pass++)
+ {
+ int passlevel = level;
+ Id *specialupdaters = solv->specialupdaters;
+ if (pass == 1 && !solv->decisioncnt_keep)
+ solv->decisioncnt_keep = solv->decisionq.count;
+ /* start with installedpos, the position that gave us problems the last time */
+ for (i = installedpos, n = installed->start; n < installed->end; i++, n++)
+ {
+ Rule *rr;
+ Id d;
+
+ if (i == installed->end)
+ i = installed->start;
+ s = pool->solvables + i;
+ if (s->repo != installed)
+ continue;
+
+ if (solv->decisionmap[i] > 0 && (!specialupdaters || !specialupdaters[i - installed->start]))
+ continue; /* already decided */
+ if (!pass && solv->updatemap.size && !MAPTST(&solv->updatemap, i - installed->start))
+ continue; /* updates first */
+ r = solv->rules + solv->updaterules + (i - installed->start);
+ rr = r;
+ if (!rr->p || rr->d < 0) /* disabled -> look at feature rule */
+ rr -= solv->installed->end - solv->installed->start;
+ if (!rr->p) /* identical to update rule? */
+ rr = r;
+ if (!rr->p && !(specialupdaters && specialupdaters[i - installed->start]))
+ continue; /* orpaned package */
+
+ /* check if we should update this package to the latest version
+ * noupdate is set for erase jobs, in that case we want to deinstall
+ * the installed package and not replace it with a newer version
+ * rr->p != i is for dup jobs where the installed package cannot be kept */
+ queue_empty(&dq);
+ if (!MAPTST(&solv->noupdate, i - installed->start) && (solv->decisionmap[i] < 0 || solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, i - installed->start)) || (rr->p && rr->p != i)))
+ {
+ if (!rr->p)
+ {
+ /* specialupdater with no update/feature rule */
+ for (d = specialupdaters[i - installed->start]; (p = pool->whatprovidesdata[d++]) != 0; )
+ {
+ if (solv->decisionmap[p] > 0)
+ {
+ dq.count = 0;
+ break;
+ }
+ if (!solv->decisionmap[p])
+ queue_push(&dq, p);
+ }
+ }
+ else if (specialupdaters && (d = specialupdaters[i - installed->start]) != 0)
+ {
+ /* special multiversion handling, make sure best version is chosen */
+ if (rr->p == i && solv->decisionmap[i] >= 0)
+ queue_push(&dq, i);
+ while ((p = pool->whatprovidesdata[d++]) != 0)
+ if (solv->decisionmap[p] >= 0)
+ queue_push(&dq, p);
+ if (dq.count && solv->update_targets && solv->update_targets->elements[i - installed->start])
+ prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[i - installed->start], &dq);
+ if (dq.count)
+ {
+ policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
+ p = dq.elements[0];
+ if (p != i && solv->decisionmap[p] == 0)
+ {
+ rr = solv->rules + solv->featurerules + (i - solv->installed->start);
+ if (!rr->p) /* update rule == feature rule? */
+ rr = rr - solv->featurerules + solv->updaterules;
+ dq.count = 1;
+ }
+ else
+ dq.count = 0;
+ }
+ }
+ else
+ {
+ /* update to best package of the update rule */
+ FOR_RULELITERALS(p, pp, rr)
+ {
+ if (solv->decisionmap[p] > 0)
+ {
+ dq.count = 0; /* already fulfilled */
+ break;
+ }
+ if (!solv->decisionmap[p])
+ queue_push(&dq, p);
+ }
+ }
+ }
+ if (dq.count && solv->update_targets && solv->update_targets->elements[i - installed->start])
+ prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[i - installed->start], &dq);
+ /* install best version */
+ if (dq.count)
+ {
+ olevel = level;
+ level = selectandinstall(solv, level, &dq, disablerules, rr - solv->rules);
+ if (level <= olevel)
+ {
+ if (level < passlevel)
+ break; /* trouble */
+ if (level < olevel)
+ n = installed->start; /* redo all */
+ i--;
+ n--;
+ continue;
+ }
+ }
+ /* if still undecided keep package */
+ if (solv->decisionmap[i] == 0)
+ {
+ olevel = level;
+ if (solv->cleandepsmap.size && MAPTST(&solv->cleandepsmap, i - installed->start))
+ {
+#if 0
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "cleandeps erasing %s\n", pool_solvid2str(pool, i));
+ level = setpropagatelearn(solv, level, -i, disablerules, 0);
+#else
+ continue;
+#endif
+ }
+ else
+ {
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "keeping %s\n", pool_solvid2str(pool, i));
+ level = setpropagatelearn(solv, level, i, disablerules, r - solv->rules);
+ }
+ if (level <= olevel)
+ {
+ if (level < passlevel)
+ break; /* trouble */
+ if (level < olevel)
+ n = installed->start; /* redo all */
+ i--;
+ n--;
+ continue; /* retry with learnt rule */
+ }
+ }
+ }
+ if (n < installed->end)
+ {
+ installedpos = i; /* retry problem solvable next time */
+ break; /* ran into trouble */
+ }
+ installedpos = installed->start; /* reset installedpos */
+ }
+ systemlevel = level + 1;
+ if (pass < 2)
+ continue; /* had trouble, retry */
+ }
+ if (!solv->decisioncnt_keep)
+ solv->decisioncnt_keep = solv->decisionq.count;
+
+ if (level < systemlevel && solv->focus_installed)
+ {
+ olevel = level;
+ level = resolve_jobrules(solv, level, disablerules, &dq);
+ if (level < olevel)
+ continue;
+ systemlevel = level + 1;
+ }
+
+ if (level < systemlevel)
+ systemlevel = level;
+
+ /*
+ * decide
+ */
+ if (!solv->decisioncnt_resolve)
+ solv->decisioncnt_resolve = solv->decisionq.count;
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "deciding unresolved rules\n");
+ postponed = 0;
+ for (i = 1, n = 1; ; i++, n++)
+ {
+ if (n >= solv->nrules)
+ {
+ if (postponed <= 0)
+ break;
+ i = postponed;
+ postponed = -1;
+ n = 1;
+ }
+ if (i == solv->nrules)
+ i = 1;
+ r = solv->rules + i;
+ if (r->d < 0) /* ignore disabled rules */
+ continue;
+ if (r->p < 0) /* most common cases first */
+ {
+ if (r->d == 0 || solv->decisionmap[-r->p] <= 0)
+ continue;
+ }
+ if (dq.count)
+ queue_empty(&dq);
+ if (r->d == 0)
+ {
+ /* binary or unary rule */
+ /* need two positive undecided literals, r->p already checked above */
+ if (r->w2 <= 0)
+ continue;
+ if (solv->decisionmap[r->p] || solv->decisionmap[r->w2])
+ continue;
+ queue_push(&dq, r->p);
+ queue_push(&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
+ * (decisionmap[-r->p] for the r->p < 0 case is already checked above)
+ */
+ if (r->p >= 0)
+ {
+ if (solv->decisionmap[r->p] > 0)
+ continue;
+ if (solv->decisionmap[r->p] == 0)
+ queue_push(&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)
+ queue_push(&dq, p);
+ }
+ }
+ if (p)
+ continue;
+ }
+ IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
+ {
+ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "unfulfilled ");
+ solver_printruleclass(solv, SOLV_DEBUG_PROPAGATE, r);
+ }
+ /* dq.count < 2 cannot happen as this means that
+ * the rule is unit */
+ assert(dq.count > 1);
+
+ /* prune to cleandeps packages */
+ if (solv->cleandepsmap.size && solv->installed)
+ {
+ Repo *installed = solv->installed;
+ for (j = 0; j < dq.count; j++)
+ if (pool->solvables[dq.elements[j]].repo == installed && MAPTST(&solv->cleandepsmap, dq.elements[j] - installed->start))
+ break;
+ if (j < dq.count)
+ {
+ dq.elements[0] = dq.elements[j];
+ queue_truncate(&dq, 1);
+ }
+ }
+
+ if (dq.count > 1 && postponed >= 0)
+ {
+ policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE_NOREORDER);
+ if (dq.count > 1)
+ {
+ if (!postponed)
+ postponed = i;
+ continue;
+ }
+ }
+
+ olevel = level;
+ level = selectandinstall(solv, level, &dq, disablerules, r - solv->rules);
+ if (level < systemlevel)
+ break; /* trouble */
+ /* something changed, so look at all rules again */
+ n = 0;
+ }
+
+ if (n < solv->nrules) /* ran into trouble? */
+ continue; /* start over */
+
+ /* decide leftover cleandeps packages */
+ if (solv->cleandepsmap.size && solv->installed)
+ {
+ for (p = solv->installed->start; p < solv->installed->end; p++)
+ {
+ s = pool->solvables + p;
+ if (s->repo != solv->installed)
+ continue;
+ if (solv->decisionmap[p] == 0 && MAPTST(&solv->cleandepsmap, p - solv->installed->start))
+ {
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "cleandeps erasing %s\n", pool_solvid2str(pool, p));
+ olevel = level;
+ level = setpropagatelearn(solv, level, -p, 0, 0);
+ if (level < olevel)
+ break;
+ }
+ }
+ if (p < solv->installed->end)
+ continue;
+ }
+
+ /* at this point we have a consistent system. now do the extras... */
+
+ if (!solv->decisioncnt_weak)
+ solv->decisioncnt_weak = solv->decisionq.count;
+ if (doweak)
+ {
+ int qcount;
+
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "installing recommended packages\n");
+ queue_empty(&dq); /* recommended packages */
+ queue_empty(&dqs); /* supplemented packages */
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ if (solv->decisionmap[i] < 0)
+ continue;
+ if (solv->decisionmap[i] > 0)
+ {
+ /* installed, check for recommends */
+ Id *recp, rec, pp, p;
+ s = pool->solvables + i;
+ if (!solv->addalreadyrecommended && s->repo == solv->installed)
+ continue;
+ /* XXX need to special case AND ? */
+ if (s->recommends)
+ {
+ recp = s->repo->idarraydata + s->recommends;
+ while ((rec = *recp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, rec))
+ {
+ add_complex_recommends(solv, rec, &dq, 0);
+ continue;
+ }
+#endif
+ qcount = dq.count;
+ FOR_PROVIDES(p, pp, rec)
+ {
+ if (solv->decisionmap[p] > 0)
+ {
+ dq.count = qcount;
+ break;
+ }
+ else if (solv->decisionmap[p] == 0)
+ {
+ if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
+ continue;
+ queue_pushunique(&dq, p);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ s = pool->solvables + i;
+ if (!s->supplements)
+ continue;
+ if (!pool_installable(pool, s))
+ continue;
+ if (!solver_is_supplementing(solv, s))
+ continue;
+ if (solv->dupmap_all && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start))))
+ continue;
+ queue_push(&dqs, i);
+ }
+ }
+
+ /* filter out all packages obsoleted by installed packages */
+ /* this is no longer needed if we have reverse obsoletes */
+ if ((dqs.count || dq.count) && solv->installed)
+ {
+ Map obsmap;
+ Id obs, *obsp, po, ppo;
+
+ map_init(&obsmap, pool->nsolvables);
+ for (p = solv->installed->start; p < solv->installed->end; p++)
+ {
+ s = pool->solvables + p;
+ if (s->repo != solv->installed || !s->obsoletes)
+ continue;
+ if (solv->decisionmap[p] <= 0)
+ continue;
+ if (!solv->keepexplicitobsoletes && solv->multiversion.size && MAPTST(&solv->multiversion, p))
+ continue;
+ obsp = s->repo->idarraydata + s->obsoletes;
+ /* foreach obsoletes */
+ while ((obs = *obsp++) != 0)
+ FOR_PROVIDES(po, ppo, obs)
+ {
+ Solvable *pos = pool->solvables + po;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pos, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, pos))
+ continue;
+ MAPSET(&obsmap, po);
+ }
+ }
+ for (i = j = 0; i < dqs.count; i++)
+ if (!MAPTST(&obsmap, dqs.elements[i]))
+ dqs.elements[j++] = dqs.elements[i];
+ dqs.count = j;
+ for (i = j = 0; i < dq.count; i++)
+ if (!MAPTST(&obsmap, dq.elements[i]))
+ dq.elements[j++] = dq.elements[i];
+ dq.count = j;
+ map_free(&obsmap);
+ }
+
+ /* filter out all already supplemented packages if requested */
+ if (!solv->addalreadyrecommended && dqs.count)
+ {
+ /* filter out old supplements */
+ for (i = j = 0; i < dqs.count; i++)
+ {
+ p = dqs.elements[i];
+ s = pool->solvables + p;
+ if (s->supplements && solver_is_supplementing_alreadyinstalled(solv, s))
+ dqs.elements[j++] = p;
+ }
+ dqs.count = j;
+ }
+
+ /* multiversion doesn't mix well with supplements.
+ * filter supplemented packages where we already decided
+ * to install a different version (see bnc#501088) */
+ if (dqs.count && solv->multiversion.size)
+ {
+ for (i = j = 0; i < dqs.count; i++)
+ {
+ p = dqs.elements[i];
+ if (MAPTST(&solv->multiversion, p))
+ {
+ Id p2, pp2;
+ s = pool->solvables + p;
+ FOR_PROVIDES(p2, pp2, s->name)
+ if (solv->decisionmap[p2] > 0 && pool->solvables[p2].name == s->name)
+ break;
+ if (p2)
+ continue; /* ignore this package */
+ }
+ dqs.elements[j++] = p;
+ }
+ dqs.count = j;
+ }
+
+ /* make dq contain both recommended and supplemented pkgs */
+ if (dqs.count)
+ {
+ for (i = 0; i < dqs.count; i++)
+ queue_pushunique(&dq, dqs.elements[i]);
+ }
+
+ if (dq.count)
+ {
+ Map dqmap;
+ int decisioncount = solv->decisionq.count;
+
+ if (dq.count == 1)
+ {
+ /* simple case, just one package. no need to choose to best version */
+ p = dq.elements[0];
+ if (dqs.count)
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "installing supplemented %s\n", pool_solvid2str(pool, p));
+ else
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "installing recommended %s\n", pool_solvid2str(pool, p));
+ level = setpropagatelearn(solv, level, p, 0, 0);
+ continue; /* back to main loop */
+ }
+
+ /* filter packages, this gives us the best versions */
+ policy_filter_unwanted(solv, &dq, POLICY_MODE_RECOMMEND);
+
+ /* create map of result */
+ map_init(&dqmap, pool->nsolvables);
+ for (i = 0; i < dq.count; i++)
+ MAPSET(&dqmap, dq.elements[i]);
+
+ /* install all supplemented packages */
+ for (i = 0; i < dqs.count; i++)
+ {
+ p = dqs.elements[i];
+ if (solv->decisionmap[p] || !MAPTST(&dqmap, p))
+ continue;
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "installing supplemented %s\n", pool_solvid2str(pool, p));
+ olevel = level;
+ level = setpropagatelearn(solv, level, p, 0, 0);
+ if (level <= olevel)
+ break;
+ }
+ if (i < dqs.count || solv->decisionq.count < decisioncount)
+ {
+ map_free(&dqmap);
+ continue;
+ }
+
+ /* install all recommended packages */
+ /* more work as we want to created branches if multiple
+ * choices are valid */
+ for (i = 0; i < decisioncount; i++)
+ {
+ Id rec, *recp, pp;
+ p = solv->decisionq.elements[i];
+ if (p < 0)
+ continue;
+ s = pool->solvables + p;
+ if (!s->repo || (!solv->addalreadyrecommended && s->repo == solv->installed))
+ continue;
+ if (!s->recommends)
+ continue;
+ recp = s->repo->idarraydata + s->recommends;
+ while ((rec = *recp++) != 0)
+ {
+ queue_empty(&dq);
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, rec))
+ add_complex_recommends(solv, rec, &dq, &dqmap);
+ else
+#endif
+ FOR_PROVIDES(p, pp, rec)
+ {
+ if (solv->decisionmap[p] > 0)
+ {
+ dq.count = 0;
+ break;
+ }
+ else if (solv->decisionmap[p] == 0 && MAPTST(&dqmap, p))
+ queue_push(&dq, p);
+ }
+ if (!dq.count)
+ continue;
+ if (dq.count > 1)
+ policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
+ /* if we have multiple candidates we open a branch */
+ if (dq.count > 1)
+ createbranch(solv, level, &dq, s - pool->solvables, rec);
+ p = dq.elements[0];
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "installing recommended %s\n", pool_solvid2str(pool, p));
+ olevel = level;
+ level = setpropagatelearn(solv, level, p, 0, 0);
+ if (level <= olevel || solv->decisionq.count < decisioncount)
+ break; /* we had to revert some decisions */
+ }
+ if (rec)
+ break; /* had a problem above, quit loop */
+ }
+ map_free(&dqmap);
+ continue; /* back to main loop so that all deps are checked */
+ }
+ }
+
+ if (!solv->decisioncnt_orphan)
+ solv->decisioncnt_orphan = solv->decisionq.count;
+ if (solv->installed && (solv->orphaned.count || solv->brokenorphanrules))
+ {
+ int installedone = 0;
+
+ /* let's see if we can install some unsupported package */
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "deciding orphaned packages\n");
+ for (i = 0; i < solv->orphaned.count; i++)
+ {
+ p = solv->orphaned.elements[i];
+ if (solv->decisionmap[p])
+ continue; /* already decided */
+ if (solv->droporphanedmap_all)
+ continue;
+ if (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))
+ continue;
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "keeping orphaned %s\n", pool_solvid2str(pool, p));
+ olevel = level;
+ level = setpropagatelearn(solv, level, p, 0, 0);
+ installedone = 1;
+ if (level < olevel)
+ break;
+ }
+ if (installedone || i < solv->orphaned.count)
+ continue; /* back to main loop */
+ for (i = 0; i < solv->orphaned.count; i++)
+ {
+ p = solv->orphaned.elements[i];
+ if (solv->decisionmap[p])
+ continue; /* already decided */
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "removing orphaned %s\n", pool_solvid2str(pool, p));
+ olevel = level;
+ level = setpropagatelearn(solv, level, -p, 0, 0);
+ if (level < olevel)
+ break;
+ }
+ if (i < solv->orphaned.count)
+ continue; /* back to main loop */
+ if (solv->brokenorphanrules)
+ {
+ solver_check_brokenorphanrules(solv, &dq);
+ if (dq.count)
+ {
+ policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
+ for (i = 0; i < dq.count; i++)
+ {
+ p = dq.elements[i];
+ POOL_DEBUG(SOLV_DEBUG_POLICY, "installing orphaned dep %s\n", pool_solvid2str(pool, p));
+ olevel = level;
+ level = setpropagatelearn(solv, level, p, 0, 0);
+ if (level < olevel)
+ break;
+ }
+ continue;
+ }
+ }
+ }
+
+ /* one final pass to make sure we decided all installed packages */
+ if (solv->installed)
+ {
+ for (p = solv->installed->start; p < solv->installed->end; p++)
+ {
+ if (solv->decisionmap[p])
+ continue; /* already decided */
+ s = pool->solvables + p;
+ if (s->repo != solv->installed)
+ continue;
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "removing unwanted %s\n", pool_solvid2str(pool, p));
+ olevel = level;
+ level = setpropagatelearn(solv, level, -p, 0, 0);
+ if (level < olevel)
+ break;
+ }
+ if (p < solv->installed->end)
+ continue; /* back to main loop */
+ }
+
+ if (solv->installed && solv->cleandepsmap.size && cleandeps_check_mistakes(solv))
+ {
+ solver_reset(solv);
+ level = 0; /* restart from scratch */
+ continue;
+ }
+
+ if (solv->solution_callback)
+ {
+ solv->solution_callback(solv, solv->solution_callback_data);
+ if (solv->branches.count)
+ {
+ int l, endi = 0;
+ p = l = 0;
+ for (i = solv->branches.count - 1; i >= 0; i--)
+ {
+ p = solv->branches.elements[i];
+ if (p > 0 && !l)
+ {
+ endi = i + 1;
+ l = p;
+ i -= 3; /* skip: p data count */
+ }
+ else if (p > 0)
+ break;
+ else if (p < 0)
+ l = 0;
+ }
+ if (i >= 0)
+ {
+ while (i > 0 && solv->branches.elements[i - 1] > 0)
+ i--;
+ level = takebranch(solv, i, endi, "branching", disablerules);
+ continue;
+ }
+ }
+ /* all branches done, we're finally finished */
+ break;
+ }
+
+ /* auto-minimization step */
+ if (solv->branches.count)
+ {
+ int endi, lasti = -1, lastiend = -1;
+ if (solv->recommends_index < solv->decisionq.count)
+ policy_update_recommendsmap(solv);
+ for (endi = solv->branches.count; endi > 0;)
+ {
+ int l, lastsi = -1, starti = endi - solv->branches.elements[endi - 2];
+ l = solv->branches.elements[endi - 1];
+ for (i = starti; i < endi - 4; i++)
+ {
+ p = solv->branches.elements[i];
+ if (p <= 0)
+ continue;
+ if (solv->decisionmap[p] > l)
+ {
+ lasti = i;
+ lastiend = endi;
+ lastsi = -1;
+ break;
+ }
+ if (lastsi < 0 && (MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
+ lastsi = i;
+ }
+ if (lastsi >= 0)
+ {
+ /* we have a recommended package that could not be installed */
+ /* take it if our current selection is not recommended */
+ for (i = starti; i < endi - 4; i++)
+ {
+ p = -solv->branches.elements[i];
+ if (p <= 0 || solv->decisionmap[p] != l + 1)
+ continue;
+ if (!(MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
+ {
+ lasti = lastsi;
+ lastiend = endi;
+ break;
+ }
+ }
+ }
+ endi = starti;
+ }
+ if (lasti >= 0)
+ {
+ minimizationsteps++;
+ level = takebranch(solv, lasti, lastiend, "minimizing", disablerules);
+ continue; /* back to main loop */
+ }
+ }
+ /* no minimization found, we're finally finished! */
+ break;
+ }
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "solver statistics: %d learned rules, %d unsolvable, %d minimization steps\n", solv->stats_learned, solv->stats_unsolvable, minimizationsteps);
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "done solving.\n\n");
+ queue_free(&dq);
+ queue_free(&dqs);
+ if (level < 0)
+ {
+ /* unsolvable */
+ solv->decisioncnt_jobs = solv->decisionq.count;
+ solv->decisioncnt_update = solv->decisionq.count;
+ solv->decisioncnt_keep = solv->decisionq.count;
+ solv->decisioncnt_resolve = solv->decisionq.count;
+ solv->decisioncnt_weak = solv->decisionq.count;
+ solv->decisioncnt_orphan = solv->decisionq.count;
+ }
+#if 0
+ solver_printdecisionq(solv, SOLV_DEBUG_RESULT);
+#endif
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * remove disabled conflicts
+ *
+ * purpose: update the decisionmap after some rules were disabled.
+ * this is used to calculate the suggested/recommended package list.
+ * Also returns a "removed" list to undo the discisionmap changes.
+ */
+
+static void
+removedisabledconflicts(Solver *solv, Queue *removed)
+{
+ Pool *pool = solv->pool;
+ int i, n;
+ Id p, why, *dp;
+ Id new;
+ Rule *r;
+ Id *decisionmap = solv->decisionmap;
+
+ queue_empty(removed);
+ for (i = 0; i < solv->decisionq.count; i++)
+ {
+ p = solv->decisionq.elements[i];
+ if (p > 0)
+ continue; /* conflicts only, please */
+ why = solv->decisionq_why.elements[i];
+ if (why == 0)
+ {
+ /* no rule involved, must be a orphan package drop */
+ continue;
+ }
+ /* we never do conflicts on free decisions, so there
+ * must have been an unit rule */
+ assert(why > 0);
+ r = solv->rules + why;
+ if (r->d < 0 && decisionmap[-p])
+ {
+ /* rule is now disabled, remove from decisionmap */
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "removing conflict for package %s[%d]\n", pool_solvid2str(pool, -p), -p);
+ queue_push(removed, -p);
+ queue_push(removed, decisionmap[-p]);
+ decisionmap[-p] = 0;
+ }
+ }
+ if (!removed->count)
+ return;
+ /* we removed some confliced packages. some of them might still
+ * be in conflict, so search for unit rules and re-conflict */
+ new = 0;
+ for (i = n = 1, r = solv->rules + i; n < solv->nrules; i++, r++, n++)
+ {
+ if (i == solv->nrules)
+ {
+ i = 1;
+ r = solv->rules + i;
+ }
+ if (r->d < 0)
+ continue;
+ if (!r->w2)
+ {
+ if (r->p < 0 && !decisionmap[-r->p])
+ new = r->p;
+ }
+ else if (!r->d)
+ {
+ /* binary rule */
+ if (r->p < 0 && decisionmap[-r->p] == 0 && DECISIONMAP_FALSE(r->w2))
+ new = r->p;
+ else if (r->w2 < 0 && decisionmap[-r->w2] == 0 && DECISIONMAP_FALSE(r->p))
+ new = r->w2;
+ }
+ else
+ {
+ if (r->p < 0 && decisionmap[-r->p] == 0)
+ new = r->p;
+ if (new || DECISIONMAP_FALSE(r->p))
+ {
+ dp = pool->whatprovidesdata + r->d;
+ while ((p = *dp++) != 0)
+ {
+ if (new && p == new)
+ continue;
+ if (p < 0 && decisionmap[-p] == 0)
+ {
+ if (new)
+ {
+ new = 0;
+ break;
+ }
+ new = p;
+ }
+ else if (!DECISIONMAP_FALSE(p))
+ {
+ new = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (new)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "re-conflicting package %s[%d]\n", pool_solvid2str(pool, -new), -new);
+ decisionmap[-new] = -1;
+ new = 0;
+ n = 0; /* redo all rules */
+ }
+ }
+}
+
+static inline void
+undo_removedisabledconflicts(Solver *solv, Queue *removed)
+{
+ int i;
+ for (i = 0; i < removed->count; i += 2)
+ solv->decisionmap[removed->elements[i]] = removed->elements[i + 1];
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * weaken solvable dependencies
+ */
+
+static void
+weaken_solvable_deps(Solver *solv, Id p)
+{
+ int i;
+ Rule *r;
+
+ for (i = 1, r = solv->rules + i; i < solv->pkgrules_end; i++, r++)
+ {
+ if (r->p != -p)
+ continue;
+ if ((r->d == 0 || r->d == -1) && r->w2 < 0)
+ continue; /* conflict */
+ queue_push(&solv->weakruleq, i);
+ }
+}
+
+
+/********************************************************************/
+/* main() */
+
+
+void
+solver_calculate_multiversionmap(Pool *pool, Queue *job, Map *multiversionmap)
+{
+ int i;
+ Id how, what, select;
+ Id p, pp;
+ for (i = 0; i < job->count; i += 2)
+ {
+ how = job->elements[i];
+ if ((how & SOLVER_JOBMASK) != SOLVER_MULTIVERSION)
+ continue;
+ what = job->elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
+ if (!multiversionmap->size)
+ map_grow(multiversionmap, pool->nsolvables);
+ if (select == SOLVER_SOLVABLE_ALL)
+ {
+ FOR_POOL_SOLVABLES(p)
+ MAPSET(multiversionmap, p);
+ }
+ else if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Solvable *s;
+ Repo *repo = pool_id2repo(pool, what);
+ if (repo)
+ FOR_REPO_SOLVABLES(repo, p, s)
+ MAPSET(multiversionmap, p);
+ }
+ FOR_JOB_SELECT(p, pp, select, what)
+ MAPSET(multiversionmap, p);
+ }
+}
+
+void
+solver_calculate_noobsmap(Pool *pool, Queue *job, Map *multiversionmap)
+{
+ solver_calculate_multiversionmap(pool, job, multiversionmap);
+}
+
+/*
+ * add a rule created by a job, record job number and weak flag
+ */
+static inline void
+solver_addjobrule(Solver *solv, Id p, Id p2, Id d, Id job, int weak)
+{
+ solver_addrule(solv, p, p2, d);
+ queue_push(&solv->ruletojob, job);
+ if (weak)
+ queue_push(&solv->weakruleq, solv->nrules - 1);
+}
+
+static inline void
+add_cleandeps_package(Solver *solv, Id p)
+{
+ if (!solv->cleandeps_updatepkgs)
+ {
+ solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->cleandeps_updatepkgs);
+ }
+ queue_pushunique(solv->cleandeps_updatepkgs, p);
+}
+
+static void
+add_update_target(Solver *solv, Id p, Id how)
+{
+ Pool *pool = solv->pool;
+ Solvable *s = pool->solvables + p;
+ Repo *installed = solv->installed;
+ Id pi, pip;
+ if (!solv->update_targets)
+ {
+ solv->update_targets = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->update_targets);
+ }
+ if (s->repo == installed)
+ {
+ queue_push2(solv->update_targets, p, p);
+ return;
+ }
+ FOR_PROVIDES(pi, pip, s->name)
+ {
+ Solvable *si = pool->solvables + pi;
+ if (si->repo != installed || si->name != s->name)
+ continue;
+ if (how & SOLVER_FORCEBEST)
+ {
+ if (!solv->bestupdatemap.size)
+ map_grow(&solv->bestupdatemap, installed->end - installed->start);
+ MAPSET(&solv->bestupdatemap, pi - installed->start);
+ }
+ if (how & SOLVER_CLEANDEPS)
+ add_cleandeps_package(solv, pi);
+ queue_push2(solv->update_targets, pi, p);
+ /* check if it's ok to keep the installed package */
+ if (s->evr == si->evr && solvable_identical(s, si))
+ queue_push2(solv->update_targets, pi, pi);
+ }
+ if (s->obsoletes)
+ {
+ Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(pi, pip, obs)
+ {
+ Solvable *si = pool->solvables + pi;
+ if (si->repo != installed)
+ continue;
+ if (si->name == s->name)
+ continue; /* already handled above */
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, si, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, si))
+ continue;
+ if (how & SOLVER_FORCEBEST)
+ {
+ if (!solv->bestupdatemap.size)
+ map_grow(&solv->bestupdatemap, installed->end - installed->start);
+ MAPSET(&solv->bestupdatemap, pi - installed->start);
+ }
+ if (how & SOLVER_CLEANDEPS)
+ add_cleandeps_package(solv, pi);
+ queue_push2(solv->update_targets, pi, p);
+ }
+ }
+ }
+}
+
+static int
+transform_update_targets_sortfn(const void *ap, const void *bp, void *dp)
+{
+ const Id *a = ap;
+ const Id *b = bp;
+ if (a[0] - b[0])
+ return a[0] - b[0];
+ return a[1] - b[1];
+}
+
+static void
+transform_update_targets(Solver *solv)
+{
+ Repo *installed = solv->installed;
+ Queue *update_targets = solv->update_targets;
+ int i, j;
+ Id p, q, lastp, lastq;
+
+ if (!update_targets->count)
+ {
+ queue_free(update_targets);
+ solv->update_targets = solv_free(update_targets);
+ return;
+ }
+ if (update_targets->count > 2)
+ solv_sort(update_targets->elements, update_targets->count >> 1, 2 * sizeof(Id), transform_update_targets_sortfn, solv);
+ queue_insertn(update_targets, 0, installed->end - installed->start, 0);
+ lastp = lastq = 0;
+ for (i = j = installed->end - installed->start; i < update_targets->count; i += 2)
+ {
+ if ((p = update_targets->elements[i]) != lastp)
+ {
+ if (!solv->updatemap.size)
+ map_grow(&solv->updatemap, installed->end - installed->start);
+ MAPSET(&solv->updatemap, p - installed->start);
+ update_targets->elements[j++] = 0; /* finish old set */
+ update_targets->elements[p - installed->start] = j; /* start new set */
+ lastp = p;
+ lastq = 0;
+ }
+ if ((q = update_targets->elements[i + 1]) != lastq)
+ {
+ update_targets->elements[j++] = q;
+ lastq = q;
+ }
+ }
+ queue_truncate(update_targets, j);
+ queue_push(update_targets, 0); /* finish last set */
+}
+
+
+static void
+addedmap2deduceq(Solver *solv, Map *addedmap)
+{
+ Pool *pool = solv->pool;
+ int i, j;
+ Id p;
+ Rule *r;
+
+ queue_empty(&solv->addedmap_deduceq);
+ for (i = 2, j = solv->pkgrules_end - 1; i < pool->nsolvables && j > 0; j--)
+ {
+ r = solv->rules + j;
+ if (r->p >= 0)
+ continue;
+ if ((r->d == 0 || r->d == -1) && r->w2 < 0)
+ continue;
+ p = -r->p;
+ if (!MAPTST(addedmap, p))
+ {
+ /* should never happen, but... */
+ if (!solv->addedmap_deduceq.count || solv->addedmap_deduceq.elements[solv->addedmap_deduceq.count - 1] != -p)
+ queue_push(&solv->addedmap_deduceq, -p);
+ continue;
+ }
+ for (; i < p; i++)
+ if (MAPTST(addedmap, i))
+ queue_push(&solv->addedmap_deduceq, i);
+ if (i == p)
+ i++;
+ }
+ for (; i < pool->nsolvables; i++)
+ if (MAPTST(addedmap, i))
+ queue_push(&solv->addedmap_deduceq, i);
+ j = 0;
+ for (i = 2; i < pool->nsolvables; i++)
+ if (MAPTST(addedmap, i))
+ j++;
+}
+
+static void
+deduceq2addedmap(Solver *solv, Map *addedmap)
+{
+ int j;
+ Id p;
+ Rule *r;
+ for (j = solv->pkgrules_end - 1; j > 0; j--)
+ {
+ r = solv->rules + j;
+ if (r->d < 0 && r->p)
+ solver_enablerule(solv, r);
+ if (r->p >= 0)
+ continue;
+ if ((r->d == 0 || r->d == -1) && r->w2 < 0)
+ continue;
+ p = -r->p;
+ MAPSET(addedmap, p);
+ }
+ for (j = 0; j < solv->addedmap_deduceq.count; j++)
+ {
+ p = solv->addedmap_deduceq.elements[j];
+ if (p > 0)
+ MAPSET(addedmap, p);
+ else
+ MAPCLR(addedmap, p);
+ }
+}
+
+#ifdef ENABLE_COMPLEX_DEPS
+static int
+add_complex_jobrules(Solver *solv, Id dep, int flags, int jobidx, int weak)
+{
+ Pool *pool = solv->pool;
+ Queue bq;
+ int i, j;
+
+ queue_init(&bq);
+ i = pool_normalize_complex_dep(pool, dep, &bq, flags | CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ {
+ queue_free(&bq);
+ if (i == 0)
+ solver_addjobrule(solv, -SYSTEMSOLVABLE, 0, 0, jobidx, weak);
+ return 0;
+ }
+ for (i = 0; i < bq.count; i++)
+ {
+ if (!bq.elements[i])
+ continue;
+ for (j = 0; bq.elements[i + j + 1]; j++)
+ ;
+ if (j > 1)
+ solver_addjobrule(solv, bq.elements[i], 0, pool_ids2whatprovides(pool, bq.elements + i + 1, j), jobidx, weak);
+ else
+ solver_addjobrule(solv, bq.elements[i], bq.elements[i + 1], 0, jobidx, weak);
+ i += j + 1;
+ }
+ queue_free(&bq);
+ return 1;
+}
+#endif
+
+/*
+ *
+ * solve job queue
+ *
+ */
+
+int
+solver_solve(Solver *solv, Queue *job)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ int i;
+ int oldnrules, initialnrules;
+ Map addedmap; /* '1' == have pkg-rules for solvable */
+ Map installcandidatemap;
+ Id how, what, select, name, weak, p, pp, d;
+ Queue q;
+ Solvable *s;
+ Rule *r;
+ int now, solve_start;
+ int needduprules = 0;
+ int hasbestinstalljob = 0;
+
+ solve_start = solv_timems(0);
+
+ /* log solver options */
+ POOL_DEBUG(SOLV_DEBUG_STATS, "solver started\n");
+ POOL_DEBUG(SOLV_DEBUG_STATS, "dosplitprovides=%d, noupdateprovide=%d, noinfarchcheck=%d\n", solv->dosplitprovides, solv->noupdateprovide, solv->noinfarchcheck);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "allowuninstall=%d, allowdowngrade=%d, allownamechange=%d, allowarchchange=%d, allowvendorchange=%d\n", solv->allowuninstall, solv->allowdowngrade, solv->allownamechange, solv->allowarchchange, solv->allowvendorchange);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "promoteepoch=%d, forbidselfconflicts=%d\n", pool->promoteepoch, pool->forbidselfconflicts);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "obsoleteusesprovides=%d, implicitobsoleteusesprovides=%d, obsoleteusescolors=%d, implicitobsoleteusescolors=%d\n", pool->obsoleteusesprovides, pool->implicitobsoleteusesprovides, pool->obsoleteusescolors, pool->implicitobsoleteusescolors);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "dontinstallrecommended=%d, addalreadyrecommended=%d\n", solv->dontinstallrecommended, solv->addalreadyrecommended);
+
+ /* create whatprovides if not already there */
+ if (!pool->whatprovides)
+ pool_createwhatprovides(pool);
+
+ /* create obsolete index */
+ policy_create_obsolete_index(solv);
+
+ /* remember job */
+ queue_free(&solv->job);
+ queue_init_clone(&solv->job, job);
+ solv->pooljobcnt = pool->pooljobs.count;
+ if (pool->pooljobs.count)
+ queue_insertn(&solv->job, 0, pool->pooljobs.count, pool->pooljobs.elements);
+ job = &solv->job;
+
+ /* free old stuff in jase we re-run a solver */
+ queuep_free(&solv->update_targets);
+ queuep_free(&solv->cleandeps_updatepkgs);
+ queue_empty(&solv->ruleassertions);
+ solv->bestrules_pkg = solv_free(solv->bestrules_pkg);
+ solv->yumobsrules_info = solv_free(solv->yumobsrules_info);
+ solv->choicerules_ref = solv_free(solv->choicerules_ref);
+ if (solv->noupdate.size)
+ map_empty(&solv->noupdate);
+ map_zerosize(&solv->multiversion);
+ solv->updatemap_all = 0;
+ map_zerosize(&solv->updatemap);
+ solv->bestupdatemap_all = 0;
+ map_zerosize(&solv->bestupdatemap);
+ solv->fixmap_all = 0;
+ map_zerosize(&solv->fixmap);
+ solv->dupmap_all = 0;
+ map_zerosize(&solv->dupmap);
+ map_zerosize(&solv->dupinvolvedmap);
+ solv->droporphanedmap_all = 0;
+ map_zerosize(&solv->droporphanedmap);
+ solv->allowuninstall_all = 0;
+ map_zerosize(&solv->allowuninstallmap);
+ map_zerosize(&solv->cleandepsmap);
+ map_zerosize(&solv->weakrulemap);
+ queue_empty(&solv->weakruleq);
+ solv->watches = solv_free(solv->watches);
+ queue_empty(&solv->ruletojob);
+ if (solv->decisionq.count)
+ memset(solv->decisionmap, 0, pool->nsolvables * sizeof(Id));
+ queue_empty(&solv->decisionq);
+ queue_empty(&solv->decisionq_why);
+ solv->decisioncnt_jobs = solv->decisioncnt_update = solv->decisioncnt_keep = solv->decisioncnt_resolve = solv->decisioncnt_weak = solv->decisioncnt_orphan = 0;
+ queue_empty(&solv->learnt_why);
+ queue_empty(&solv->learnt_pool);
+ queue_empty(&solv->branches);
+ solv->propagate_index = 0;
+ queue_empty(&solv->problems);
+ queue_empty(&solv->solutions);
+ queue_empty(&solv->orphaned);
+ solv->stats_learned = solv->stats_unsolvable = 0;
+ if (solv->recommends_index)
+ {
+ map_empty(&solv->recommendsmap);
+ map_empty(&solv->suggestsmap);
+ queuep_free(&solv->recommendscplxq);
+ queuep_free(&solv->suggestscplxq);
+ solv->recommends_index = 0;
+ }
+ queuep_free(&solv->brokenorphanrules);
+ solv->specialupdaters = solv_free(solv->specialupdaters);
+
+
+ /*
+ * create basic rule set of all involved packages
+ * use addedmap bitmap to make sure we don't create rules twice
+ */
+
+ /* create multiversion map if needed */
+ solver_calculate_multiversionmap(pool, job, &solv->multiversion);
+
+ map_init(&addedmap, pool->nsolvables);
+ MAPSET(&addedmap, SYSTEMSOLVABLE);
+
+ map_init(&installcandidatemap, pool->nsolvables);
+ queue_init(&q);
+
+ now = solv_timems(0);
+ /*
+ * create rules for all package that could be involved with the solving
+ * so called: pkg rules
+ *
+ */
+ initialnrules = solv->pkgrules_end ? solv->pkgrules_end : 1;
+ if (initialnrules > 1)
+ deduceq2addedmap(solv, &addedmap);
+ if (solv->nrules != initialnrules)
+ solver_shrinkrules(solv, initialnrules);
+ solv->nrules = initialnrules;
+ solv->pkgrules_end = 0;
+
+ if (installed)
+ {
+ /* check for update/verify jobs as they need to be known early */
+ /* also setup the droporphaned map, we need it when creating update rules */
+ for (i = 0; i < job->count; i += 2)
+ {
+ how = job->elements[i];
+ what = job->elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
+ switch (how & SOLVER_JOBMASK)
+ {
+ case SOLVER_VERIFY:
+ if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid))
+ solv->fixmap_all = 1;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ if (s->repo != installed)
+ continue;
+ if (!solv->fixmap.size)
+ map_grow(&solv->fixmap, installed->end - installed->start);
+ MAPSET(&solv->fixmap, p - installed->start);
+ }
+ break;
+ case SOLVER_UPDATE:
+ if (select == SOLVER_SOLVABLE_ALL)
+ {
+ solv->updatemap_all = 1;
+ if (how & SOLVER_FORCEBEST)
+ solv->bestupdatemap_all = 1;
+ if (how & SOLVER_CLEANDEPS)
+ {
+ FOR_REPO_SOLVABLES(installed, p, s)
+ add_cleandeps_package(solv, p);
+ }
+ }
+ else if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Repo *repo = pool_id2repo(pool, what);
+ if (!repo)
+ break;
+ if (repo == installed && !(how & SOLVER_TARGETED))
+ {
+ solv->updatemap_all = 1;
+ if (how & SOLVER_FORCEBEST)
+ solv->bestupdatemap_all = 1;
+ if (how & SOLVER_CLEANDEPS)
+ {
+ FOR_REPO_SOLVABLES(installed, p, s)
+ add_cleandeps_package(solv, p);
+ }
+ break;
+ }
+ if (solv->noautotarget && !(how & SOLVER_TARGETED))
+ break;
+ /* targeted update */
+ FOR_REPO_SOLVABLES(repo, p, s)
+ add_update_target(solv, p, how);
+ }
+ else
+ {
+ if (!(how & SOLVER_TARGETED))
+ {
+ int targeted = 1;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ if (s->repo != installed)
+ continue;
+ if (!solv->updatemap.size)
+ map_grow(&solv->updatemap, installed->end - installed->start);
+ MAPSET(&solv->updatemap, p - installed->start);
+ if (how & SOLVER_FORCEBEST)
+ {
+ if (!solv->bestupdatemap.size)
+ map_grow(&solv->bestupdatemap, installed->end - installed->start);
+ MAPSET(&solv->bestupdatemap, p - installed->start);
+ }
+ if (how & SOLVER_CLEANDEPS)
+ add_cleandeps_package(solv, p);
+ targeted = 0;
+ }
+ if (!targeted || solv->noautotarget)
+ break;
+ }
+ FOR_JOB_SELECT(p, pp, select, what)
+ add_update_target(solv, p, how);
+ }
+ break;
+ case SOLVER_DROP_ORPHANED:
+ if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+ solv->droporphanedmap_all = 1;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ if (s->repo != installed)
+ continue;
+ if (!solv->droporphanedmap.size)
+ map_grow(&solv->droporphanedmap, installed->end - installed->start);
+ MAPSET(&solv->droporphanedmap, p - installed->start);
+ }
+ break;
+ case SOLVER_ALLOWUNINSTALL:
+ if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid))
+ solv->allowuninstall_all = 1;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ if (s->repo != installed)
+ continue;
+ if (!solv->allowuninstallmap.size)
+ map_grow(&solv->allowuninstallmap, installed->end - installed->start);
+ MAPSET(&solv->allowuninstallmap, p - installed->start);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (solv->update_targets)
+ transform_update_targets(solv);
+
+ oldnrules = solv->nrules;
+ FOR_REPO_SOLVABLES(installed, p, s)
+ solver_addpkgrulesforsolvable(solv, s, &addedmap);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for installed solvables\n", solv->nrules - oldnrules);
+ oldnrules = solv->nrules;
+ FOR_REPO_SOLVABLES(installed, p, s)
+ solver_addpkgrulesforupdaters(solv, s, &addedmap, 1);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for updaters of installed solvables\n", solv->nrules - oldnrules);
+ }
+
+ /*
+ * create rules for all packages involved in the job
+ * (to be installed or removed)
+ */
+
+ oldnrules = solv->nrules;
+ for (i = 0; i < job->count; i += 2)
+ {
+ how = job->elements[i];
+ what = job->elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
+
+ switch (how & SOLVER_JOBMASK)
+ {
+ case SOLVER_INSTALL:
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ MAPSET(&installcandidatemap, p);
+ solver_addpkgrulesforsolvable(solv, pool->solvables + p, &addedmap);
+ }
+ break;
+ case SOLVER_DISTUPGRADE:
+ if (select == SOLVER_SOLVABLE_ALL)
+ {
+ solv->dupmap_all = 1;
+ solv->updatemap_all = 1;
+ if (how & SOLVER_FORCEBEST)
+ solv->bestupdatemap_all = 1;
+ }
+ if ((how & SOLVER_TARGETED) != 0)
+ needduprules = 1;
+ if (!solv->dupmap_all || solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size || solv->keep_orphans)
+ needduprules = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for packages involved in a job\n", solv->nrules - oldnrules);
+
+
+ /*
+ * add rules for suggests, enhances
+ */
+ oldnrules = solv->nrules;
+ solver_addpkgrulesforweak(solv, &addedmap);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules because of weak dependencies\n", solv->nrules - oldnrules);
+
+#ifdef ENABLE_LINKED_PKGS
+ oldnrules = solv->nrules;
+ solver_addpkgrulesforlinked(solv, &addedmap);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules because of linked packages\n", solv->nrules - oldnrules);
+#endif
+
+ /*
+ * first pass done, we now have all the pkg rules we need.
+ * unify existing rules before going over all job rules and
+ * policy rules.
+ * at this point the system is always solvable,
+ * as an empty system (remove all packages) is a valid solution
+ */
+
+ IF_POOLDEBUG (SOLV_DEBUG_STATS)
+ {
+ int possible = 0, installable = 0;
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ if (pool_installable(pool, pool->solvables + i))
+ installable++;
+ if (MAPTST(&addedmap, i))
+ possible++;
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "%d of %d installable solvables considered for solving\n", possible, installable);
+ }
+
+ if (solv->nrules > initialnrules)
+ solver_unifyrules(solv); /* remove duplicate pkg rules */
+ solv->pkgrules_end = solv->nrules; /* mark end of pkg rules */
+
+ if (solv->nrules > initialnrules)
+ addedmap2deduceq(solv, &addedmap); /* so that we can recreate the addedmap */
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "pkg rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "pkg rule creation took %d ms\n", solv_timems(now));
+
+ /* create dup maps if needed. We need the maps early to create our
+ * update rules */
+ if (needduprules)
+ solver_createdupmaps(solv);
+
+ /*
+ * create feature rules
+ *
+ * foreach installed:
+ * create assertion (keep installed, if no update available)
+ * or
+ * create update rule (A|update1(A)|update2(A)|...)
+ *
+ * those are used later on to keep a version of the installed packages in
+ * best effort mode
+ */
+
+ solv->featurerules = solv->nrules; /* mark start of feature rules */
+ if (installed)
+ {
+ /* foreach possibly installed solvable */
+ for (i = installed->start, s = pool->solvables + i; i < installed->end; i++, s++)
+ {
+ if (s->repo != installed)
+ {
+ solver_addrule(solv, 0, 0, 0); /* create dummy rule */
+ continue;
+ }
+ solver_addupdaterule(solv, s, 1); /* allow s to be updated */
+ }
+ /* make sure we accounted for all rules */
+ assert(solv->nrules - solv->featurerules == installed->end - installed->start);
+ }
+ solv->featurerules_end = solv->nrules;
+
+ /*
+ * Add update rules for installed solvables
+ *
+ * almost identical to feature rules
+ * except that downgrades/archchanges/vendorchanges are not allowed
+ */
+
+ solv->updaterules = solv->nrules;
+
+ if (installed)
+ { /* foreach installed solvables */
+ /* we create all update rules, but disable some later on depending on the job */
+ for (i = installed->start, s = pool->solvables + i; i < installed->end; i++, s++)
+ {
+ Rule *sr;
+
+ if (s->repo != installed)
+ {
+ solver_addrule(solv, 0, 0, 0); /* create dummy rule */
+ continue;
+ }
+ solver_addupdaterule(solv, s, 0); /* allowall = 0: downgrades not allowed */
+ /*
+ * check for and remove duplicate
+ */
+ r = solv->rules + solv->nrules - 1; /* r: update rule */
+ sr = r - (installed->end - installed->start); /* sr: feature rule */
+ if (!r->p)
+ {
+ if (sr->p)
+ memset(sr, 0, sizeof(*sr)); /* no feature rules without update rules */
+ continue;
+ }
+ /* it's also orphaned if the feature rule consists just of the installed package */
+ if (!solv->dupmap_all && sr->p == i && !sr->d && !sr->w2)
+ queue_push(&solv->orphaned, i);
+ if (!solver_rulecmp(solv, r, sr))
+ memset(sr, 0, sizeof(*sr)); /* delete unneeded feature rule */
+ else
+ solver_disablerule(solv, sr); /* disable feature rule for now */
+ }
+ /* consistency check: we added a rule for _every_ installed solvable */
+ assert(solv->nrules - solv->updaterules == installed->end - installed->start);
+ }
+ solv->updaterules_end = solv->nrules;
+
+
+ /*
+ * now add all job rules
+ */
+
+ solv->jobrules = solv->nrules;
+ for (i = 0; i < job->count; i += 2)
+ {
+ oldnrules = solv->nrules;
+
+ if (i && i == solv->pooljobcnt)
+ POOL_DEBUG(SOLV_DEBUG_JOB, "end of pool jobs\n");
+ how = job->elements[i];
+ what = job->elements[i + 1];
+ weak = how & SOLVER_WEAK;
+ select = how & SOLVER_SELECTMASK;
+ switch (how & SOLVER_JOBMASK)
+ {
+ case SOLVER_INSTALL:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: %sinstall %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
+ if ((how & SOLVER_CLEANDEPS) != 0 && !solv->cleandepsmap.size && installed)
+ map_grow(&solv->cleandepsmap, installed->end - installed->start);
+ if (select == SOLVER_SOLVABLE)
+ {
+ p = what;
+ d = 0;
+ }
+#ifdef ENABLE_COMPLEX_DEPS
+ else if ((select == SOLVER_SOLVABLE_PROVIDES || select == SOLVER_SOLVABLE_NAME) && pool_is_complex_dep(pool, what))
+ {
+ if (add_complex_jobrules(solv, what, select == SOLVER_SOLVABLE_NAME ? CPLXDEPS_NAME : 0, i, weak))
+ if (how & SOLVER_FORCEBEST)
+ hasbestinstalljob = 1;
+ break;
+ }
+#endif
+ else
+ {
+ queue_empty(&q);
+ FOR_JOB_SELECT(p, pp, select, what)
+ queue_push(&q, p);
+ if (!q.count)
+ {
+ if (select == SOLVER_SOLVABLE_ONE_OF)
+ break; /* ignore empty installs */
+ /* no candidate found or unsupported, make this an impossible rule */
+ queue_push(&q, -SYSTEMSOLVABLE);
+ }
+ p = queue_shift(&q); /* get first candidate */
+ d = !q.count ? 0 : pool_queuetowhatprovides(pool, &q); /* internalize */
+ }
+ /* force install of namespace supplements hack */
+ if (select == SOLVER_SOLVABLE_PROVIDES && !d && (p == SYSTEMSOLVABLE || p == -SYSTEMSOLVABLE) && ISRELDEP(what))
+ {
+ Reldep *rd = GETRELDEP(pool, what);
+ if (rd->flags == REL_NAMESPACE)
+ {
+ p = SYSTEMSOLVABLE;
+ if (!solv->installsuppdepq)
+ {
+ solv->installsuppdepq = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->installsuppdepq);
+ }
+ queue_pushunique(solv->installsuppdepq, rd->evr == 0 ? rd->name : what);
+ }
+ }
+ solver_addjobrule(solv, p, 0, d, i, weak);
+ if (how & SOLVER_FORCEBEST)
+ hasbestinstalljob = 1;
+ break;
+ case SOLVER_ERASE:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: %s%serase %s\n", weak ? "weak " : "", how & SOLVER_CLEANDEPS ? "clean deps " : "", solver_select2str(pool, select, what));
+ if ((how & SOLVER_CLEANDEPS) != 0 && !solv->cleandepsmap.size && installed)
+ map_grow(&solv->cleandepsmap, installed->end - installed->start);
+ /* specific solvable: by id or by nevra */
+ name = (select == SOLVER_SOLVABLE || (select == SOLVER_SOLVABLE_NAME && ISRELDEP(what))) ? 0 : -1;
+ if (select == SOLVER_SOLVABLE_ALL) /* hmmm ;) */
+ {
+ FOR_POOL_SOLVABLES(p)
+ solver_addjobrule(solv, -p, 0, 0, i, weak);
+ }
+ else if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Repo *repo = pool_id2repo(pool, what);
+ if (repo)
+ FOR_REPO_SOLVABLES(repo, p, s)
+ solver_addjobrule(solv, -p, 0, 0, i, weak);
+ }
+#ifdef ENABLE_COMPLEX_DEPS
+ else if ((select == SOLVER_SOLVABLE_PROVIDES || select == SOLVER_SOLVABLE_NAME) && pool_is_complex_dep(pool, what))
+ {
+ /* no special "erase a specific solvable" handling? */
+ add_complex_jobrules(solv, what, select == SOLVER_SOLVABLE_NAME ? (CPLXDEPS_NAME | CPLXDEPS_TODNF | CPLXDEPS_INVERT) : (CPLXDEPS_TODNF | CPLXDEPS_INVERT), i, weak);
+ break;
+ }
+#endif
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ if (installed && s->repo == installed)
+ name = !name ? s->name : -1;
+ solver_addjobrule(solv, -p, 0, 0, i, weak);
+ }
+ /* special case for "erase a specific solvable": we also
+ * erase all other solvables with that name, so that they
+ * don't get picked up as replacement.
+ * name is > 0 if exactly one installed solvable matched.
+ */
+ /* XXX: look also at packages that obsolete this package? */
+ if (name > 0)
+ {
+ int j, k;
+ k = solv->nrules;
+ FOR_PROVIDES(p, pp, name)
+ {
+ s = pool->solvables + p;
+ if (s->name != name)
+ continue;
+ /* keep other versions installed */
+ if (s->repo == installed)
+ continue;
+ /* keep installcandidates of other jobs */
+ if (MAPTST(&installcandidatemap, p))
+ continue;
+ /* don't add the same rule twice */
+ for (j = oldnrules; j < k; j++)
+ if (solv->rules[j].p == -p)
+ break;
+ if (j == k)
+ solver_addjobrule(solv, -p, 0, 0, i, weak); /* remove by id */
+ }
+ }
+ break;
+
+ case SOLVER_UPDATE:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: %supdate %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
+ break;
+ case SOLVER_VERIFY:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: %sverify %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
+ break;
+ case SOLVER_WEAKENDEPS:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: %sweaken deps %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
+ if (select != SOLVER_SOLVABLE)
+ break;
+ s = pool->solvables + what;
+ weaken_solvable_deps(solv, what);
+ break;
+ case SOLVER_MULTIVERSION:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: %smultiversion %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
+ break;
+ case SOLVER_LOCK:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: %slock %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
+ if (select == SOLVER_SOLVABLE_ALL)
+ {
+ FOR_POOL_SOLVABLES(p)
+ solver_addjobrule(solv, installed && pool->solvables[p].repo == installed ? p : -p, 0, 0, i, weak);
+ }
+ else if (select == SOLVER_SOLVABLE_REPO)
+ {
+ Repo *repo = pool_id2repo(pool, what);
+ if (repo)
+ FOR_REPO_SOLVABLES(repo, p, s)
+ solver_addjobrule(solv, installed && pool->solvables[p].repo == installed ? p : -p, 0, 0, i, weak);
+ }
+ FOR_JOB_SELECT(p, pp, select, what)
+ solver_addjobrule(solv, installed && pool->solvables[p].repo == installed ? p : -p, 0, 0, i, weak);
+ break;
+ case SOLVER_DISTUPGRADE:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: distupgrade %s\n", solver_select2str(pool, select, what));
+ break;
+ case SOLVER_DROP_ORPHANED:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: drop orphaned %s\n", solver_select2str(pool, select, what));
+ break;
+ case SOLVER_USERINSTALLED:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: user installed %s\n", solver_select2str(pool, select, what));
+ break;
+ case SOLVER_ALLOWUNINSTALL:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: allowuninstall %s\n", solver_select2str(pool, select, what));
+ break;
+ default:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: unknown job\n");
+ break;
+ }
+
+ IF_POOLDEBUG (SOLV_DEBUG_JOB)
+ {
+ int j;
+ if (solv->nrules == oldnrules)
+ POOL_DEBUG(SOLV_DEBUG_JOB, " - no rule created\n");
+ for (j = oldnrules; j < solv->nrules; j++)
+ {
+ POOL_DEBUG(SOLV_DEBUG_JOB, " - job ");
+ solver_printrule(solv, SOLV_DEBUG_JOB, solv->rules + j);
+ }
+ }
+ }
+ assert(solv->ruletojob.count == solv->nrules - solv->jobrules);
+ solv->jobrules_end = solv->nrules;
+
+ /* now create infarch and dup rules */
+ if (!solv->noinfarchcheck)
+ {
+ solver_addinfarchrules(solv, &addedmap);
+#if 0
+ if (pool->implicitobsoleteusescolors)
+ {
+ /* currently doesn't work well with infarch rules, so make
+ * them weak */
+ for (i = solv->infarchrules; i < solv->infarchrules_end; i++)
+ queue_push(&solv->weakruleq, i);
+ }
+#endif
+ }
+ else
+ solv->infarchrules = solv->infarchrules_end = solv->nrules;
+
+ if (needduprules)
+ solver_addduprules(solv, &addedmap);
+ else
+ solv->duprules = solv->duprules_end = solv->nrules;
+
+ if (solv->bestupdatemap_all || solv->bestupdatemap.size || hasbestinstalljob)
+ solver_addbestrules(solv, hasbestinstalljob);
+ else
+ solv->bestrules = solv->bestrules_end = solv->nrules;
+
+ if (needduprules)
+ solver_freedupmaps(solv); /* no longer needed */
+
+ if (solv->do_yum_obsoletes)
+ solver_addyumobsrules(solv);
+ else
+ solv->yumobsrules = solv->yumobsrules_end = solv->nrules;
+
+ if (1)
+ solver_addchoicerules(solv);
+ else
+ solv->choicerules = solv->choicerules_end = solv->nrules;
+
+ if (0)
+ {
+ for (i = solv->featurerules; i < solv->nrules; i++)
+ solver_printruleclass(solv, SOLV_DEBUG_RESULT, solv->rules + i);
+ }
+ /* all rules created
+ * --------------------------------------------------------------
+ * prepare for solving
+ */
+
+ /* free unneeded memory */
+ map_free(&addedmap);
+ map_free(&installcandidatemap);
+ queue_free(&q);
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "%d pkg rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules\n", solv->pkgrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "overall rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
+
+ /* create weak map */
+ if (solv->weakruleq.count)
+ {
+ map_grow(&solv->weakrulemap, solv->nrules);
+ for (i = 0; i < solv->weakruleq.count; i++)
+ {
+ p = solv->weakruleq.elements[i];
+ MAPSET(&solv->weakrulemap, p);
+ }
+ }
+
+ /* enable cleandepsmap creation if we have updatepkgs */
+ if (solv->cleandeps_updatepkgs && !solv->cleandepsmap.size)
+ map_grow(&solv->cleandepsmap, installed->end - installed->start);
+ /* no mistakes */
+ if (solv->cleandeps_mistakes)
+ {
+ queue_free(solv->cleandeps_mistakes);
+ solv->cleandeps_mistakes = solv_free(solv->cleandeps_mistakes);
+ }
+
+ /* all new rules are learnt after this point */
+ solv->learntrules = solv->nrules;
+
+ /* create watches chains */
+ makewatches(solv);
+
+ /* create assertion index. it is only used to speed up
+ * makeruledecsions() a bit */
+ for (i = 1, r = solv->rules + i; i < solv->nrules; i++, r++)
+ if (r->p && !r->w2 && (r->d == 0 || r->d == -1))
+ queue_push(&solv->ruleassertions, i);
+
+ /* disable update rules that conflict with our job */
+ solver_disablepolicyrules(solv);
+
+ /* break orphans if requested */
+ if (solv->dupmap_all && solv->orphaned.count && solv->break_orphans)
+ solver_breakorphans(solv);
+
+ /*
+ * ********************************************
+ * solve!
+ * ********************************************
+ */
+
+ now = solv_timems(0);
+ solver_run_sat(solv, 1, solv->dontinstallrecommended ? 0 : 1);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "solver took %d ms\n", solv_timems(now));
+
+ /*
+ * prepare solution queue if there were problems
+ */
+ solver_prepare_solutions(solv);
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "final solver statistics: %d problems, %d learned rules, %d unsolvable\n", solv->problems.count / 2, solv->stats_learned, solv->stats_unsolvable);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "solver_solve took %d ms\n", solv_timems(solve_start));
+
+ /* return number of problems */
+ return solv->problems.count ? solv->problems.count / 2 : 0;
+}
+
+Transaction *
+solver_create_transaction(Solver *solv)
+{
+ return transaction_create_decisionq(solv->pool, &solv->decisionq, &solv->multiversion);
+}
+
+void solver_get_orphaned(Solver *solv, Queue *orphanedq)
+{
+ queue_free(orphanedq);
+ queue_init_clone(orphanedq, &solv->orphaned);
+}
+
+void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *suggestionsq, int noselected)
+{
+ Pool *pool = solv->pool;
+ Queue redoq, disabledq;
+ int goterase, i;
+ Solvable *s;
+ Rule *r;
+ Map obsmap;
+
+ if (!recommendationsq && !suggestionsq)
+ return;
+
+ map_init(&obsmap, pool->nsolvables);
+ if (solv->installed)
+ {
+ Id obs, *obsp, p, po, ppo;
+ for (p = solv->installed->start; p < solv->installed->end; p++)
+ {
+ s = pool->solvables + p;
+ if (s->repo != solv->installed || !s->obsoletes)
+ continue;
+ if (solv->decisionmap[p] <= 0)
+ continue;
+ if (solv->multiversion.size && MAPTST(&solv->multiversion, p))
+ continue;
+ obsp = s->repo->idarraydata + s->obsoletes;
+ /* foreach obsoletes */
+ while ((obs = *obsp++) != 0)
+ FOR_PROVIDES(po, ppo, obs)
+ MAPSET(&obsmap, po);
+ }
+ }
+
+ queue_init(&redoq);
+ queue_init(&disabledq);
+ goterase = 0;
+ /* disable all erase jobs (including weak "keep uninstalled" rules) */
+ for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++)
+ {
+ if (r->d < 0) /* disabled ? */
+ continue;
+ if (r->p >= 0) /* install job? */
+ continue;
+ queue_push(&disabledq, i);
+ solver_disablerule(solv, r);
+ goterase++;
+ }
+
+ if (goterase)
+ {
+ enabledisablelearntrules(solv);
+ removedisabledconflicts(solv, &redoq);
+ }
+
+ /*
+ * find recommended packages
+ */
+ if (recommendationsq)
+ {
+ Id rec, *recp, p, pp;
+
+ queue_empty(recommendationsq);
+ /* create map of all recommened packages */
+ solv->recommends_index = -1;
+ MAPZERO(&solv->recommendsmap);
+
+ /* put all packages the solver already chose in the map */
+ if (solv->decisioncnt_weak)
+ {
+ for (i = solv->decisioncnt_weak; i < solv->decisioncnt_orphan; i++)
+ {
+ Id why;
+ why = solv->decisionq_why.elements[i];
+ if (why)
+ continue; /* forced by unit rule or dep resolving */
+ p = solv->decisionq.elements[i];
+ if (p < 0)
+ continue;
+ MAPSET(&solv->recommendsmap, p);
+ }
+ }
+
+ for (i = 0; i < solv->decisionq.count; i++)
+ {
+ p = solv->decisionq.elements[i];
+ if (p < 0)
+ continue;
+ s = pool->solvables + p;
+ if (s->recommends)
+ {
+ recp = s->repo->idarraydata + s->recommends;
+ while ((rec = *recp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, rec))
+ {
+ do_complex_recommendations(solv, rec, &solv->recommendsmap, noselected);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, rec)
+ if (solv->decisionmap[p] > 0)
+ break;
+ if (p)
+ {
+ if (!noselected)
+ {
+ FOR_PROVIDES(p, pp, rec)
+ if (solv->decisionmap[p] > 0)
+ MAPSET(&solv->recommendsmap, p);
+ }
+ continue; /* p != 0: already fulfilled */
+ }
+ FOR_PROVIDES(p, pp, rec)
+ MAPSET(&solv->recommendsmap, p);
+ }
+ }
+ }
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ if (solv->decisionmap[i] < 0)
+ continue;
+ if (solv->decisionmap[i] > 0 && noselected)
+ continue;
+ if (MAPTST(&obsmap, i))
+ continue;
+ s = pool->solvables + i;
+ if (!MAPTST(&solv->recommendsmap, i))
+ {
+ if (!s->supplements)
+ continue;
+ if (!pool_installable(pool, s))
+ continue;
+ if (!solver_is_supplementing(solv, s))
+ continue;
+ }
+ queue_push(recommendationsq, i);
+ }
+ /* we use MODE_SUGGEST here so that repo prio is ignored */
+ policy_filter_unwanted(solv, recommendationsq, POLICY_MODE_SUGGEST);
+ }
+
+ /*
+ * find suggested packages
+ */
+
+ if (suggestionsq)
+ {
+ Id sug, *sugp, p, pp;
+
+ queue_empty(suggestionsq);
+ /* create map of all suggests that are still open */
+ solv->recommends_index = -1;
+ MAPZERO(&solv->suggestsmap);
+ for (i = 0; i < solv->decisionq.count; i++)
+ {
+ p = solv->decisionq.elements[i];
+ if (p < 0)
+ continue;
+ s = pool->solvables + p;
+ if (s->suggests)
+ {
+ sugp = s->repo->idarraydata + s->suggests;
+ while ((sug = *sugp++) != 0)
+ {
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, sug))
+ {
+ do_complex_recommendations(solv, sug, &solv->suggestsmap, noselected);
+ continue;
+ }
+#endif
+ FOR_PROVIDES(p, pp, sug)
+ if (solv->decisionmap[p] > 0)
+ break;
+ if (p)
+ {
+ if (!noselected)
+ {
+ FOR_PROVIDES(p, pp, sug)
+ if (solv->decisionmap[p] > 0)
+ MAPSET(&solv->suggestsmap, p);
+ }
+ continue; /* already fulfilled */
+ }
+ FOR_PROVIDES(p, pp, sug)
+ MAPSET(&solv->suggestsmap, p);
+ }
+ }
+ }
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ if (solv->decisionmap[i] < 0)
+ continue;
+ if (solv->decisionmap[i] > 0 && noselected)
+ continue;
+ if (MAPTST(&obsmap, i))
+ continue;
+ s = pool->solvables + i;
+ if (!MAPTST(&solv->suggestsmap, i))
+ {
+ if (!s->enhances)
+ continue;
+ if (!pool_installable(pool, s))
+ continue;
+ if (!solver_is_enhancing(solv, s))
+ continue;
+ }
+ queue_push(suggestionsq, i);
+ }
+ policy_filter_unwanted(solv, suggestionsq, POLICY_MODE_SUGGEST);
+ }
+
+ /* undo removedisabledconflicts */
+ if (redoq.count)
+ undo_removedisabledconflicts(solv, &redoq);
+ queue_free(&redoq);
+
+ /* undo job rule disabling */
+ for (i = 0; i < disabledq.count; i++)
+ solver_enablerule(solv, solv->rules + disabledq.elements[i]);
+ queue_free(&disabledq);
+ map_free(&obsmap);
+}
+
+
+/***********************************************************************/
+/* disk usage computations */
+
+/*-------------------------------------------------------------------
+ *
+ * calculate DU changes
+ */
+
+void
+solver_calc_duchanges(Solver *solv, DUChanges *mps, int nmps)
+{
+ Map installedmap;
+
+ solver_create_state_maps(solv, &installedmap, 0);
+ pool_calc_duchanges(solv->pool, &installedmap, mps, nmps);
+ map_free(&installedmap);
+}
+
+
+/*-------------------------------------------------------------------
+ *
+ * calculate changes in install size
+ */
+
+int
+solver_calc_installsizechange(Solver *solv)
+{
+ Map installedmap;
+ int change;
+
+ solver_create_state_maps(solv, &installedmap, 0);
+ change = pool_calc_installsizechange(solv->pool, &installedmap);
+ map_free(&installedmap);
+ return change;
+}
+
+void
+solver_create_state_maps(Solver *solv, Map *installedmap, Map *conflictsmap)
+{
+ pool_create_state_maps(solv->pool, &solv->decisionq, installedmap, conflictsmap);
+}
+
+void
+solver_trivial_installable(Solver *solv, Queue *pkgs, Queue *res)
+{
+ Pool *pool = solv->pool;
+ Map installedmap;
+ int i;
+ pool_create_state_maps(pool, &solv->decisionq, &installedmap, 0);
+ pool_trivial_installable_multiversionmap(pool, &installedmap, pkgs, res, solv->multiversion.size ? &solv->multiversion : 0);
+ for (i = 0; i < res->count; i++)
+ if (res->elements[i] != -1)
+ {
+ Solvable *s = pool->solvables + pkgs->elements[i];
+ if (!strncmp("patch:", pool_id2str(pool, s->name), 6) && solvable_is_irrelevant_patch(s, &installedmap))
+ res->elements[i] = -1;
+ }
+ map_free(&installedmap);
+}
+
+/*-------------------------------------------------------------------
+ *
+ * decision introspection
+ */
+
+int
+solver_get_decisionlevel(Solver *solv, Id p)
+{
+ return solv->decisionmap[p];
+}
+
+void
+solver_get_decisionqueue(Solver *solv, Queue *decisionq)
+{
+ queue_free(decisionq);
+ queue_init_clone(decisionq, &solv->decisionq);
+}
+
+int
+solver_get_lastdecisionblocklevel(Solver *solv)
+{
+ Id p;
+ if (solv->decisionq.count == 0)
+ return 0;
+ p = solv->decisionq.elements[solv->decisionq.count - 1];
+ if (p < 0)
+ p = -p;
+ return solv->decisionmap[p] < 0 ? -solv->decisionmap[p] : solv->decisionmap[p];
+}
+
+void
+solver_get_decisionblock(Solver *solv, int level, Queue *decisionq)
+{
+ Id p;
+ int i;
+
+ queue_empty(decisionq);
+ for (i = 0; i < solv->decisionq.count; i++)
+ {
+ p = solv->decisionq.elements[i];
+ if (p < 0)
+ p = -p;
+ if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
+ break;
+ }
+ if (i == solv->decisionq.count)
+ return;
+ for (i = 0; i < solv->decisionq.count; i++)
+ {
+ p = solv->decisionq.elements[i];
+ if (p < 0)
+ p = -p;
+ if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
+ queue_push(decisionq, p);
+ else
+ break;
+ }
+}
+
+int
+solver_describe_decision(Solver *solv, Id p, Id *infop)
+{
+ int i;
+ Id pp, why;
+
+ if (infop)
+ *infop = 0;
+ if (!solv->decisionmap[p])
+ return SOLVER_REASON_UNRELATED;
+ pp = solv->decisionmap[p] < 0 ? -p : p;
+ for (i = 0; i < solv->decisionq.count; i++)
+ if (solv->decisionq.elements[i] == pp)
+ break;
+ if (i == solv->decisionq.count) /* just in case... */
+ return SOLVER_REASON_UNRELATED;
+ why = solv->decisionq_why.elements[i];
+ if (infop)
+ *infop = why > 0 ? why : -why;
+ if (why > 0)
+ return SOLVER_REASON_UNIT_RULE;
+ why = -why;
+ if (i == 0)
+ return SOLVER_REASON_KEEP_INSTALLED; /* the systemsolvable */
+ if (i < solv->decisioncnt_update)
+ return SOLVER_REASON_RESOLVE_JOB;
+ if (i < solv->decisioncnt_keep)
+ {
+ if (why == 0 && pp < 0)
+ return SOLVER_REASON_CLEANDEPS_ERASE;
+ return SOLVER_REASON_UPDATE_INSTALLED;
+ }
+ if (i < solv->decisioncnt_resolve)
+ {
+ if (solv->focus_installed && i >= solv->decisioncnt_jobs)
+ return SOLVER_REASON_RESOLVE_JOB;
+ if (why == 0 && pp < 0)
+ return SOLVER_REASON_CLEANDEPS_ERASE;
+ return SOLVER_REASON_KEEP_INSTALLED;
+ }
+ if (why > 0)
+ return SOLVER_REASON_RESOLVE;
+ /* weak or orphaned */
+ if (i < solv->decisioncnt_orphan)
+ return SOLVER_REASON_WEAKDEP;
+ return SOLVER_REASON_RESOLVE_ORPHAN;
+}
+
+
+void
+solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq)
+{
+ Pool *pool = solv->pool;
+ int i;
+ int level = solv->decisionmap[p];
+ int decisionno;
+ Solvable *s;
+
+ queue_empty(whyq);
+ if (level < 0)
+ return; /* huh? */
+ for (decisionno = 0; decisionno < solv->decisionq.count; decisionno++)
+ if (solv->decisionq.elements[decisionno] == p)
+ break;
+ if (decisionno == solv->decisionq.count)
+ return; /* huh? */
+ if (decisionno < solv->decisioncnt_weak || decisionno >= solv->decisioncnt_orphan)
+ return; /* huh? */
+
+ /* 1) list all packages that recommend us */
+ for (i = 1; i < pool->nsolvables; i++)
+ {
+ Id *recp, rec, pp2, p2;
+ if (solv->decisionmap[i] < 0 || solv->decisionmap[i] >= level)
+ continue;
+ s = pool->solvables + i;
+ if (!s->recommends)
+ continue;
+ if (!solv->addalreadyrecommended && s->repo == solv->installed)
+ continue;
+ recp = s->repo->idarraydata + s->recommends;
+ while ((rec = *recp++) != 0)
+ {
+ int found = 0;
+ FOR_PROVIDES(p2, pp2, rec)
+ {
+ if (p2 == p)
+ found = 1;
+ else
+ {
+ /* if p2 is already installed, this recommends is ignored */
+ if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
+ break;
+ }
+ }
+ if (!p2 && found)
+ {
+ queue_push(whyq, SOLVER_REASON_RECOMMENDED);
+ queue_push2(whyq, i, rec);
+ }
+ }
+ }
+ /* 2) list all supplements */
+ s = pool->solvables + p;
+ if (s->supplements && level > 0)
+ {
+ Id *supp, sup, pp2, p2;
+ /* this is a hack. to use solver_dep_fulfilled we temporarily clear
+ * everything above our level in the decisionmap */
+ for (i = decisionno; i < solv->decisionq.count; i++ )
+ {
+ p2 = solv->decisionq.elements[i];
+ if (p2 > 0)
+ solv->decisionmap[p2] = -solv->decisionmap[p2];
+ }
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (solver_dep_fulfilled(solv, sup))
+ {
+ int found = 0;
+ /* let's see if this is an easy supp */
+ FOR_PROVIDES(p2, pp2, sup)
+ {
+ if (!solv->addalreadyrecommended && solv->installed)
+ {
+ if (pool->solvables[p2].repo == solv->installed)
+ continue;
+ }
+ if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
+ {
+ queue_push(whyq, SOLVER_REASON_SUPPLEMENTED);
+ queue_push2(whyq, p2, sup);
+ found = 1;
+ }
+ }
+ if (!found)
+ {
+ /* hard case, just note dependency with no package */
+ queue_push(whyq, SOLVER_REASON_SUPPLEMENTED);
+ queue_push2(whyq, 0, sup);
+ }
+ }
+ for (i = decisionno; i < solv->decisionq.count; i++)
+ {
+ p2 = solv->decisionq.elements[i];
+ if (p2 > 0)
+ solv->decisionmap[p2] = -solv->decisionmap[p2];
+ }
+ }
+}
+
+void
+pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what)
+{
+ Id p, pp;
+ how &= SOLVER_SELECTMASK;
+ queue_empty(pkgs);
+ if (how == SOLVER_SOLVABLE_ALL)
+ {
+ FOR_POOL_SOLVABLES(p)
+ queue_push(pkgs, p);
+ }
+ else if (how == SOLVER_SOLVABLE_REPO)
+ {
+ Repo *repo = pool_id2repo(pool, what);
+ Solvable *s;
+ if (repo)
+ FOR_REPO_SOLVABLES(repo, p, s)
+ queue_push(pkgs, p);
+ }
+ else
+ {
+ FOR_JOB_SELECT(p, pp, how, what)
+ queue_push(pkgs, p);
+ }
+}
+
+int
+pool_isemptyupdatejob(Pool *pool, Id how, Id what)
+{
+ Id p, pp, pi, pip;
+ Id select = how & SOLVER_SELECTMASK;
+ if ((how & SOLVER_JOBMASK) != SOLVER_UPDATE)
+ return 0;
+ if (select == SOLVER_SOLVABLE_ALL || select == SOLVER_SOLVABLE_REPO)
+ return 0;
+ if (!pool->installed)
+ return 1;
+ FOR_JOB_SELECT(p, pp, select, what)
+ if (pool->solvables[p].repo == pool->installed)
+ return 0;
+ /* hard work */
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ Solvable *s = pool->solvables + p;
+ FOR_PROVIDES(pi, pip, s->name)
+ {
+ Solvable *si = pool->solvables + pi;
+ if (si->repo != pool->installed || si->name != s->name)
+ continue;
+ return 0;
+ }
+ if (s->obsoletes)
+ {
+ Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(pi, pip, obs)
+ {
+ Solvable *si = pool->solvables + pi;
+ if (si->repo != pool->installed)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, si, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, si))
+ continue;
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+static int
+get_userinstalled_cmp(const void *ap, const void *bp, void *dp)
+{
+ return *(Id *)ap - *(Id *)bp;
+}
+
+static int
+get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = dp;
+ return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp));
+}
+
+static int
+get_userinstalled_cmp_namearch(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = dp;
+ int r;
+ r = strcmp(pool_id2str(pool, ((Id *)ap)[0]), pool_id2str(pool, ((Id *)bp)[0]));
+ if (r)
+ return r;
+ return strcmp(pool_id2str(pool, ((Id *)ap)[1]), pool_id2str(pool, ((Id *)bp)[1]));
+}
+
+static void
+get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags)
+{
+ Id lastp = -1, lasta = -1;
+ int i, j;
+ if (q->count < ((flags & GET_USERINSTALLED_NAMEARCH) ? 4 : 2))
+ return;
+ if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+ solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), get_userinstalled_cmp_namearch, pool);
+ else if ((flags & GET_USERINSTALLED_NAMES) != 0)
+ solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool);
+ else
+ solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0);
+ if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+ {
+ for (i = j = 0; i < q->count; i += 2)
+ if (q->elements[i] != lastp || q->elements[i + 1] != lasta)
+ {
+ q->elements[j++] = lastp = q->elements[i];
+ q->elements[j++] = lasta = q->elements[i + 1];
+ }
+ }
+ else
+ {
+ for (i = j = 0; i < q->count; i++)
+ if (q->elements[i] != lastp)
+ q->elements[j++] = lastp = q->elements[i];
+ }
+ queue_truncate(q, j);
+}
+
+static void
+namearch2solvables(Pool *pool, Queue *q, Queue *qout, int job)
+{
+ int i;
+ if (!pool->installed)
+ return;
+ for (i = 0; i < q->count; i += 2)
+ {
+ Id p, pp, name = q->elements[i], arch = q->elements[i + 1];
+ FOR_PROVIDES(p, pp, name)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->repo != pool->installed || s->name != name || (arch && s->arch != arch))
+ continue;
+ if (job)
+ queue_push(qout, job);
+ queue_push(qout, p);
+ }
+ }
+}
+
+void
+solver_get_userinstalled(Solver *solv, Queue *q, int flags)
+{
+ Pool *pool = solv->pool;
+ Id p, p2, pp;
+ Solvable *s;
+ Repo *installed = solv->installed;
+ int i, j;
+ Map userinstalled;
+
+ map_init(&userinstalled, 0);
+ queue_empty(q);
+ /* first process jobs */
+ for (i = 0; i < solv->job.count; i += 2)
+ {
+ Id how = solv->job.elements[i];
+ Id what, select;
+ if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
+ {
+ if (!userinstalled.size)
+ map_grow(&userinstalled, installed->end - installed->start);
+ what = solv->job.elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
+ if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+ FOR_REPO_SOLVABLES(installed, p, s)
+ MAPSET(&userinstalled, p - installed->start);
+ FOR_JOB_SELECT(p, pp, select, what)
+ if (pool->solvables[p].repo == installed)
+ MAPSET(&userinstalled, p - installed->start);
+ continue;
+ }
+ if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL)
+ continue;
+ if ((how & SOLVER_NOTBYUSER) != 0)
+ continue;
+ what = solv->job.elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
+ FOR_JOB_SELECT(p, pp, select, what)
+ if (solv->decisionmap[p] > 0)
+ {
+ queue_push(q, p);
+#ifdef ENABLE_LINKED_PKGS
+ if (has_package_link(pool, pool->solvables + p))
+ {
+ int j;
+ Queue lq;
+ queue_init(&lq);
+ find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0);
+ for (j = 0; j < lq.count; j++)
+ if (solv->decisionmap[lq.elements[j]] > 0)
+ queue_push(q, lq.elements[j]);
+ }
+#endif
+ }
+ }
+ /* now process updates of userinstalled packages */
+ if (installed && userinstalled.size)
+ {
+ for (i = 1; i < solv->decisionq.count; i++)
+ {
+ p = solv->decisionq.elements[i];
+ if (p <= 0)
+ continue;
+ s = pool->solvables + p;
+ if (!s->repo)
+ continue;
+ if (s->repo == installed)
+ {
+ if (MAPTST(&userinstalled, p - installed->start))
+ queue_push(q, p);
+ continue;
+ }
+ /* new package, check if we replace a userinstalled one */
+ FOR_PROVIDES(p2, pp, s->name)
+ {
+ Solvable *ps = pool->solvables + p2;
+ if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
+ continue;
+ if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
+ continue;
+ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ queue_push(q, p);
+ break;
+ }
+ if (!p2 && s->repo != installed && s->obsoletes)
+ {
+ Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(p2, pp, obs)
+ {
+ Solvable *ps = pool->solvables + p2;
+ if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ queue_push(q, p);
+ break;
+ }
+ if (p2)
+ break;
+ }
+ }
+ }
+ }
+ map_free(&userinstalled);
+
+ /* convert to desired output format */
+ if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+ {
+ int qcount = q->count;
+ queue_insertn(q, 0, qcount, 0);
+ for (i = j = 0; i < qcount; i++)
+ {
+ s = pool->solvables + q->elements[i + qcount];
+ q->elements[j++] = s->name;
+ q->elements[j++] = s->arch;
+ }
+ }
+ else if ((flags & GET_USERINSTALLED_NAMES) != 0)
+ {
+ for (i = 0; i < q->count; i++)
+ {
+ s = pool->solvables + q->elements[i];
+ q->elements[i] = s->name;
+ }
+ }
+ /* sort and unify */
+ get_userinstalled_sort_uniq(pool, q, flags);
+
+ /* invert if asked for */
+ if ((flags & GET_USERINSTALLED_INVERTED) != 0)
+ {
+ /* first generate queue with all installed packages */
+ Queue invq;
+ queue_init(&invq);
+ for (i = 1; i < solv->decisionq.count; i++)
+ {
+ p = solv->decisionq.elements[i];
+ if (p <= 0)
+ continue;
+ s = pool->solvables + p;
+ if (!s->repo)
+ continue;
+ if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+ queue_push2(&invq, s->name, s->arch);
+ else if ((flags & GET_USERINSTALLED_NAMES) != 0)
+ queue_push(&invq, s->name);
+ else
+ queue_push(&invq, p);
+ }
+ /* push q on invq, just in case... */
+ queue_insertn(&invq, invq.count, q->count, q->elements);
+ get_userinstalled_sort_uniq(pool, &invq, flags);
+ /* subtract queues (easy as they are sorted and invq is a superset of q) */
+ if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+ {
+ if (q->count)
+ {
+ for (i = j = 0; i < invq.count; i += 2)
+ if (invq.elements[i] == q->elements[j] && invq.elements[i + 1] == q->elements[j + 1])
+ {
+ invq.elements[i] = invq.elements[i + 1] = 0;
+ j += 2;
+ if (j >= q->count)
+ break;
+ }
+ queue_empty(q);
+ }
+ for (i = 0; i < invq.count; i += 2)
+ if (invq.elements[i])
+ queue_push2(q, invq.elements[i], invq.elements[i + 1]);
+ }
+ else
+ {
+ if (q->count)
+ {
+ for (i = j = 0; i < invq.count; i++)
+ if (invq.elements[i] == q->elements[j])
+ {
+ invq.elements[i] = 0;
+ if (++j >= q->count)
+ break;
+ }
+ queue_empty(q);
+ }
+ for (i = 0; i < invq.count; i++)
+ if (invq.elements[i])
+ queue_push(q, invq.elements[i]);
+ }
+ queue_free(&invq);
+ }
+}
+
+void
+pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags)
+{
+ int i;
+
+ if ((flags & GET_USERINSTALLED_INVERTED) != 0)
+ {
+ Queue invq;
+ Id p, lastid;
+ Solvable *s;
+ int bad;
+ if (!pool->installed)
+ return;
+ queue_init(&invq);
+ if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+ flags &= ~GET_USERINSTALLED_NAMES; /* just in case */
+ FOR_REPO_SOLVABLES(pool->installed, p, s)
+ queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p);
+ if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+ {
+ /* for namearch we convert to packages */
+ namearch2solvables(pool, q, &invq, 0);
+ get_userinstalled_sort_uniq(pool, &invq, flags);
+ namearch2solvables(pool, q, &invq, 0);
+ flags = 0;
+ }
+ else
+ {
+ queue_insertn(&invq, invq.count, q->count, q->elements);
+ get_userinstalled_sort_uniq(pool, &invq, flags);
+ /* now the fun part, add q again, sort, and remove all dups */
+ queue_insertn(&invq, invq.count, q->count, q->elements);
+ }
+ if (invq.count > 1)
+ {
+ if ((flags & GET_USERINSTALLED_NAMES) != 0)
+ solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool);
+ else
+ solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0);
+ }
+ lastid = -1;
+ bad = 1;
+ for (i = 0; i < invq.count; i++)
+ {
+ if (invq.elements[i] == lastid)
+ {
+ bad = 1;
+ continue;
+ }
+ if (!bad)
+ queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
+ bad = 0;
+ lastid = invq.elements[i];
+ }
+ if (!bad)
+ queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
+ queue_free(&invq);
+ }
+ else
+ {
+ if (flags & GET_USERINSTALLED_NAMEARCH)
+ namearch2solvables(pool, q, job, SOLVER_USERINSTALLED | SOLVER_SOLVABLE);
+ else
+ {
+ for (i = 0; i < q->count; i++)
+ queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]);
+ }
+ }
+}
+
+int
+solver_alternatives_count(Solver *solv)
+{
+ Id *elements = solv->branches.elements;
+ int res, count;
+ for (res = 0, count = solv->branches.count; count; res++)
+ count -= elements[count - 2];
+ return res;
+}
+
+int
+solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp)
+{
+ int cnt = solver_alternatives_count(solv);
+ int count = solv->branches.count;
+ Id *elements = solv->branches.elements;
+ if (choices)
+ queue_empty(choices);
+ if (alternative <= 0 || alternative > cnt)
+ return 0;
+ elements += count;
+ for (; cnt > alternative; cnt--)
+ elements -= elements[-2];
+ if (levelp)
+ *levelp = elements[-1];
+ if (fromp)
+ *fromp = elements[-4];
+ if (idp)
+ *idp = elements[-3];
+ if (chosenp)
+ {
+ int i;
+ *chosenp = 0;
+ for (i = elements[-2]; i > 4; i--)
+ {
+ Id p = -elements[-i];
+ if (p > 0 && solv->decisionmap[p] == elements[-1] + 1)
+ {
+ *chosenp = p;
+ break;
+ }
+ }
+ }
+ if (choices)
+ queue_insertn(choices, 0, elements[-2] - 4, elements - elements[-2]);
+ return elements[-4] ? SOLVER_ALTERNATIVE_TYPE_RECOMMENDS : SOLVER_ALTERNATIVE_TYPE_RULE;
+}
+
+const char *
+solver_select2str(Pool *pool, Id select, Id what)
+{
+ const char *s;
+ char *b;
+ select &= SOLVER_SELECTMASK;
+ if (select == SOLVER_SOLVABLE)
+ return pool_solvid2str(pool, what);
+ if (select == SOLVER_SOLVABLE_NAME)
+ return pool_dep2str(pool, what);
+ if (select == SOLVER_SOLVABLE_PROVIDES)
+ {
+ s = pool_dep2str(pool, what);
+ b = pool_alloctmpspace(pool, 11 + strlen(s));
+ sprintf(b, "providing %s", s);
+ return b;
+ }
+ if (select == SOLVER_SOLVABLE_ONE_OF)
+ {
+ Id p;
+ b = 0;
+ while ((p = pool->whatprovidesdata[what++]) != 0)
+ {
+ s = pool_solvid2str(pool, p);
+ if (b)
+ b = pool_tmpappend(pool, b, ", ", s);
+ else
+ b = pool_tmpjoin(pool, s, 0, 0);
+ pool_freetmpspace(pool, s);
+ }
+ return b ? b : "nothing";
+ }
+ if (select == SOLVER_SOLVABLE_REPO)
+ {
+ b = pool_alloctmpspace(pool, 20);
+ sprintf(b, "repo #%d", what);
+ return b;
+ }
+ if (select == SOLVER_SOLVABLE_ALL)
+ return "all packages";
+ return "unknown job select";
+}
+
+const char *
+pool_job2str(Pool *pool, Id how, Id what, Id flagmask)
+{
+ Id select = how & SOLVER_SELECTMASK;
+ const char *strstart = 0, *strend = 0;
+ char *s;
+ int o;
+
+ switch (how & SOLVER_JOBMASK)
+ {
+ case SOLVER_NOOP:
+ return "do nothing";
+ case SOLVER_INSTALL:
+ if (select == SOLVER_SOLVABLE && pool->installed && pool->solvables[what].repo == pool->installed)
+ strstart = "keep ", strend = " installed";
+ else if (select == SOLVER_SOLVABLE || select == SOLVER_SOLVABLE_NAME)
+ strstart = "install ";
+ else if (select == SOLVER_SOLVABLE_PROVIDES)
+ strstart = "install a package ";
+ else
+ strstart = "install one of ";
+ break;
+ case SOLVER_ERASE:
+ if (select == SOLVER_SOLVABLE && !(pool->installed && pool->solvables[what].repo == pool->installed))
+ strstart = "keep ", strend = " uninstalled";
+ else if (select == SOLVER_SOLVABLE_PROVIDES)
+ strstart = "deinstall all packages ";
+ else
+ strstart = "deinstall ";
+ break;
+ case SOLVER_UPDATE:
+ strstart = "update ";
+ break;
+ case SOLVER_WEAKENDEPS:
+ strstart = "weaken deps of ";
+ break;
+ case SOLVER_MULTIVERSION:
+ strstart = "multi version ";
+ break;
+ case SOLVER_LOCK:
+ strstart = "lock ";
+ break;
+ case SOLVER_DISTUPGRADE:
+ strstart = "dist upgrade ";
+ break;
+ case SOLVER_VERIFY:
+ strstart = "verify ";
+ break;
+ case SOLVER_DROP_ORPHANED:
+ strstart = "deinstall ", strend = " if orphaned";
+ break;
+ case SOLVER_USERINSTALLED:
+ strstart = "regard ", strend = " as userinstalled";
+ break;
+ case SOLVER_ALLOWUNINSTALL:
+ strstart = "allow deinstallation of ";
+ break;
+ default:
+ strstart = "unknown job ";
+ break;
+ }
+ s = pool_tmpjoin(pool, strstart, solver_select2str(pool, select, what), strend);
+ how &= flagmask;
+ if ((how & ~(SOLVER_SELECTMASK|SOLVER_JOBMASK)) == 0)
+ return s;
+ o = strlen(s);
+ s = pool_tmpappend(pool, s, " ", 0);
+ if (how & SOLVER_WEAK)
+ s = pool_tmpappend(pool, s, ",weak", 0);
+ if (how & SOLVER_ESSENTIAL)
+ s = pool_tmpappend(pool, s, ",essential", 0);
+ if (how & SOLVER_CLEANDEPS)
+ s = pool_tmpappend(pool, s, ",cleandeps", 0);
+ if (how & SOLVER_ORUPDATE)
+ s = pool_tmpappend(pool, s, ",orupdate", 0);
+ if (how & SOLVER_FORCEBEST)
+ s = pool_tmpappend(pool, s, ",forcebest", 0);
+ if (how & SOLVER_TARGETED)
+ s = pool_tmpappend(pool, s, ",targeted", 0);
+ if (how & SOLVER_SETEV)
+ s = pool_tmpappend(pool, s, ",setev", 0);
+ if (how & SOLVER_SETEVR)
+ s = pool_tmpappend(pool, s, ",setevr", 0);
+ if (how & SOLVER_SETARCH)
+ s = pool_tmpappend(pool, s, ",setarch", 0);
+ if (how & SOLVER_SETVENDOR)
+ s = pool_tmpappend(pool, s, ",setvendor", 0);
+ if (how & SOLVER_SETREPO)
+ s = pool_tmpappend(pool, s, ",setrepo", 0);
+ if (how & SOLVER_SETNAME)
+ s = pool_tmpappend(pool, s, ",setname", 0);
+ if (how & SOLVER_NOAUTOSET)
+ s = pool_tmpappend(pool, s, ",noautoset", 0);
+ if (s[o + 1] != ',')
+ s = pool_tmpappend(pool, s, ",?", 0);
+ s[o + 1] = '[';
+ return pool_tmpappend(pool, s, "]", 0);
+}
+
+const char *
+solver_alternative2str(Solver *solv, int type, Id id, Id from)
+{
+ Pool *pool = solv->pool;
+ if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
+ {
+ const char *s = pool_dep2str(pool, id);
+ return pool_tmpappend(pool, s, ", recommended by ", pool_solvid2str(pool, from));
+ }
+ if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
+ {
+ int rtype;
+ Id depfrom, depto, dep;
+ char buf[64];
+ if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
+ id = solver_rule2pkgrule(solv, id);
+ rtype = solver_ruleinfo(solv, id, &depfrom, &depto, &dep);
+ if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
+ {
+ if ((depto & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES)
+ return pool_dep2str(pool, dep);
+ return solver_select2str(pool, depto & SOLVER_SELECTMASK, dep);
+ }
+ if (rtype == SOLVER_RULE_PKG_REQUIRES)
+ {
+ const char *s = pool_dep2str(pool, dep);
+ return pool_tmpappend(pool, s, ", required by ", pool_solvid2str(pool, depfrom));
+ }
+ sprintf(buf, "Rule #%d", id);
+ return pool_tmpjoin(pool, buf, 0, 0);
+ }
+ return "unknown alternative type";
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solver.h
+ *
+ */
+
+#ifndef LIBSOLV_SOLVER_H
+#define LIBSOLV_SOLVER_H
+
+#include "pooltypes.h"
+#include "pool.h"
+#include "repo.h"
+#include "queue.h"
+#include "bitmap.h"
+#include "transaction.h"
+#include "rules.h"
+#include "problems.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _Solver {
+ Pool *pool; /* back pointer to pool */
+ Queue job; /* copy of the job we're solving */
+
+ int (*solution_callback)(struct _Solver *solv, void *data);
+ void *solution_callback_data;
+
+ int pooljobcnt; /* number of pooljob entries in job queue */
+
+#ifdef LIBSOLV_INTERNAL
+ Repo *installed; /* copy of pool->installed */
+
+ /* list of rules, ordered
+ * pkg rules first, then features, updates, jobs, learnt
+ * see start/end offsets below
+ */
+ Rule *rules; /* all rules */
+ Id nrules; /* [Offset] index of the last rule */
+
+ Queue ruleassertions; /* Queue of all assertion rules */
+
+ /* start/end offset for rule 'areas' */
+
+ Id pkgrules_end; /* [Offset] dep rules end */
+
+ Id featurerules; /* feature rules start/end */
+ Id featurerules_end;
+
+ Id updaterules; /* policy rules, e.g. keep packages installed or update. All literals > 0 */
+ Id updaterules_end;
+
+ Id jobrules; /* user rules */
+ Id jobrules_end;
+
+ Id infarchrules; /* inferior arch rules */
+ Id infarchrules_end;
+
+ Id duprules; /* dist upgrade rules */
+ Id duprules_end;
+
+ Id bestrules; /* rules from SOLVER_FORCEBEST */
+ Id bestrules_end;
+ Id *bestrules_pkg;
+
+ Id yumobsrules; /* rules from yum obsoletes handling */
+ Id yumobsrules_end;
+ Id *yumobsrules_info; /* the dependency for each rule */
+
+ Id choicerules; /* choice rules (always weak) */
+ Id choicerules_end;
+ Id *choicerules_ref;
+
+ Id learntrules; /* learnt rules, (end == nrules) */
+
+ Map noupdate; /* don't try to update these
+ installed solvables */
+ Map multiversion; /* ignore obsoletes for these (multiinstall) */
+
+ Map updatemap; /* bring these installed packages to the newest version */
+ int updatemap_all; /* bring all packages to the newest version */
+
+ Map bestupdatemap; /* create best rule for those packages */
+ int bestupdatemap_all; /* bring all packages to the newest version */
+
+ Map fixmap; /* fix these packages */
+ int fixmap_all; /* fix all packages */
+
+ Queue weakruleq; /* index into 'rules' for weak ones */
+ Map weakrulemap; /* map rule# to '1' for weak rules, 1..learntrules */
+
+ 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
+ */
+
+ Queue ruletojob; /* index into job queue: jobs for which a rule exits */
+
+ /* our decisions: */
+ Queue decisionq; /* >0:install, <0:remove/conflict */
+ Queue decisionq_why; /* index of rule, Offset into rules */
+
+ Id *decisionmap; /* map for all available solvables,
+ * = 0: undecided
+ * > 0: level of decision when installed,
+ * < 0: level of decision when conflict */
+
+ int decisioncnt_jobs;
+ int decisioncnt_update;
+ int decisioncnt_keep;
+ int decisioncnt_resolve;
+ int decisioncnt_weak;
+ int decisioncnt_orphan;
+
+ /* learnt rule history */
+ Queue learnt_why;
+ Queue learnt_pool;
+
+ Queue branches;
+ int propagate_index; /* index into decisionq for non-propagated decisions */
+
+ Queue problems; /* list of lists of conflicting rules, < 0 for job rules */
+ Queue solutions; /* refined problem storage space */
+
+ Queue orphaned; /* orphaned packages (to be removed?) */
+
+ int stats_learned; /* statistic */
+ int stats_unsolvable; /* statistic */
+
+ Map recommendsmap; /* recommended packages from decisionmap */
+ Map suggestsmap; /* suggested packages from decisionmap */
+ int recommends_index; /* recommendsmap/suggestsmap is created up to this level */
+ Queue *recommendscplxq;
+ Queue *suggestscplxq;
+
+ Id *obsoletes; /* obsoletes for each installed solvable */
+ Id *obsoletes_data; /* data area for obsoletes */
+ Id *specialupdaters; /* updaters for packages with a limited update rule */
+
+ /*-------------------------------------------------------------------------------------------------------------
+ * Solver configuration
+ *-------------------------------------------------------------------------------------------------------------*/
+
+ int allowdowngrade; /* allow to downgrade installed solvable */
+ int allownamechange; /* allow to change name of installed solvables */
+ int allowarchchange; /* allow to change architecture of installed solvables */
+ int allowvendorchange; /* allow to change vendor of installed solvables */
+ int allowuninstall; /* allow removal of installed solvables */
+ int noupdateprovide; /* true: update packages needs not to provide old package */
+ int needupdateprovide; /* true: update packages must provide old package */
+ int dosplitprovides; /* true: consider legacy split provides */
+ int dontinstallrecommended; /* true: do not install recommended packages */
+ int addalreadyrecommended; /* true: also install recommended packages that were already recommended by the installed packages */
+ int dontshowinstalledrecommended; /* true: do not show recommended packages that are already installed */
+
+ int noinfarchcheck; /* true: do not forbid inferior architectures */
+ int keepexplicitobsoletes; /* true: honor obsoletes during multiinstall */
+ int bestobeypolicy; /* true: stay in policy with the best rules */
+ int noautotarget; /* true: do not assume targeted for up/dup jobs that contain no installed solvable */
+ int focus_installed; /* true: resolve update rules first */
+ int do_yum_obsoletes; /* true: add special yumobs rules */
+
+ Map dupmap; /* dup these packages*/
+ int dupmap_all; /* dup all packages */
+ Map dupinvolvedmap; /* packages involved in dup process */
+ int dup_allowdowngrade; /* dup mode: allow to downgrade installed solvable */
+ int dup_allownamechange; /* dup mode: allow to change name of installed solvable */
+ int dup_allowarchchange; /* dup mode: allow to change architecture of installed solvables */
+ int dup_allowvendorchange; /* dup mode: allow to change vendor of installed solvables */
+
+ Map droporphanedmap; /* packages to drop in dup mode */
+ int droporphanedmap_all;
+
+ Map cleandepsmap; /* try to drop these packages as of cleandeps erases */
+
+ Queue *ruleinfoq; /* tmp space for solver_ruleinfo() */
+
+ Queue *cleandeps_updatepkgs; /* packages we update in cleandeps mode */
+ Queue *cleandeps_mistakes; /* mistakes we made */
+
+ Queue *update_targets; /* update to specific packages */
+
+ Queue *installsuppdepq; /* deps from the install namespace provides hack */
+
+ Queue addedmap_deduceq; /* deduce addedmap from pkg rules */
+ Id *instbuddy; /* buddies of installed packages */
+ int keep_orphans; /* how to treat orphans */
+ int break_orphans; /* how to treat orphans */
+ Queue *brokenorphanrules; /* broken rules of orphaned packages */
+
+ Map allowuninstallmap; /* ok to uninstall those */
+ int allowuninstall_all;
+
+#endif /* LIBSOLV_INTERNAL */
+};
+
+typedef struct _Solver Solver;
+
+/*
+ * queue commands
+ */
+
+#define SOLVER_SOLVABLE 0x01
+#define SOLVER_SOLVABLE_NAME 0x02
+#define SOLVER_SOLVABLE_PROVIDES 0x03
+#define SOLVER_SOLVABLE_ONE_OF 0x04
+#define SOLVER_SOLVABLE_REPO 0x05
+#define SOLVER_SOLVABLE_ALL 0x06
+
+#define SOLVER_SELECTMASK 0xff
+
+#define SOLVER_NOOP 0x0000
+#define SOLVER_INSTALL 0x0100
+#define SOLVER_ERASE 0x0200
+#define SOLVER_UPDATE 0x0300
+#define SOLVER_WEAKENDEPS 0x0400
+#define SOLVER_MULTIVERSION 0x0500
+#define SOLVER_LOCK 0x0600
+#define SOLVER_DISTUPGRADE 0x0700
+#define SOLVER_VERIFY 0x0800
+#define SOLVER_DROP_ORPHANED 0x0900
+#define SOLVER_USERINSTALLED 0x0a00
+#define SOLVER_ALLOWUNINSTALL 0x0b00
+
+#define SOLVER_JOBMASK 0xff00
+
+#define SOLVER_WEAK 0x010000
+#define SOLVER_ESSENTIAL 0x020000
+#define SOLVER_CLEANDEPS 0x040000
+/* ORUPDATE makes SOLVER_INSTALL not prune to installed
+ * packages, thus updating installed packages */
+#define SOLVER_ORUPDATE 0x080000
+/* FORCEBEST makes the solver insist on best packages, so
+ * you will get problem reported if the best package is
+ * not installable. This can be used with INSTALL, UPDATE
+ * and DISTUPGRADE */
+#define SOLVER_FORCEBEST 0x100000
+/* TARGETED is used in up/dup jobs to make the solver
+ * treat the specified selection as target packages.
+ * It is automatically assumed if the selection does not
+ * contain an "installed" package unless the
+ * NO_AUTOTARGET solver flag is set */
+#define SOLVER_TARGETED 0x200000
+/* This (SOLVER_INSTALL) job was automatically added
+ * and thus does not the add to the userinstalled packages */
+#define SOLVER_NOTBYUSER 0x400000
+
+#define SOLVER_SETEV 0x01000000
+#define SOLVER_SETEVR 0x02000000
+#define SOLVER_SETARCH 0x04000000
+#define SOLVER_SETVENDOR 0x08000000
+#define SOLVER_SETREPO 0x10000000
+#define SOLVER_NOAUTOSET 0x20000000
+#define SOLVER_SETNAME 0x40000000
+
+#define SOLVER_SETMASK 0x7f000000
+
+/* legacy */
+#define SOLVER_NOOBSOLETES SOLVER_MULTIVERSION
+
+#define SOLVER_REASON_UNRELATED 0
+#define SOLVER_REASON_UNIT_RULE 1
+#define SOLVER_REASON_KEEP_INSTALLED 2
+#define SOLVER_REASON_RESOLVE_JOB 3
+#define SOLVER_REASON_UPDATE_INSTALLED 4
+#define SOLVER_REASON_CLEANDEPS_ERASE 5
+#define SOLVER_REASON_RESOLVE 6
+#define SOLVER_REASON_WEAKDEP 7
+#define SOLVER_REASON_RESOLVE_ORPHAN 8
+
+#define SOLVER_REASON_RECOMMENDED 16
+#define SOLVER_REASON_SUPPLEMENTED 17
+
+
+#define SOLVER_FLAG_ALLOW_DOWNGRADE 1
+#define SOLVER_FLAG_ALLOW_ARCHCHANGE 2
+#define SOLVER_FLAG_ALLOW_VENDORCHANGE 3
+#define SOLVER_FLAG_ALLOW_UNINSTALL 4
+#define SOLVER_FLAG_NO_UPDATEPROVIDE 5
+#define SOLVER_FLAG_SPLITPROVIDES 6
+#define SOLVER_FLAG_IGNORE_RECOMMENDED 7
+#define SOLVER_FLAG_ADD_ALREADY_RECOMMENDED 8
+#define SOLVER_FLAG_NO_INFARCHCHECK 9
+#define SOLVER_FLAG_ALLOW_NAMECHANGE 10
+#define SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES 11
+#define SOLVER_FLAG_BEST_OBEY_POLICY 12
+#define SOLVER_FLAG_NO_AUTOTARGET 13
+#define SOLVER_FLAG_DUP_ALLOW_DOWNGRADE 14
+#define SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE 15
+#define SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE 16
+#define SOLVER_FLAG_DUP_ALLOW_NAMECHANGE 17
+#define SOLVER_FLAG_KEEP_ORPHANS 18
+#define SOLVER_FLAG_BREAK_ORPHANS 19
+#define SOLVER_FLAG_FOCUS_INSTALLED 20
+#define SOLVER_FLAG_YUM_OBSOLETES 21
+#define SOLVER_FLAG_NEED_UPDATEPROVIDE 22
+
+#define GET_USERINSTALLED_NAMES (1 << 0) /* package names instead of ids */
+#define GET_USERINSTALLED_INVERTED (1 << 1) /* autoinstalled */
+#define GET_USERINSTALLED_NAMEARCH (1 << 2) /* package/arch tuples instead of ids */
+
+#define SOLVER_ALTERNATIVE_TYPE_RULE 1
+#define SOLVER_ALTERNATIVE_TYPE_RECOMMENDS 2
+#define SOLVER_ALTERNATIVE_TYPE_SUGGESTS 3
+
+extern Solver *solver_create(Pool *pool);
+extern void solver_free(Solver *solv);
+extern int solver_solve(Solver *solv, Queue *job);
+extern Transaction *solver_create_transaction(Solver *solv);
+extern int solver_set_flag(Solver *solv, int flag, int value);
+extern int solver_get_flag(Solver *solv, int flag);
+
+extern int solver_get_decisionlevel(Solver *solv, Id p);
+extern void solver_get_decisionqueue(Solver *solv, Queue *decisionq);
+extern int solver_get_lastdecisionblocklevel(Solver *solv);
+extern void solver_get_decisionblock(Solver *solv, int level, Queue *decisionq);
+
+extern void solver_get_orphaned(Solver *solv, Queue *orphanedq);
+extern void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *suggestionsq, int noselected);
+extern void solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered);
+extern void solver_get_userinstalled(Solver *solv, Queue *q, int flags);
+extern void pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags);
+
+extern int solver_describe_decision(Solver *solv, Id p, Id *infop);
+extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq);
+
+extern int solver_alternatives_count(Solver *solv);
+extern int solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp);
+
+extern void solver_calculate_multiversionmap(Pool *pool, Queue *job, Map *multiversionmap);
+extern void solver_calculate_noobsmap(Pool *pool, Queue *job, Map *multiversionmap); /* obsolete */
+extern void solver_create_state_maps(Solver *solv, Map *installedmap, Map *conflictsmap);
+
+extern void solver_calc_duchanges(Solver *solv, DUChanges *mps, int nmps);
+extern int solver_calc_installsizechange(Solver *solv);
+extern void solver_trivial_installable(Solver *solv, Queue *pkgs, Queue *res);
+
+extern void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what);
+extern int pool_isemptyupdatejob(Pool *pool, Id how, Id what);
+
+extern const char *solver_select2str(Pool *pool, Id select, Id what);
+extern const char *pool_job2str(Pool *pool, Id how, Id what, Id flagmask);
+extern const char *solver_alternative2str(Solver *solv, int type, Id id, Id from);
+
+
+/* iterate over all literals of a rule */
+#define FOR_RULELITERALS(l, pp, r) \
+ for (pp = r->d < 0 ? -r->d - 1 : r->d, \
+ l = r->p; l; l = (pp <= 0 ? (pp-- ? 0 : r->w2) : \
+ pool->whatprovidesdata[pp++]))
+
+
+
+
+/* XXX: this currently doesn't work correctly for SOLVER_SOLVABLE_REPO and
+ SOLVER_SOLVABLE_ALL */
+
+/* iterate over all packages selected by a job */
+#define FOR_JOB_SELECT(p, pp, select, what) \
+ if (select == SOLVER_SOLVABLE_REPO || select == SOLVER_SOLVABLE_ALL) \
+ p = pp = 0; \
+ else for (pp = (select == SOLVER_SOLVABLE ? 0 : \
+ select == SOLVER_SOLVABLE_ONE_OF ? what : \
+ pool_whatprovides(pool, what)), \
+ p = (select == SOLVER_SOLVABLE ? what : pool->whatprovidesdata[pp++]); \
+ p ; p = pool->whatprovidesdata[pp++]) \
+ if (select == SOLVER_SOLVABLE_NAME && \
+ pool_match_nevr(pool, pool->solvables + p, what) == 0) \
+ continue; \
+ else
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_SOLVER_H */
--- /dev/null
+/*
+ * Copyright (c) 2011, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solver_private.h - private functions
+ *
+ */
+
+#ifndef LIBSOLV_SOLVER_PRIVATE_H
+#define LIBSOLV_SOLVER_PRIVATE_H
+
+extern void solver_run_sat(Solver *solv, int disablerules, int doweak);
+extern void solver_reset(Solver *solv);
+
+extern int solver_splitprovides(Solver *solv, Id dep, Map *m);
+
+static inline int
+solver_dep_fulfilled(Solver *solv, Id dep)
+{
+ Pool *pool = solv->pool;
+ Id p, pp;
+
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags == REL_COND)
+ {
+ if (ISRELDEP(rd->evr))
+ {
+ Reldep *rd2 = GETRELDEP(pool, rd->evr);
+ if (rd2->flags == REL_ELSE)
+ {
+ if (solver_dep_fulfilled(solv, rd2->name))
+ return solver_dep_fulfilled(solv, rd->name);
+ return solver_dep_fulfilled(solv, rd2->evr);
+ }
+ }
+ if (solver_dep_fulfilled(solv, rd->name))
+ return 1;
+ return !solver_dep_fulfilled(solv, rd->evr);
+ }
+ if (rd->flags == REL_AND)
+ {
+ if (!solver_dep_fulfilled(solv, rd->name))
+ return 0;
+ return solver_dep_fulfilled(solv, rd->evr);
+ }
+ if (rd->flags == REL_OR)
+ {
+ if (solver_dep_fulfilled(solv, rd->name))
+ return 1;
+ return solver_dep_fulfilled(solv, rd->evr);
+ }
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+ return solver_splitprovides(solv, rd->evr, 0);
+ }
+ FOR_PROVIDES(p, pp, dep)
+ {
+ if (solv->decisionmap[p] > 0)
+ return 1;
+ }
+ return 0;
+}
+
+static inline int
+solver_is_supplementing(Solver *solv, Solvable *s)
+{
+ Id sup, *supp;
+ if (!s->supplements)
+ return 0;
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (solver_dep_fulfilled(solv, sup))
+ return 1;
+ return 0;
+}
+
+static inline int
+solver_is_enhancing(Solver *solv, Solvable *s)
+{
+ Id enh, *enhp;
+ if (!s->enhances)
+ return 0;
+ enhp = s->repo->idarraydata + s->enhances;
+ while ((enh = *enhp++) != 0)
+ if (solver_dep_fulfilled(solv, enh))
+ return 1;
+ return 0;
+}
+
+#endif /* LIBSOLV_SOLVER_PRIVATE_H */
--- /dev/null
+/*
+ * Copyright (c) 2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solverdebug.c
+ *
+ * debug functions for the SAT solver
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "solver.h"
+#include "solver_private.h"
+#include "solverdebug.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "poolarch.h"
+#include "util.h"
+#include "evr.h"
+#include "policy.h"
+
+
+void
+solver_printruleelement(Solver *solv, int type, Rule *r, Id v)
+{
+ Pool *pool = solv->pool;
+ Solvable *s;
+ if (v < 0)
+ {
+ s = pool->solvables + -v;
+ POOL_DEBUG(type, " !%s [%d]", pool_solvable2str(pool, s), -v);
+ }
+ else
+ {
+ s = pool->solvables + v;
+ POOL_DEBUG(type, " %s [%d]", pool_solvable2str(pool, s), v);
+ }
+ if (pool->installed && s->repo == pool->installed)
+ POOL_DEBUG(type, "I");
+ if (r)
+ {
+ if (r->w1 == v)
+ POOL_DEBUG(type, " (w1)");
+ if (r->w2 == v)
+ POOL_DEBUG(type, " (w2)");
+ }
+ if (solv->decisionmap[s - pool->solvables] > 0)
+ POOL_DEBUG(type, " Install.level%d", solv->decisionmap[s - pool->solvables]);
+ if (solv->decisionmap[s - pool->solvables] < 0)
+ POOL_DEBUG(type, " Conflict.level%d", -solv->decisionmap[s - pool->solvables]);
+ POOL_DEBUG(type, "\n");
+}
+
+
+/*
+ * print rule
+ */
+
+void
+solver_printrule(Solver *solv, int type, Rule *r)
+{
+ Pool *pool = solv->pool;
+ int i;
+ Id d, v;
+
+ if (r >= solv->rules && r < solv->rules + solv->nrules) /* r is a solver rule */
+ POOL_DEBUG(type, "Rule #%d:", (int)(r - solv->rules));
+ else
+ POOL_DEBUG(type, "Rule:"); /* r is any rule */
+ if (r->d < 0)
+ POOL_DEBUG(type, " (disabled)");
+ POOL_DEBUG(type, "\n");
+ d = r->d < 0 ? -r->d - 1 : r->d;
+ for (i = 0; ; i++)
+ {
+ if (i == 0)
+ /* print direct literal */
+ v = r->p;
+ else if (!d)
+ {
+ if (i == 2)
+ break;
+ /* binary rule --> print w2 as second literal */
+ v = r->w2;
+ }
+ else
+ /* every other which is in d */
+ v = solv->pool->whatprovidesdata[d + i - 1];
+ if (v == ID_NULL)
+ break;
+ solver_printruleelement(solv, type, r, v);
+ }
+ POOL_DEBUG(type, " next rules: %d %d\n", r->n1, r->n2);
+}
+
+void
+solver_printruleclass(Solver *solv, int type, Rule *r)
+{
+ Pool *pool = solv->pool;
+ Id p = r - solv->rules;
+ assert(p >= 0);
+ if (p < solv->learntrules)
+ if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, p))
+ POOL_DEBUG(type, "WEAK ");
+ if (solv->learntrules && p >= solv->learntrules)
+ POOL_DEBUG(type, "LEARNT ");
+ else if (p >= solv->bestrules && p < solv->bestrules_end)
+ POOL_DEBUG(type, "BEST ");
+ else if (p >= solv->choicerules && p < solv->choicerules_end)
+ POOL_DEBUG(type, "CHOICE ");
+ else if (p >= solv->infarchrules && p < solv->infarchrules_end)
+ POOL_DEBUG(type, "INFARCH ");
+ else if (p >= solv->duprules && p < solv->duprules_end)
+ POOL_DEBUG(type, "DUP ");
+ else if (p >= solv->jobrules && p < solv->jobrules_end)
+ POOL_DEBUG(type, "JOB ");
+ else if (p >= solv->updaterules && p < solv->updaterules_end)
+ POOL_DEBUG(type, "UPDATE ");
+ else if (p >= solv->featurerules && p < solv->featurerules_end)
+ POOL_DEBUG(type, "FEATURE ");
+ else if (p >= solv->yumobsrules && p < solv->yumobsrules_end)
+ POOL_DEBUG(type, "YUMOBS ");
+ solver_printrule(solv, type, r);
+}
+
+void
+solver_printproblem(Solver *solv, Id v)
+{
+ Pool *pool = solv->pool;
+ int i;
+ Rule *r;
+ Id *jp;
+
+ if (v > 0)
+ solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, solv->rules + v);
+ else
+ {
+ v = -(v + 1);
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "JOB %d\n", v);
+ jp = solv->ruletojob.elements;
+ for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
+ if (*jp == v)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "- ");
+ solver_printrule(solv, SOLV_DEBUG_SOLUTIONS, r);
+ }
+ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "ENDJOB\n");
+ }
+}
+
+void
+solver_printwatches(Solver *solv, int type)
+{
+ Pool *pool = solv->pool;
+ int counter;
+
+ POOL_DEBUG(type, "Watches: \n");
+ for (counter = -(pool->nsolvables - 1); counter < pool->nsolvables; counter++)
+ POOL_DEBUG(type, " solvable [%d] -- rule [%d]\n", counter, solv->watches[counter + pool->nsolvables]);
+}
+
+void
+solver_printdecisionq(Solver *solv, int type)
+{
+ Pool *pool = solv->pool;
+ int i;
+ Id p, why;
+
+ POOL_DEBUG(type, "Decisions:\n");
+ for (i = 0; i < solv->decisionq.count; i++)
+ {
+ p = solv->decisionq.elements[i];
+ if (p > 0)
+ POOL_DEBUG(type, "%d %d install %s, ", i, solv->decisionmap[p], pool_solvid2str(pool, p));
+ else
+ POOL_DEBUG(type, "%d %d conflict %s, ", i, -solv->decisionmap[-p], pool_solvid2str(pool, -p));
+ why = solv->decisionq_why.elements[i];
+ if (why > 0)
+ {
+ POOL_DEBUG(type, "forced by ");
+ solver_printruleclass(solv, type, solv->rules + why);
+ }
+ else if (why < 0)
+ {
+ POOL_DEBUG(type, "chosen from ");
+ solver_printruleclass(solv, type, solv->rules - why);
+ }
+ else
+ POOL_DEBUG(type, "picked for some unknown reason.\n");
+ }
+}
+
+/*
+ * printdecisions
+ */
+
+void
+solver_printdecisions(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ Transaction *trans = solver_create_transaction(solv);
+ Id p, type;
+ int i, j;
+ Solvable *s;
+ Queue iq;
+ Queue recommendations;
+ Queue suggestions;
+ Queue orphaned;
+
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "transaction:\n");
+
+ queue_init(&iq);
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ p = trans->steps.elements[i];
+ s = pool->solvables + p;
+ type = transaction_type(trans, p, SOLVER_TRANSACTION_SHOW_ACTIVE|SOLVER_TRANSACTION_SHOW_ALL|SOLVER_TRANSACTION_SHOW_OBSOLETES|SOLVER_TRANSACTION_SHOW_MULTIINSTALL);
+ switch(type)
+ {
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " multi install %s", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_TRANSACTION_MULTIREINSTALL:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " multi reinstall %s", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_TRANSACTION_INSTALL:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " install %s", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_TRANSACTION_REINSTALL:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " reinstall %s", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_TRANSACTION_DOWNGRADE:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " downgrade %s", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_TRANSACTION_CHANGE:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " change %s", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_TRANSACTION_UPGRADE:
+ case SOLVER_TRANSACTION_OBSOLETES:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " upgrade %s", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_TRANSACTION_ERASE:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " erase %s", pool_solvable2str(pool, s));
+ break;
+ default:
+ break;
+ }
+ switch(type)
+ {
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_ERASE:
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ case SOLVER_TRANSACTION_MULTIREINSTALL:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ break;
+ case SOLVER_TRANSACTION_REINSTALL:
+ case SOLVER_TRANSACTION_DOWNGRADE:
+ case SOLVER_TRANSACTION_CHANGE:
+ case SOLVER_TRANSACTION_UPGRADE:
+ case SOLVER_TRANSACTION_OBSOLETES:
+ transaction_all_obs_pkgs(trans, p, &iq);
+ if (iq.count)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " (obsoletes");
+ for (j = 0; j < iq.count; j++)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s", pool_solvid2str(pool, iq.elements[j]));
+ POOL_DEBUG(SOLV_DEBUG_RESULT, ")");
+ }
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ break;
+ default:
+ break;
+ }
+ }
+ queue_free(&iq);
+
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+
+ queue_init(&recommendations);
+ queue_init(&suggestions);
+ queue_init(&orphaned);
+ solver_get_recommendations(solv, &recommendations, &suggestions, 0);
+ solver_get_orphaned(solv, &orphaned);
+ if (recommendations.count)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "recommended packages:\n");
+ for (i = 0; i < recommendations.count; i++)
+ {
+ s = pool->solvables + recommendations.elements[i];
+ if (solv->decisionmap[recommendations.elements[i]] > 0)
+ {
+ if (installed && s->repo == installed)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (installed)\n", pool_solvable2str(pool, s));
+ else
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (selected)\n", pool_solvable2str(pool, s));
+ }
+ else
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", pool_solvable2str(pool, s));
+ }
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ }
+
+ if (suggestions.count)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "suggested packages:\n");
+ for (i = 0; i < suggestions.count; i++)
+ {
+ s = pool->solvables + suggestions.elements[i];
+ if (solv->decisionmap[suggestions.elements[i]] > 0)
+ {
+ if (installed && s->repo == installed)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (installed)\n", pool_solvable2str(pool, s));
+ else
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (selected)\n", pool_solvable2str(pool, s));
+ }
+ else
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", pool_solvable2str(pool, s));
+ }
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ }
+ if (orphaned.count)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "orphaned packages:\n");
+ for (i = 0; i < orphaned.count; i++)
+ {
+ s = pool->solvables + orphaned.elements[i];
+ if (solv->decisionmap[solv->orphaned.elements[i]] > 0)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (kept)\n", pool_solvable2str(pool, s));
+ else
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (erased)\n", pool_solvable2str(pool, s));
+ }
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ }
+ queue_free(&recommendations);
+ queue_free(&suggestions);
+ queue_free(&orphaned);
+ transaction_free(trans);
+}
+
+static inline
+const char *id2strnone(Pool *pool, Id id)
+{
+ return !id || id == 1 ? "(none)" : pool_id2str(pool, id);
+}
+
+void
+transaction_print(Transaction *trans)
+{
+ Pool *pool = trans->pool;
+ Queue classes, pkgs;
+ int i, j, mode, l, linel;
+ char line[76];
+ const char *n;
+
+ queue_init(&classes);
+ queue_init(&pkgs);
+ mode = SOLVER_TRANSACTION_SHOW_OBSOLETES | SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE;
+ transaction_classify(trans, mode, &classes);
+ for (i = 0; i < classes.count; i += 4)
+ {
+ Id class = classes.elements[i];
+ Id cnt = classes.elements[i + 1];
+ switch(class)
+ {
+ case SOLVER_TRANSACTION_ERASE:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%d erased packages:\n", cnt);
+ break;
+ case SOLVER_TRANSACTION_INSTALL:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%d installed packages:\n", cnt);
+ break;
+ case SOLVER_TRANSACTION_REINSTALLED:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%d reinstalled packages:\n", cnt);
+ break;
+ case SOLVER_TRANSACTION_DOWNGRADED:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%d downgraded packages:\n", cnt);
+ break;
+ case SOLVER_TRANSACTION_CHANGED:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%d changed packages:\n", cnt);
+ break;
+ case SOLVER_TRANSACTION_UPGRADED:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%d upgraded packages:\n", cnt);
+ break;
+ case SOLVER_TRANSACTION_VENDORCHANGE:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%d vendor changes from '%s' to '%s':\n", cnt, id2strnone(pool, classes.elements[i + 2]), id2strnone(pool, classes.elements[i + 3]));
+ break;
+ case SOLVER_TRANSACTION_ARCHCHANGE:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%d arch changes from %s to %s:\n", cnt, pool_id2str(pool, classes.elements[i + 2]), pool_id2str(pool, classes.elements[i + 3]));
+ break;
+ default:
+ class = SOLVER_TRANSACTION_IGNORE;
+ break;
+ }
+ if (class == SOLVER_TRANSACTION_IGNORE)
+ continue;
+ transaction_classify_pkgs(trans, mode, class, classes.elements[i + 2], classes.elements[i + 3], &pkgs);
+ *line = 0;
+ linel = 0;
+ for (j = 0; j < pkgs.count; j++)
+ {
+ Id p = pkgs.elements[j];
+ Solvable *s = pool->solvables + p;
+ Solvable *s2;
+
+ switch(class)
+ {
+ case SOLVER_TRANSACTION_DOWNGRADED:
+ case SOLVER_TRANSACTION_UPGRADED:
+ s2 = pool->solvables + transaction_obs_pkg(trans, p);
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " - %s -> %s\n", pool_solvable2str(pool, s), pool_solvable2str(pool, s2));
+ break;
+ case SOLVER_TRANSACTION_VENDORCHANGE:
+ case SOLVER_TRANSACTION_ARCHCHANGE:
+ n = pool_id2str(pool, s->name);
+ l = strlen(n);
+ if (l + linel > sizeof(line) - 3)
+ {
+ if (*line)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", line);
+ *line = 0;
+ linel = 0;
+ }
+ if (l + linel > sizeof(line) - 3)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", n);
+ else
+ {
+ if (*line)
+ {
+ strcpy(line + linel, ", ");
+ linel += 2;
+ }
+ strcpy(line + linel, n);
+ linel += l;
+ }
+ break;
+ default:
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " - %s\n", pool_solvable2str(pool, s));
+ break;
+ }
+ }
+ if (*line)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", line);
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ }
+ queue_free(&classes);
+ queue_free(&pkgs);
+}
+
+void
+solver_printproblemruleinfo(Solver *solv, Id probr)
+{
+ Pool *pool = solv->pool;
+ Id dep, source, target;
+ SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep);
+
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "%s\n", solver_problemruleinfo2str(solv, type, source, target, dep));
+}
+
+void
+solver_printprobleminfo(Solver *solv, Id problem)
+{
+ solver_printproblemruleinfo(solv, solver_findproblemrule(solv, problem));
+}
+
+void
+solver_printcompleteprobleminfo(Solver *solv, Id problem)
+{
+ Queue q;
+ Id probr;
+ int i, nobad = 0;
+
+ queue_init(&q);
+ solver_findallproblemrules(solv, problem, &q);
+ for (i = 0; i < q.count; i++)
+ {
+ probr = q.elements[i];
+ if (!(probr >= solv->updaterules && probr < solv->updaterules_end) && !(probr >= solv->jobrules && probr < solv->jobrules_end))
+ {
+ nobad = 1;
+ break;
+ }
+ }
+ for (i = 0; i < q.count; i++)
+ {
+ probr = q.elements[i];
+ if (nobad && ((probr >= solv->updaterules && probr < solv->updaterules_end) || (probr >= solv->jobrules && probr < solv->jobrules_end)))
+ continue;
+ solver_printproblemruleinfo(solv, probr);
+ }
+ queue_free(&q);
+}
+
+static int illegals[] = {
+ POLICY_ILLEGAL_DOWNGRADE,
+ POLICY_ILLEGAL_NAMECHANGE,
+ POLICY_ILLEGAL_ARCHCHANGE,
+ POLICY_ILLEGAL_VENDORCHANGE,
+ 0
+};
+
+void
+solver_printsolution(Solver *solv, Id problem, Id solution)
+{
+ Pool *pool = solv->pool;
+ Id p, rp, element;
+
+ element = 0;
+ while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
+ {
+ if (p > 0 && rp > 0)
+ {
+ /* for replacements we want to know why it was illegal */
+ Solvable *s = pool->solvables + p, *rs = pool->solvables + rp;
+ int illegal = policy_is_illegal(solv, s, rs, 0);
+ if (illegal)
+ {
+ int i;
+ for (i = 0; illegals[i]; i++)
+ if ((illegal & illegals[i]) != 0)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " - allow %s\n", policy_illegal2str(solv, illegals[i], s, rs));
+ illegal ^= illegals[i];
+ }
+ if (!illegal)
+ continue;
+ }
+ }
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " - %s\n", solver_solutionelement2str(solv, p, rp));
+ }
+}
+
+void
+solver_printallsolutions(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ int pcnt;
+ Id problem, solution;
+
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "Encountered problems! Here are the solutions:\n\n");
+ pcnt = 0;
+ problem = 0;
+ while ((problem = solver_next_problem(solv, problem)) != 0)
+ {
+ pcnt++;
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "Problem %d:\n", pcnt);
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "====================================\n");
+#if 1
+ solver_printprobleminfo(solv, problem);
+#else
+ solver_printcompleteprobleminfo(solv, problem);
+#endif
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ solution = 0;
+ while ((solution = solver_next_solution(solv, problem, solution)) != 0)
+ {
+ solver_printsolution(solv, problem, solution);
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ }
+ }
+}
+
+void
+solver_printtrivial(Solver *solv)
+{
+ Pool *pool = solv->pool;
+ Queue in, out;
+ Id p;
+ const char *n;
+ Solvable *s;
+ int i;
+
+ queue_init(&in);
+ for (p = 1, s = pool->solvables + p; p < solv->pool->nsolvables; p++, s++)
+ {
+ n = pool_id2str(pool, s->name);
+ if (strncmp(n, "patch:", 6) != 0 && strncmp(n, "pattern:", 8) != 0)
+ continue;
+ queue_push(&in, p);
+ }
+ if (!in.count)
+ {
+ queue_free(&in);
+ return;
+ }
+ queue_init(&out);
+ solver_trivial_installable(solv, &in, &out);
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "trivial installable status:\n");
+ for (i = 0; i < in.count; i++)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %s: %d\n", pool_solvid2str(pool, in.elements[i]), out.elements[i]);
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
+ queue_free(&in);
+ queue_free(&out);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2008, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solverdebug.h
+ *
+ */
+
+#ifndef LIBSOLV_SOLVERDEBUG_H
+#define LIBSOLV_SOLVERDEBUG_H
+
+#include "pooltypes.h"
+#include "pool.h"
+#include "solver.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void solver_printruleelement(Solver *solv, int type, Rule *r, Id v);
+extern void solver_printrule(Solver *solv, int type, Rule *r);
+extern void solver_printruleclass(Solver *solv, int type, Rule *r);
+extern void solver_printproblem(Solver *solv, Id v);
+extern void solver_printwatches(Solver *solv, int type);
+extern void solver_printdecisionq(Solver *solv, int type);
+extern void solver_printdecisions(Solver *solv);
+extern void solver_printproblemruleinfo(Solver *solv, Id rule);
+extern void solver_printprobleminfo(Solver *solv, Id problem);
+extern void solver_printcompleteprobleminfo(Solver *solv, Id problem);
+extern void solver_printsolution(Solver *solv, Id problem, Id solution);
+extern void solver_printallsolutions(Solver *solv);
+extern void solver_printtrivial(Solver *solv);
+
+extern void transaction_print(Transaction *trans);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_SOLVERDEBUG_H */
+
--- /dev/null
+/*
+ * Copyright (c) 2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include "solvversion.h"
+
+const char solv_version[] = LIBSOLV_VERSION_STRING;
+int solv_version_major = LIBSOLV_VERSION_MAJOR;
+int solv_version_minor = LIBSOLV_VERSION_MINOR;
+int solv_version_patch = LIBSOLV_VERSION_PATCH;
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solvversion.h
+ *
+ */
+
+#ifndef LIBSOLV_SOLVVERSION_H
+#define LIBSOLV_SOLVVERSION_H
+
+#define LIBSOLV_VERSION_STRING "@VERSION@"
+#define LIBSOLV_VERSION_MAJOR @LIBSOLV_MAJOR@
+#define LIBSOLV_VERSION_MINOR @LIBSOLV_MINOR@
+#define LIBSOLV_VERSION_PATCH @LIBSOLV_PATCH@
+#define LIBSOLV_VERSION (LIBSOLV_VERSION_MAJOR * 10000 + LIBSOLV_VERSION_MINOR * 100 + LIBSOLV_VERSION_PATCH)
+
+extern const char solv_version[];
+extern int solv_version_major;
+extern int solv_version_minor;
+extern int solv_version_patch;
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <string.h>
+#include "util.h"
+#include "strpool.h"
+
+#define STRING_BLOCK 2047
+#define STRINGSPACE_BLOCK 65535
+
+void
+stringpool_init(Stringpool *ss, const char *strs[])
+{
+ unsigned totalsize = 0;
+ unsigned count;
+
+ memset(ss, 0, sizeof(*ss));
+ /* count number and total size of predefined strings */
+ for (count = 0; strs[count]; count++)
+ totalsize += strlen(strs[count]) + 1;
+
+ /* alloc appropriate space */
+ ss->stringspace = solv_extend_resize(0, totalsize, 1, STRINGSPACE_BLOCK);
+ ss->strings = solv_extend_resize(0, count, sizeof(Offset), STRING_BLOCK);
+
+ /* now copy predefined strings into allocated space */
+ ss->sstrings = 0;
+ for (count = 0; strs[count]; count++)
+ {
+ strcpy(ss->stringspace + ss->sstrings, strs[count]);
+ ss->strings[count] = ss->sstrings;
+ ss->sstrings += strlen(strs[count]) + 1;
+ }
+ ss->nstrings = count;
+}
+
+void
+stringpool_free(Stringpool *ss)
+{
+ solv_free(ss->strings);
+ solv_free(ss->stringspace);
+ solv_free(ss->stringhashtbl);
+}
+
+void
+stringpool_freehash(Stringpool *ss)
+{
+ ss->stringhashtbl = solv_free(ss->stringhashtbl);
+ ss->stringhashmask = 0;
+}
+
+void
+stringpool_init_empty(Stringpool *ss)
+{
+ const char *emptystrs[] = {
+ "<NULL>",
+ "",
+ 0,
+ };
+ stringpool_init(ss, emptystrs);
+}
+
+void
+stringpool_clone(Stringpool *ss, Stringpool *from)
+{
+ memset(ss, 0, sizeof(*ss));
+ ss->strings = solv_extend_resize(0, from->nstrings, sizeof(Offset), STRING_BLOCK);
+ memcpy(ss->strings, from->strings, from->nstrings * sizeof(Offset));
+ ss->stringspace = solv_extend_resize(0, from->sstrings, 1, STRINGSPACE_BLOCK);
+ memcpy(ss->stringspace, from->stringspace, from->sstrings);
+ ss->nstrings = from->nstrings;
+ ss->sstrings = from->sstrings;
+}
+
+Id
+stringpool_strn2id(Stringpool *ss, const char *str, unsigned int len, int create)
+{
+ Hashval h, hh, hashmask, oldhashmask;
+ int i;
+ Id id;
+ Hashtable hashtbl;
+
+ if (!str)
+ return STRID_NULL;
+ if (!len)
+ return STRID_EMPTY;
+
+ hashmask = oldhashmask = ss->stringhashmask;
+ hashtbl = ss->stringhashtbl;
+
+ /* expand hashtable if needed */
+ if ((Hashval)ss->nstrings * 2 > hashmask)
+ {
+ solv_free(hashtbl);
+
+ /* realloc hash table */
+ ss->stringhashmask = hashmask = mkmask(ss->nstrings + STRING_BLOCK);
+ ss->stringhashtbl = hashtbl = (Hashtable)solv_calloc(hashmask + 1, sizeof(Id));
+
+ /* rehash all strings into new hashtable */
+ for (i = 1; i < ss->nstrings; i++)
+ {
+ h = strhash(ss->stringspace + ss->strings[i]) & hashmask;
+ hh = HASHCHAIN_START;
+ while (hashtbl[h] != 0)
+ h = HASHCHAIN_NEXT(h, hh, hashmask);
+ hashtbl[h] = i;
+ }
+ }
+
+ /* compute hash and check for match */
+ h = strnhash(str, len) & hashmask;
+ hh = HASHCHAIN_START;
+ while ((id = hashtbl[h]) != 0)
+ {
+ if(!memcmp(ss->stringspace + ss->strings[id], str, len)
+ && ss->stringspace[ss->strings[id] + len] == 0)
+ break;
+ h = HASHCHAIN_NEXT(h, hh, hashmask);
+ }
+ if (id || !create) /* exit here if string found */
+ return id;
+
+ /* this should be a test for a flag that tells us if the
+ * correct blocking is used, but adding a flag would break
+ * the ABI. So we use the existance of the hash area as
+ * indication instead */
+ if (!oldhashmask)
+ {
+ ss->stringspace = solv_extend_resize(ss->stringspace, ss->sstrings + len + 1, 1, STRINGSPACE_BLOCK);
+ ss->strings = solv_extend_resize(ss->strings, ss->nstrings + 1, sizeof(Offset), STRING_BLOCK);
+ }
+
+ /* generate next id and save in table */
+ id = ss->nstrings++;
+ hashtbl[h] = id;
+
+ ss->strings = solv_extend(ss->strings, id, 1, sizeof(Offset), STRING_BLOCK);
+ ss->strings[id] = ss->sstrings; /* we will append to the end */
+
+ /* append string to stringspace */
+ ss->stringspace = solv_extend(ss->stringspace, ss->sstrings, len + 1, 1, STRINGSPACE_BLOCK);
+ memcpy(ss->stringspace + ss->sstrings, str, len);
+ ss->stringspace[ss->sstrings + len] = 0;
+ ss->sstrings += len + 1;
+ return id;
+}
+
+Id
+stringpool_str2id(Stringpool *ss, const char *str, int create)
+{
+ if (!str)
+ return STRID_NULL;
+ if (!*str)
+ return STRID_EMPTY;
+ return stringpool_strn2id(ss, str, (unsigned int)strlen(str), create);
+}
+
+void
+stringpool_shrink(Stringpool *ss)
+{
+ ss->stringspace = solv_extend_resize(ss->stringspace, ss->sstrings, 1, STRINGSPACE_BLOCK);
+ ss->strings = solv_extend_resize(ss->strings, ss->nstrings, sizeof(Offset), STRING_BLOCK);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+#ifndef LIBSOLV_STRINGPOOL_H
+#define LIBSOLV_STRINGPOOL_H
+
+#include "pooltypes.h"
+#include "hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define STRID_NULL 0
+#define STRID_EMPTY 1
+
+struct _Stringpool
+{
+ Offset *strings; /* table of offsets into stringspace, indexed by Id: Id -> Offset */
+ int nstrings; /* number of ids in strings table */
+ char *stringspace; /* space for all unique strings: stringspace + Offset = string */
+ Offset sstrings; /* size of used stringspace */
+
+ Hashtable stringhashtbl; /* hash table: (string ->) Hash -> Id */
+ Hashval stringhashmask; /* modulo value for hash table (size of table - 1) */
+};
+
+void stringpool_init(Stringpool *ss, const char *strs[]);
+void stringpool_init_empty(Stringpool *ss);
+void stringpool_clone(Stringpool *ss, Stringpool *from);
+void stringpool_free(Stringpool *ss);
+void stringpool_freehash(Stringpool *ss);
+
+Id stringpool_str2id(Stringpool *ss, const char *str, int create);
+Id stringpool_strn2id(Stringpool *ss, const char *str, unsigned int len, int create);
+
+void stringpool_shrink(Stringpool *ss);
+
+
+static inline const char *
+stringpool_id2str(Stringpool *ss, Id id)
+{
+ return ss->stringspace + ss->strings[id];
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007-2015, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * transaction.c
+ *
+ * Transaction handling
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "transaction.h"
+#include "solver.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "poolarch.h"
+#include "evr.h"
+#include "util.h"
+
+static int
+obsq_sortcmp(const void *ap, const void *bp, void *dp)
+{
+ Id a, b, oa, ob;
+ Pool *pool = dp;
+ Solvable *s, *oas, *obs;
+ int r;
+
+ a = ((Id *)ap)[0];
+ oa = ((Id *)ap)[1];
+ b = ((Id *)bp)[0];
+ ob = ((Id *)bp)[1];
+ if (a != b)
+ return a - b;
+ if (oa == ob)
+ return 0;
+ s = pool->solvables + a;
+ oas = pool->solvables + oa;
+ obs = pool->solvables + ob;
+ if (oas->name != obs->name)
+ {
+ /* bring "same name" obsoleters (i.e. upgraders) to front */
+ if (oas->name == s->name)
+ return -1;
+ if (obs->name == s->name)
+ return 1;
+ return strcmp(pool_id2str(pool, oas->name), pool_id2str(pool, obs->name));
+ }
+ r = pool_evrcmp(pool, oas->evr, obs->evr, EVRCMP_COMPARE);
+ if (r)
+ return -r; /* highest version first */
+ return oa - ob;
+}
+
+void
+transaction_all_obs_pkgs(Transaction *trans, Id p, Queue *pkgs)
+{
+ Pool *pool = trans->pool;
+ Solvable *s = pool->solvables + p;
+ Queue *ti = &trans->transaction_info;
+ Id q;
+ int i;
+
+ queue_empty(pkgs);
+ if (p <= 0 || !s->repo)
+ return;
+ if (s->repo == pool->installed)
+ {
+ q = trans->transaction_installed[p - pool->installed->start];
+ if (!q)
+ return;
+ if (q > 0)
+ {
+ /* only a single obsoleting package */
+ queue_push(pkgs, q);
+ return;
+ }
+ /* find which packages obsolete us */
+ for (i = 0; i < ti->count; i += 2)
+ if (ti->elements[i + 1] == p)
+ queue_push2(pkgs, p, ti->elements[i]);
+ /* sort obsoleters */
+ if (pkgs->count > 2)
+ solv_sort(pkgs->elements, pkgs->count / 2, 2 * sizeof(Id), obsq_sortcmp, pool);
+ for (i = 0; i < pkgs->count; i += 2)
+ pkgs->elements[i / 2] = pkgs->elements[i + 1];
+ queue_truncate(pkgs, pkgs->count / 2);
+ }
+ else
+ {
+ /* find the packages we obsolete */
+ for (i = 0; i < ti->count; i += 2)
+ {
+ if (ti->elements[i] == p)
+ queue_push(pkgs, ti->elements[i + 1]);
+ else if (pkgs->count)
+ break;
+ }
+ }
+}
+
+Id
+transaction_obs_pkg(Transaction *trans, Id p)
+{
+ Pool *pool = trans->pool;
+ Solvable *s = pool->solvables + p;
+ Queue *ti;
+ int i;
+
+ if (p <= 0 || !s->repo)
+ return 0;
+ if (s->repo == pool->installed)
+ {
+ p = trans->transaction_installed[p - pool->installed->start];
+ return p < 0 ? -p : p;
+ }
+ ti = &trans->transaction_info;
+ for (i = 0; i < ti->count; i += 2)
+ if (ti->elements[i] == p)
+ return ti->elements[i + 1];
+ return 0;
+}
+
+
+/*
+ * calculate base type of transaction element
+ */
+
+static Id
+transaction_base_type(Transaction *trans, Id p)
+{
+ Pool *pool = trans->pool;
+ Solvable *s, *s2;
+ int r;
+ Id p2;
+
+ if (!MAPTST(&trans->transactsmap, p))
+ return SOLVER_TRANSACTION_IGNORE;
+ p2 = transaction_obs_pkg(trans, p);
+ if (pool->installed && pool->solvables[p].repo == pool->installed)
+ {
+ /* erase */
+ if (!p2)
+ return SOLVER_TRANSACTION_ERASE;
+ s = pool->solvables + p;
+ s2 = pool->solvables + p2;
+ if (s->name == s2->name)
+ {
+ if (s->evr == s2->evr && solvable_identical(s, s2))
+ return SOLVER_TRANSACTION_REINSTALLED;
+ r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
+ if (r < 0)
+ return SOLVER_TRANSACTION_UPGRADED;
+ else if (r > 0)
+ return SOLVER_TRANSACTION_DOWNGRADED;
+ return SOLVER_TRANSACTION_CHANGED;
+ }
+ return SOLVER_TRANSACTION_OBSOLETED;
+ }
+ else
+ {
+ /* install or multiinstall */
+ int multi = trans->multiversionmap.size && MAPTST(&trans->multiversionmap, p);
+ if (multi)
+ {
+ if (p2)
+ {
+ s = pool->solvables + p;
+ s2 = pool->solvables + p2;
+ if (s->name == s2->name && s->arch == s2->arch && s->evr == s2->evr)
+ return SOLVER_TRANSACTION_MULTIREINSTALL;
+ }
+ return SOLVER_TRANSACTION_MULTIINSTALL;
+ }
+ if (!p2)
+ return SOLVER_TRANSACTION_INSTALL;
+ s = pool->solvables + p;
+ s2 = pool->solvables + p2;
+ if (s->name == s2->name)
+ {
+ if (s->evr == s2->evr && solvable_identical(s, s2))
+ return SOLVER_TRANSACTION_REINSTALL;
+ r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
+ if (r > 0)
+ return SOLVER_TRANSACTION_UPGRADE;
+ else if (r < 0)
+ return SOLVER_TRANSACTION_DOWNGRADE;
+ else
+ return SOLVER_TRANSACTION_CHANGE;
+ }
+ return SOLVER_TRANSACTION_OBSOLETES;
+ }
+}
+
+/* these packages do not get installed by the package manager */
+static inline int
+is_pseudo_package(Pool *pool, Solvable *s)
+{
+ const char *n = pool_id2str(pool, s->name);
+ if (*n == 'p' && !strncmp(n, "patch:", 6))
+ return 1;
+ if (*n == 'p' && !strncmp(n, "pattern:", 8))
+ return 1;
+ if (*n == 'p' && !strncmp(n, "product:", 8))
+ return 1;
+ if (*n == 'a' && !strncmp(n, "application:", 12))
+ return 1;
+ return 0;
+}
+
+/* these packages will never show up installed */
+static inline int
+is_noinst_pseudo_package(Pool *pool, Solvable *s)
+{
+ const char *n = pool_id2str(pool, s->name);
+ if (!strncmp(n, "patch:", 6))
+ return 1;
+ if (!strncmp(n, "pattern:", 8))
+ {
+#if defined(SUSE) && defined(ENABLE_LINKED_PKGS)
+ /* unlike normal patterns, autopatterns *can* be installed (via the package link),
+ so do not filter them */
+ if (s->provides)
+ {
+ Id prv, *prvp = s->repo->idarraydata + s->provides;
+ while ((prv = *prvp++) != 0)
+ if (ISRELDEP(prv) && !strcmp(pool_id2str(pool, prv), "autopattern()"))
+ return 0;
+ }
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+static int
+obsoleted_by_pseudos_only(Transaction *trans, Id p)
+{
+ Pool *pool = trans->pool;
+ Queue q;
+ Id op;
+ int i;
+
+ op = transaction_obs_pkg(trans, p);
+ if (op && !is_pseudo_package(pool, pool->solvables + op))
+ return 0;
+ queue_init(&q);
+ transaction_all_obs_pkgs(trans, p, &q);
+ for (i = 0; i < q.count; i++)
+ if (!is_pseudo_package(pool, pool->solvables + q.elements[i]))
+ break;
+ i = !q.count || i < q.count ? 0 : 1;
+ queue_free(&q);
+ return i;
+}
+
+/*
+ * return type of transaction element
+ *
+ * filtering is needed if either not all packages are shown
+ * or replaces are not shown, as otherwise parts of the
+ * transaction might not be shown to the user */
+
+Id
+transaction_type(Transaction *trans, Id p, int mode)
+{
+ Pool *pool = trans->pool;
+ Solvable *s = pool->solvables + p;
+ Queue oq, rq;
+ Id type, q;
+ int i, j, ref = 0;
+
+ if (!s->repo)
+ return SOLVER_TRANSACTION_IGNORE;
+
+ /* XXX: SUSE only? */
+ if (!(mode & SOLVER_TRANSACTION_KEEP_PSEUDO) && is_noinst_pseudo_package(pool, s))
+ return SOLVER_TRANSACTION_IGNORE;
+
+ type = transaction_base_type(trans, p);
+
+ if (type == SOLVER_TRANSACTION_IGNORE)
+ return SOLVER_TRANSACTION_IGNORE; /* not part of the transaction */
+
+ if ((mode & SOLVER_TRANSACTION_RPM_ONLY) != 0)
+ {
+ /* application wants to know what to feed to the package manager */
+ if (!(mode & SOLVER_TRANSACTION_KEEP_PSEUDO) && is_pseudo_package(pool, s))
+ return SOLVER_TRANSACTION_IGNORE;
+ if (type == SOLVER_TRANSACTION_ERASE || type == SOLVER_TRANSACTION_INSTALL || type == SOLVER_TRANSACTION_MULTIINSTALL)
+ return type;
+ if (s->repo == pool->installed)
+ {
+ /* check if we're a real package that is obsoleted by pseudos */
+ if (!is_pseudo_package(pool, s) && obsoleted_by_pseudos_only(trans, s - pool->solvables))
+ return SOLVER_TRANSACTION_ERASE;
+ return SOLVER_TRANSACTION_IGNORE; /* ignore as we're being obsoleted */
+ }
+ if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
+ return SOLVER_TRANSACTION_MULTIINSTALL;
+ return SOLVER_TRANSACTION_INSTALL;
+ }
+
+ if ((mode & SOLVER_TRANSACTION_SHOW_MULTIINSTALL) == 0)
+ {
+ /* application wants to make no difference between install
+ * and multiinstall */
+ if (type == SOLVER_TRANSACTION_MULTIINSTALL)
+ type = SOLVER_TRANSACTION_INSTALL;
+ if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
+ type = SOLVER_TRANSACTION_REINSTALL;
+ }
+
+ if ((mode & SOLVER_TRANSACTION_CHANGE_IS_REINSTALL) != 0)
+ {
+ /* application wants to make no difference between change
+ * and reinstall */
+ if (type == SOLVER_TRANSACTION_CHANGED)
+ type = SOLVER_TRANSACTION_REINSTALLED;
+ else if (type == SOLVER_TRANSACTION_CHANGE)
+ type = SOLVER_TRANSACTION_REINSTALL;
+ }
+
+ if (type == SOLVER_TRANSACTION_ERASE || type == SOLVER_TRANSACTION_INSTALL || type == SOLVER_TRANSACTION_MULTIINSTALL)
+ return type;
+
+ if (s->repo == pool->installed && (mode & SOLVER_TRANSACTION_SHOW_ACTIVE) == 0)
+ {
+ /* erase element and we're showing the passive side */
+ if (type == SOLVER_TRANSACTION_OBSOLETED && (mode & SOLVER_TRANSACTION_SHOW_OBSOLETES) == 0)
+ type = SOLVER_TRANSACTION_ERASE;
+ if (type == SOLVER_TRANSACTION_OBSOLETED && (mode & SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE) != 0)
+ type = SOLVER_TRANSACTION_UPGRADED;
+ return type;
+ }
+ if (s->repo != pool->installed && (mode & SOLVER_TRANSACTION_SHOW_ACTIVE) != 0)
+ {
+ /* install element and we're showing the active side */
+ if (type == SOLVER_TRANSACTION_OBSOLETES && (mode & SOLVER_TRANSACTION_SHOW_OBSOLETES) == 0)
+ type = SOLVER_TRANSACTION_INSTALL;
+ if (type == SOLVER_TRANSACTION_OBSOLETES && (mode & SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE) != 0)
+ type = SOLVER_TRANSACTION_UPGRADE;
+ return type;
+ }
+
+ /* the element doesn't match the show mode */
+
+ /* if we're showing all references, we can ignore this package */
+ if ((mode & (SOLVER_TRANSACTION_SHOW_ALL|SOLVER_TRANSACTION_SHOW_OBSOLETES)) == (SOLVER_TRANSACTION_SHOW_ALL|SOLVER_TRANSACTION_SHOW_OBSOLETES))
+ return SOLVER_TRANSACTION_IGNORE;
+
+ /* we're not showing all refs. check if some other package
+ * references us. If yes, it's safe to ignore this package,
+ * otherwise we need to map the type */
+
+ /* most of the time there's only one reference, so check it first */
+ q = transaction_obs_pkg(trans, p);
+
+ if ((mode & SOLVER_TRANSACTION_SHOW_OBSOLETES) == 0)
+ {
+ Solvable *sq = pool->solvables + q;
+ if (sq->name != s->name)
+ {
+ /* it's a replace but we're not showing replaces. map type. */
+ if (s->repo == pool->installed)
+ return SOLVER_TRANSACTION_ERASE;
+ else if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
+ return SOLVER_TRANSACTION_MULTIINSTALL;
+ else
+ return SOLVER_TRANSACTION_INSTALL;
+ }
+ }
+
+ /* if there's a match, p will be shown when q
+ * is processed */
+ if (transaction_obs_pkg(trans, q) == p)
+ return SOLVER_TRANSACTION_IGNORE;
+
+ /* too bad, a miss. check em all */
+ queue_init(&oq);
+ queue_init(&rq);
+ transaction_all_obs_pkgs(trans, p, &oq);
+ for (i = 0; i < oq.count; i++)
+ {
+ q = oq.elements[i];
+ if ((mode & SOLVER_TRANSACTION_SHOW_OBSOLETES) == 0)
+ {
+ Solvable *sq = pool->solvables + q;
+ if (sq->name != s->name)
+ continue;
+ }
+ /* check if we are referenced? */
+ if ((mode & SOLVER_TRANSACTION_SHOW_ALL) != 0)
+ {
+ transaction_all_obs_pkgs(trans, q, &rq);
+ for (j = 0; j < rq.count; j++)
+ if (rq.elements[j] == p)
+ {
+ ref = 1;
+ break;
+ }
+ if (ref)
+ break;
+ }
+ else if (transaction_obs_pkg(trans, q) == p)
+ {
+ ref = 1;
+ break;
+ }
+ }
+ queue_free(&oq);
+ queue_free(&rq);
+
+ if (!ref)
+ {
+ /* we're not referenced. map type */
+ if (s->repo == pool->installed)
+ return SOLVER_TRANSACTION_ERASE;
+ else if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
+ return SOLVER_TRANSACTION_MULTIINSTALL;
+ else
+ return SOLVER_TRANSACTION_INSTALL;
+ }
+ /* there was a ref, so p is shown with some other package */
+ return SOLVER_TRANSACTION_IGNORE;
+}
+
+
+
+static int
+classify_cmp(const void *ap, const void *bp, void *dp)
+{
+ Transaction *trans = dp;
+ Pool *pool = trans->pool;
+ const Id *a = ap;
+ const Id *b = bp;
+ int r;
+
+ r = a[0] - b[0];
+ if (r)
+ return r;
+ r = a[2] - b[2];
+ if (r)
+ return a[2] && b[2] ? strcmp(pool_id2str(pool, a[2]), pool_id2str(pool, b[2])) : r;
+ r = a[3] - b[3];
+ if (r)
+ return a[3] && b[3] ? strcmp(pool_id2str(pool, a[3]), pool_id2str(pool, b[3])) : r;
+ return 0;
+}
+
+static int
+classify_cmp_pkgs(const void *ap, const void *bp, void *dp)
+{
+ Transaction *trans = dp;
+ Pool *pool = trans->pool;
+ Id a = *(Id *)ap;
+ Id b = *(Id *)bp;
+ Solvable *sa, *sb;
+
+ sa = pool->solvables + a;
+ sb = pool->solvables + b;
+ if (sa->name != sb->name)
+ return strcmp(pool_id2str(pool, sa->name), pool_id2str(pool, sb->name));
+ if (sa->evr != sb->evr)
+ {
+ int r = pool_evrcmp(pool, sa->evr, sb->evr, EVRCMP_COMPARE);
+ if (r)
+ return r;
+ }
+ return a - b;
+}
+
+static inline void
+queue_push4(Queue *q, Id id1, Id id2, Id id3, Id id4)
+{
+ queue_push(q, id1);
+ queue_push(q, id2);
+ queue_push(q, id3);
+ queue_push(q, id4);
+}
+
+static inline void
+queue_unshift4(Queue *q, Id id1, Id id2, Id id3, Id id4)
+{
+ queue_unshift(q, id4);
+ queue_unshift(q, id3);
+ queue_unshift(q, id2);
+ queue_unshift(q, id1);
+}
+
+void
+transaction_classify(Transaction *trans, int mode, Queue *classes)
+{
+ Pool *pool = trans->pool;
+ int ntypes[SOLVER_TRANSACTION_MAXTYPE + 1];
+ Solvable *s, *sq;
+ Id v, vq, type, p, q;
+ int i, j;
+
+ queue_empty(classes);
+ memset(ntypes, 0, sizeof(ntypes));
+ /* go through transaction and classify each step */
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ p = trans->steps.elements[i];
+ s = pool->solvables + p;
+ type = transaction_type(trans, p, mode);
+ ntypes[type]++;
+ if (!pool->installed || s->repo != pool->installed)
+ continue;
+ /* don't report vendor/arch changes if we were mapped to erase. */
+ if (type == SOLVER_TRANSACTION_ERASE)
+ continue;
+ /* look at arch/vendor changes */
+ q = transaction_obs_pkg(trans, p);
+ if (!q)
+ continue;
+ sq = pool->solvables + q;
+
+ v = s->arch;
+ vq = sq->arch;
+ if (v != vq)
+ {
+ if ((mode & SOLVER_TRANSACTION_MERGE_ARCHCHANGES) != 0)
+ v = vq = 0;
+ for (j = 0; j < classes->count; j += 4)
+ if (classes->elements[j] == SOLVER_TRANSACTION_ARCHCHANGE && classes->elements[j + 2] == v && classes->elements[j + 3] == vq)
+ break;
+ if (j == classes->count)
+ queue_push4(classes, SOLVER_TRANSACTION_ARCHCHANGE, 1, v, vq);
+ else
+ classes->elements[j + 1]++;
+ }
+
+ v = s->vendor ? s->vendor : 1;
+ vq = sq->vendor ? sq->vendor : 1;
+ if (v != vq)
+ {
+ if ((mode & SOLVER_TRANSACTION_MERGE_VENDORCHANGES) != 0)
+ v = vq = 0;
+ for (j = 0; j < classes->count; j += 4)
+ if (classes->elements[j] == SOLVER_TRANSACTION_VENDORCHANGE && classes->elements[j + 2] == v && classes->elements[j + 3] == vq)
+ break;
+ if (j == classes->count)
+ queue_push4(classes, SOLVER_TRANSACTION_VENDORCHANGE, 1, v, vq);
+ else
+ classes->elements[j + 1]++;
+ }
+ }
+ /* now sort all vendor/arch changes */
+ if (classes->count > 4)
+ solv_sort(classes->elements, classes->count / 4, 4 * sizeof(Id), classify_cmp, trans);
+ /* finally add all classes. put erases last */
+ i = SOLVER_TRANSACTION_ERASE;
+ if (ntypes[i])
+ queue_unshift4(classes, i, ntypes[i], 0, 0);
+ for (i = SOLVER_TRANSACTION_MAXTYPE; i > 0; i--)
+ {
+ if (!ntypes[i])
+ continue;
+ if (i == SOLVER_TRANSACTION_ERASE)
+ continue;
+ queue_unshift4(classes, i, ntypes[i], 0, 0);
+ }
+}
+
+void
+transaction_classify_pkgs(Transaction *trans, int mode, Id class, Id from, Id to, Queue *pkgs)
+{
+ Pool *pool = trans->pool;
+ int i;
+ Id type, p, q;
+ Solvable *s, *sq;
+
+ queue_empty(pkgs);
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ p = trans->steps.elements[i];
+ s = pool->solvables + p;
+ if (class <= SOLVER_TRANSACTION_MAXTYPE)
+ {
+ type = transaction_type(trans, p, mode);
+ if (type == class)
+ queue_push(pkgs, p);
+ continue;
+ }
+ if (!pool->installed || s->repo != pool->installed)
+ continue;
+ q = transaction_obs_pkg(trans, p);
+ if (!q)
+ continue;
+ sq = pool->solvables + q;
+ if (class == SOLVER_TRANSACTION_ARCHCHANGE)
+ {
+ if ((!from && !to) || (s->arch == from && sq->arch == to))
+ queue_push(pkgs, p);
+ continue;
+ }
+ if (class == SOLVER_TRANSACTION_VENDORCHANGE)
+ {
+ Id v = s->vendor ? s->vendor : 1;
+ Id vq = sq->vendor ? sq->vendor : 1;
+ if ((!from && !to) || (v == from && vq == to))
+ queue_push(pkgs, p);
+ continue;
+ }
+ }
+ if (pkgs->count > 1)
+ solv_sort(pkgs->elements, pkgs->count, sizeof(Id), classify_cmp_pkgs, trans);
+}
+
+static void
+create_transaction_info(Transaction *trans, Queue *decisionq)
+{
+ Pool *pool = trans->pool;
+ Queue *ti = &trans->transaction_info;
+ Repo *installed = pool->installed;
+ int i, j, multi;
+ Id p, p2, pp2;
+ Solvable *s, *s2;
+
+ queue_empty(ti);
+ trans->transaction_installed = solv_free(trans->transaction_installed);
+ if (!installed)
+ return; /* no info needed */
+ for (i = 0; i < decisionq->count; i++)
+ {
+ p = decisionq->elements[i];
+ if (p <= 0 || p == SYSTEMSOLVABLE)
+ continue;
+ s = pool->solvables + p;
+ if (!s->repo || s->repo == installed)
+ continue;
+ multi = trans->multiversionmap.size && MAPTST(&trans->multiversionmap, p);
+ FOR_PROVIDES(p2, pp2, s->name)
+ {
+ if (!MAPTST(&trans->transactsmap, p2))
+ continue;
+ s2 = pool->solvables + p2;
+ if (s2->repo != installed)
+ continue;
+ if (multi && (s->name != s2->name || s->evr != s2->evr || s->arch != s2->arch))
+ continue;
+ if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
+ continue;
+ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
+ continue;
+ queue_push2(ti, p, p2);
+ }
+ if (s->obsoletes && !multi)
+ {
+ Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ {
+ FOR_PROVIDES(p2, pp2, obs)
+ {
+ if (!MAPTST(&trans->transactsmap, p2))
+ continue;
+ s2 = pool->solvables + p2;
+ if (s2->repo != installed)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, s2, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+ continue;
+ queue_push2(ti, p, p2);
+ }
+ }
+ }
+ }
+ if (ti->count > 2)
+ {
+ /* sort and unify */
+ solv_sort(ti->elements, ti->count / 2, 2 * sizeof(Id), obsq_sortcmp, pool);
+ for (i = j = 2; i < ti->count; i += 2)
+ {
+ if (ti->elements[i] == ti->elements[j - 2] && ti->elements[i + 1] == ti->elements[j - 1])
+ continue;
+ ti->elements[j++] = ti->elements[i];
+ ti->elements[j++] = ti->elements[i + 1];
+ }
+ queue_truncate(ti, j);
+ }
+
+ /* create transaction_installed helper */
+ /* entry > 0: exactly one obsoleter, entry < 0: multiple obsoleters, -entry is "best" */
+ trans->transaction_installed = solv_calloc(installed->end - installed->start, sizeof(Id));
+ for (i = 0; i < ti->count; i += 2)
+ {
+ j = ti->elements[i + 1] - installed->start;
+ if (!trans->transaction_installed[j])
+ trans->transaction_installed[j] = ti->elements[i];
+ else
+ {
+ /* more than one package obsoletes us. compare to find "best" */
+ Id q[4];
+ if (trans->transaction_installed[j] > 0)
+ trans->transaction_installed[j] = -trans->transaction_installed[j];
+ q[0] = q[2] = ti->elements[i + 1];
+ q[1] = ti->elements[i];
+ q[3] = -trans->transaction_installed[j];
+ if (obsq_sortcmp(q, q + 2, pool) < 0)
+ trans->transaction_installed[j] = -ti->elements[i];
+ }
+ }
+}
+
+/* create a transaction from the decisionq */
+Transaction *
+transaction_create_decisionq(Pool *pool, Queue *decisionq, Map *multiversionmap)
+{
+ Repo *installed = pool->installed;
+ int i, needmulti;
+ Id p;
+ Solvable *s;
+ Transaction *trans;
+
+ trans = transaction_create(pool);
+ if (multiversionmap && !multiversionmap->size)
+ multiversionmap = 0; /* ignore empty map */
+ queue_empty(&trans->steps);
+ map_init(&trans->transactsmap, pool->nsolvables);
+ needmulti = 0;
+ for (i = 0; i < decisionq->count; i++)
+ {
+ p = decisionq->elements[i];
+ s = pool->solvables + (p > 0 ? p : -p);
+ if (!s->repo)
+ continue;
+ if (installed && s->repo == installed && p < 0)
+ MAPSET(&trans->transactsmap, -p);
+ if (!(installed && s->repo == installed) && p > 0)
+ {
+ MAPSET(&trans->transactsmap, p);
+ if (multiversionmap && MAPTST(multiversionmap, p))
+ needmulti = 1;
+ }
+ }
+ MAPCLR(&trans->transactsmap, SYSTEMSOLVABLE);
+ if (needmulti)
+ map_init_clone(&trans->multiversionmap, multiversionmap);
+
+ create_transaction_info(trans, decisionq);
+
+ if (installed)
+ {
+ FOR_REPO_SOLVABLES(installed, p, s)
+ {
+ if (MAPTST(&trans->transactsmap, p))
+ queue_push(&trans->steps, p);
+ }
+ }
+ for (i = 0; i < decisionq->count; i++)
+ {
+ p = decisionq->elements[i];
+ if (p > 0 && MAPTST(&trans->transactsmap, p))
+ queue_push(&trans->steps, p);
+ }
+ return trans;
+}
+
+int
+transaction_installedresult(Transaction *trans, Queue *installedq)
+{
+ Pool *pool = trans->pool;
+ Repo *installed = pool->installed;
+ Solvable *s;
+ int i, cutoff;
+ Id p;
+
+ queue_empty(installedq);
+ /* first the new installs, than the kept packages */
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ p = trans->steps.elements[i];
+ s = pool->solvables + p;
+ if (installed && s->repo == installed)
+ continue;
+ queue_push(installedq, p);
+ }
+ cutoff = installedq->count;
+ if (installed)
+ {
+ FOR_REPO_SOLVABLES(installed, p, s)
+ if (!MAPTST(&trans->transactsmap, p))
+ queue_push(installedq, p);
+ }
+ return cutoff;
+}
+
+static void
+transaction_make_installedmap(Transaction *trans, Map *installedmap)
+{
+ Pool *pool = trans->pool;
+ Repo *installed = pool->installed;
+ Solvable *s;
+ Id p;
+ int i;
+
+ map_init(installedmap, pool->nsolvables);
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ p = trans->steps.elements[i];
+ s = pool->solvables + p;
+ if (!installed || s->repo != installed)
+ MAPSET(installedmap, p);
+ }
+ if (installed)
+ {
+ FOR_REPO_SOLVABLES(installed, p, s)
+ if (!MAPTST(&trans->transactsmap, p))
+ MAPSET(installedmap, p);
+ }
+}
+
+int
+transaction_calc_installsizechange(Transaction *trans)
+{
+ Map installedmap;
+ int change;
+
+ transaction_make_installedmap(trans, &installedmap);
+ change = pool_calc_installsizechange(trans->pool, &installedmap);
+ map_free(&installedmap);
+ return change;
+}
+
+void
+transaction_calc_duchanges(Transaction *trans, DUChanges *mps, int nmps)
+{
+ Map installedmap;
+
+ transaction_make_installedmap(trans, &installedmap);
+ pool_calc_duchanges(trans->pool, &installedmap, mps, nmps);
+ map_free(&installedmap);
+}
+
+Transaction *
+transaction_create(Pool *pool)
+{
+ Transaction *trans = solv_calloc(1, sizeof(*trans));
+ trans->pool = pool;
+ return trans;
+}
+
+Transaction *
+transaction_create_clone(Transaction *srctrans)
+{
+ Transaction *trans = transaction_create(srctrans->pool);
+ queue_init_clone(&trans->steps, &srctrans->steps);
+ queue_init_clone(&trans->transaction_info, &srctrans->transaction_info);
+ if (srctrans->transaction_installed)
+ {
+ Repo *installed = srctrans->pool->installed;
+ trans->transaction_installed = solv_memdup2(srctrans->transaction_installed, installed->end - installed->start, sizeof(Id));
+ }
+ map_init_clone(&trans->transactsmap, &srctrans->transactsmap);
+ map_init_clone(&trans->multiversionmap, &srctrans->multiversionmap);
+ if (srctrans->orderdata)
+ transaction_clone_orderdata(trans, srctrans);
+ return trans;
+}
+
+void
+transaction_free(Transaction *trans)
+{
+ queue_free(&trans->steps);
+ queue_free(&trans->transaction_info);
+ trans->transaction_installed = solv_free(trans->transaction_installed);
+ map_free(&trans->transactsmap);
+ map_free(&trans->multiversionmap);
+ if (trans->orderdata)
+ transaction_free_orderdata(trans);
+ free(trans);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * transaction.h
+ *
+ */
+
+#ifndef LIBSOLV_TRANSACTION_H
+#define LIBSOLV_TRANSACTION_H
+
+#include "pooltypes.h"
+#include "queue.h"
+#include "bitmap.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _Pool;
+struct _DUChanges;
+struct _TransactionOrderdata;
+
+typedef struct _Transaction {
+ struct _Pool *pool; /* back pointer to pool */
+
+ Queue steps; /* the transaction steps */
+
+#ifdef LIBSOLV_INTERNAL
+ Queue transaction_info;
+ Id *transaction_installed;
+ Map transactsmap;
+ Map multiversionmap;
+
+ struct _TransactionOrderdata *orderdata;
+#endif
+
+} Transaction;
+
+
+/* step types */
+#define SOLVER_TRANSACTION_IGNORE 0x00
+
+#define SOLVER_TRANSACTION_ERASE 0x10
+#define SOLVER_TRANSACTION_REINSTALLED 0x11
+#define SOLVER_TRANSACTION_DOWNGRADED 0x12
+#define SOLVER_TRANSACTION_CHANGED 0x13
+#define SOLVER_TRANSACTION_UPGRADED 0x14
+#define SOLVER_TRANSACTION_OBSOLETED 0x15
+
+#define SOLVER_TRANSACTION_INSTALL 0x20
+#define SOLVER_TRANSACTION_REINSTALL 0x21
+#define SOLVER_TRANSACTION_DOWNGRADE 0x22
+#define SOLVER_TRANSACTION_CHANGE 0x23
+#define SOLVER_TRANSACTION_UPGRADE 0x24
+#define SOLVER_TRANSACTION_OBSOLETES 0x25
+
+#define SOLVER_TRANSACTION_MULTIINSTALL 0x30
+#define SOLVER_TRANSACTION_MULTIREINSTALL 0x31
+
+#define SOLVER_TRANSACTION_MAXTYPE 0x3f
+
+/* modes */
+#define SOLVER_TRANSACTION_SHOW_ACTIVE (1 << 0)
+#define SOLVER_TRANSACTION_SHOW_ALL (1 << 1)
+#define SOLVER_TRANSACTION_SHOW_OBSOLETES (1 << 2)
+#define SOLVER_TRANSACTION_SHOW_MULTIINSTALL (1 << 3)
+#define SOLVER_TRANSACTION_CHANGE_IS_REINSTALL (1 << 4)
+#define SOLVER_TRANSACTION_MERGE_VENDORCHANGES (1 << 5)
+#define SOLVER_TRANSACTION_MERGE_ARCHCHANGES (1 << 6)
+
+#define SOLVER_TRANSACTION_RPM_ONLY (1 << 7)
+
+#define SOLVER_TRANSACTION_KEEP_PSEUDO (1 << 8)
+
+#define SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE (1 << 9)
+
+/* extra classifications */
+#define SOLVER_TRANSACTION_ARCHCHANGE 0x100
+#define SOLVER_TRANSACTION_VENDORCHANGE 0x101
+
+/* order flags */
+#define SOLVER_TRANSACTION_KEEP_ORDERDATA (1 << 0)
+#define SOLVER_TRANSACTION_KEEP_ORDERCYCLES (1 << 1)
+
+/* cycle severities */
+#define SOLVER_ORDERCYCLE_HARMLESS 0
+#define SOLVER_ORDERCYCLE_NORMAL 1
+#define SOLVER_ORDERCYCLE_CRITICAL 2
+
+extern Transaction *transaction_create(struct _Pool *pool);
+extern Transaction *transaction_create_decisionq(struct _Pool *pool, Queue *decisionq, Map *multiversionmap);
+extern Transaction *transaction_create_clone(Transaction *srctrans);
+extern void transaction_free(Transaction *trans);
+
+/* if p is installed, returns with pkg(s) obsolete p */
+/* if p is not installed, returns with pkg(s) we obsolete */
+extern Id transaction_obs_pkg(Transaction *trans, Id p);
+extern void transaction_all_obs_pkgs(Transaction *trans, Id p, Queue *pkgs);
+
+/* return step type of a transaction element */
+extern Id transaction_type(Transaction *trans, Id p, int mode);
+
+/* return sorted collection of all step types */
+/* classify_pkgs can be used to return all packages of a type */
+extern void transaction_classify(Transaction *trans, int mode, Queue *classes);
+extern void transaction_classify_pkgs(Transaction *trans, int mode, Id type, Id from, Id to, Queue *pkgs);
+
+/* return all packages that will be installed after the transaction is run*/
+/* The new packages are put at the head of the queue, the number of new
+ packages is returned */
+extern int transaction_installedresult(Transaction *trans, Queue *installedq);
+
+int transaction_calc_installsizechange(Transaction *trans);
+void transaction_calc_duchanges(Transaction *trans, struct _DUChanges *mps, int nmps);
+
+
+
+/* order a transaction */
+extern void transaction_order(Transaction *trans, int flags);
+
+/* roll your own order funcion:
+ * add pkgs free for installation to queue choices after chosen was
+ * installed. start with chosen = 0
+ * needs an ordered transaction created with SOLVER_TRANSACTION_KEEP_ORDERDATA */
+extern int transaction_order_add_choices(Transaction *trans, Id chosen, Queue *choices);
+/* add obsoleted packages into transaction steps */
+extern void transaction_add_obsoleted(Transaction *trans);
+
+/* debug function, report problems found in the order */
+extern void transaction_check_order(Transaction *trans);
+
+/* order cycle introspection */
+extern void transaction_order_get_cycleids(Transaction *trans, Queue *q, int minseverity);
+extern int transaction_order_get_cycle(Transaction *trans, Id cid, Queue *q);
+
+extern void transaction_free_orderdata(Transaction *trans);
+extern void transaction_clone_orderdata(Transaction *trans, Transaction *srctrans);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "util.h"
+
+void
+solv_oom(size_t num, size_t len)
+{
+ if (num)
+ fprintf(stderr, "Out of memory allocating %zu*%zu bytes!\n", num, len);
+ else
+ fprintf(stderr, "Out of memory allocating %zu bytes!\n", len);
+ abort();
+ exit(1);
+}
+
+void *
+solv_malloc(size_t len)
+{
+ void *r = malloc(len ? len : 1);
+ if (!r)
+ solv_oom(0, len);
+ return r;
+}
+
+void *
+solv_malloc2(size_t num, size_t len)
+{
+ if (len && (num * len) / len != num)
+ solv_oom(num, len);
+ return solv_malloc(num * len);
+}
+
+void *
+solv_realloc(void *old, size_t len)
+{
+ if (old == 0)
+ old = malloc(len ? len : 1);
+ else
+ old = realloc(old, len ? len : 1);
+ if (!old)
+ solv_oom(0, len);
+ return old;
+}
+
+void *
+solv_realloc2(void *old, size_t num, size_t len)
+{
+ if (len && (num * len) / len != num)
+ solv_oom(num, len);
+ return solv_realloc(old, num * len);
+}
+
+void *
+solv_calloc(size_t num, size_t len)
+{
+ void *r;
+ if (num == 0 || len == 0)
+ r = malloc(1);
+ else
+ r = calloc(num, len);
+ if (!r)
+ solv_oom(num, len);
+ return r;
+}
+
+/* this was solv_realloc2(old, len, size), but we now overshoot
+ * for huge len sizes */
+void *
+solv_extend_realloc(void *old, size_t len, size_t size, size_t block)
+{
+ size_t xblock = (block + 1) << 5;
+ len = (len + block) & ~block;
+ if (len >= xblock && xblock)
+ {
+ xblock <<= 1;
+ while (len >= xblock && xblock)
+ xblock <<= 1;
+ if (xblock)
+ {
+ size_t nlen;
+ xblock = (xblock >> 5) - 1;
+ nlen = (len + xblock) & ~xblock;
+ if (nlen > len)
+ len = nlen;
+ }
+ }
+ return solv_realloc2(old, len, size);
+}
+
+void *
+solv_free(void *mem)
+{
+ if (mem)
+ free(mem);
+ return 0;
+}
+
+char *
+solv_strdup(const char *s)
+{
+ char *r;
+ if (!s)
+ return 0;
+ r = strdup(s);
+ if (!r)
+ solv_oom(0, strlen(s));
+ return r;
+}
+
+unsigned int
+solv_timems(unsigned int subtract)
+{
+ struct timeval tv;
+ unsigned int r;
+
+ if (gettimeofday(&tv, 0))
+ return 0;
+ r = (((unsigned int)tv.tv_sec >> 16) * 1000) << 16;
+ r += ((unsigned int)tv.tv_sec & 0xffff) * 1000;
+ r += (unsigned int)tv.tv_usec / 1000;
+ return r - subtract;
+}
+
+/* bsd's qsort_r has different arguments, so we define our
+ own version in case we need to do some clever mapping
+
+ see also: http://sources.redhat.com/ml/libc-alpha/2008-12/msg00003.html
+ */
+#if defined(__GLIBC__) && (defined(HAVE_QSORT_R) || defined(HAVE___QSORT_R))
+
+void
+solv_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *compard)
+{
+# if defined(HAVE_QSORT_R)
+ qsort_r(base, nmemb, size, compar, compard);
+# else
+ /* backported for SLE10-SP2 */
+ __qsort_r(base, nmemb, size, compar, compard);
+# endif
+
+}
+
+#elif defined(HAVE_QSORT_R) /* not glibc, but has qsort_r() */
+
+struct solv_sort_data {
+ int (*compar)(const void *, const void *, void *);
+ void *compard;
+};
+
+static int
+solv_sort_helper(void *compard, const void *a, const void *b)
+{
+ struct solv_sort_data *d = compard;
+ return (*d->compar)(a, b, d->compard);
+}
+
+void
+solv_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *compard)
+{
+ struct solv_sort_data d;
+ d.compar = compar;
+ d.compard = compard;
+ qsort_r(base, nmemb, size, &d, solv_sort_helper);
+}
+
+#else /* not glibc and no qsort_r() */
+/* use own version of qsort if none available */
+#include "qsort_r.c"
+#endif
+
+char *
+solv_dupjoin(const char *str1, const char *str2, const char *str3)
+{
+ int l1, l2, l3;
+ char *s, *str;
+ l1 = str1 ? strlen(str1) : 0;
+ l2 = str2 ? strlen(str2) : 0;
+ l3 = str3 ? strlen(str3) : 0;
+ s = str = solv_malloc(l1 + l2 + l3 + 1);
+ if (l1)
+ {
+ strcpy(s, str1);
+ s += l1;
+ }
+ if (l2)
+ {
+ strcpy(s, str2);
+ s += l2;
+ }
+ if (l3)
+ {
+ strcpy(s, str3);
+ s += l3;
+ }
+ *s = 0;
+ return str;
+}
+
+char *
+solv_dupappend(const char *str1, const char *str2, const char *str3)
+{
+ char *str = solv_dupjoin(str1, str2, str3);
+ solv_free((void *)str1);
+ return str;
+}
+
+int
+solv_hex2bin(const char **strp, unsigned char *buf, int bufl)
+{
+ const char *str = *strp;
+ int i;
+
+ for (i = 0; i < bufl; i++)
+ {
+ int c = *str;
+ int d;
+ if (c >= '0' && c <= '9')
+ d = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ d = c - ('a' - 10);
+ else if (c >= 'A' && c <= 'F')
+ d = c - ('A' - 10);
+ else
+ break;
+ c = *++str;
+ d <<= 4;
+ if (c >= '0' && c <= '9')
+ d |= c - '0';
+ else if (c >= 'a' && c <= 'f')
+ d |= c - ('a' - 10);
+ else if (c >= 'A' && c <= 'F')
+ d |= c - ('A' - 10);
+ else
+ break;
+ buf[i] = d;
+ ++str;
+ }
+ *strp = str;
+ return i;
+}
+
+char *
+solv_bin2hex(const unsigned char *buf, int l, char *str)
+{
+ int i;
+ for (i = 0; i < l; i++, buf++)
+ {
+ int c = *buf >> 4;
+ *str++ = c < 10 ? c + '0' : c + ('a' - 10);
+ c = *buf & 15;
+ *str++ = c < 10 ? c + '0' : c + ('a' - 10);
+ }
+ *str = 0;
+ return str;
+}
+
+size_t
+solv_validutf8(const char *buf)
+{
+ const unsigned char *p;
+ int x;
+
+ for (p = (const unsigned char *)buf; (x = *p) != 0; p++)
+ {
+ if (x < 0x80)
+ continue;
+ if (x < 0xc0)
+ break;
+ if (x < 0xe0)
+ {
+ /* one byte to follow */
+ if ((p[1] & 0xc0) != 0x80)
+ break;
+ if ((x & 0x1e) == 0)
+ break; /* not minimal */
+ p += 1;
+ continue;
+ }
+ if (x < 0xf0)
+ {
+ /* two bytes to follow */
+ if ((p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80)
+ break;
+ if ((x & 0x0f) == 0 && (p[1] & 0x20) == 0)
+ break; /* not minimal */
+ if (x == 0xed && (p[1] & 0x20) != 0)
+ break; /* d800-dfff surrogate */
+ if (x == 0xef && p[1] == 0xbf && (p[2] == 0xbe || p[2] == 0xbf))
+ break; /* fffe or ffff */
+ p += 2;
+ continue;
+ }
+ if (x < 0xf8)
+ {
+ /* three bytes to follow */
+ if ((p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80)
+ break;
+ if ((x & 0x07) == 0 && (p[1] & 0x30) == 0)
+ break; /* not minimal */
+ if ((x & 0x07) > 4 || ((x & 0x07) == 4 && (p[1] & 0x30) != 0))
+ break; /* above 0x10ffff */
+ p += 3;
+ continue;
+ }
+ break; /* maybe valid utf8, but above 0x10ffff */
+ }
+ return (const char *)p - buf;
+}
+
+char *
+solv_latin1toutf8(const char *buf)
+{
+ int l = 1;
+ const char *p;
+ char *r, *rp;
+
+ for (p = buf; *p; p++)
+ if ((*(const unsigned char *)p & 128) != 0)
+ l++;
+ r = rp = solv_malloc(p - buf + l);
+ for (p = buf; *p; p++)
+ {
+ if ((*(const unsigned char *)p & 128) != 0)
+ {
+ *rp++ = *(const unsigned char *)p & 64 ? 0xc3 : 0xc2;
+ *rp++ = *p & 0xbf;
+ }
+ else
+ *rp++ = *p;
+ }
+ *rp = 0;
+ return r;
+}
+
+char *
+solv_replacebadutf8(const char *buf, int replchar)
+{
+ size_t l, nl;
+ const char *p;
+ char *r = 0, *rp = 0;
+ int repllen, replin;
+
+ if (replchar < 0 || replchar > 0x10ffff)
+ replchar = 0xfffd;
+ if (!replchar)
+ repllen = replin = 0;
+ else if (replchar < 0x80)
+ {
+ repllen = 1;
+ replin = (replchar & 0x40) | 0x80;
+ }
+ else if (replchar < 0x800)
+ {
+ repllen = 2;
+ replin = 0x40;
+ }
+ else if (replchar < 0x10000)
+ {
+ repllen = 3;
+ replin = 0x60;
+ }
+ else
+ {
+ repllen = 4;
+ replin = 0x70;
+ }
+ for (;;)
+ {
+ for (p = buf, nl = 0; *p; )
+ {
+ l = solv_validutf8(p);
+ if (rp && l)
+ {
+ memcpy(rp, p, l);
+ rp += l;
+ }
+ nl += l;
+ p += l;
+ if (!*p)
+ break;
+ /* found a bad char, replace with replchar */
+ if (rp && replchar)
+ {
+ switch (repllen)
+ {
+ case 4:
+ *rp++ = (replchar >> 18 & 0x3f) | 0x80;
+ case 3:
+ *rp++ = (replchar >> 12 & 0x3f) | 0x80;
+ case 2:
+ *rp++ = (replchar >> 6 & 0x3f) | 0x80;
+ default:
+ *rp++ = (replchar & 0x3f) | 0x80;
+ }
+ rp[-repllen] ^= replin;
+ }
+ nl += repllen;
+ p++;
+ while ((*(const unsigned char *)p & 0xc0) == 0x80)
+ p++;
+ }
+ if (rp)
+ break;
+ r = rp = solv_malloc(nl + 1);
+ }
+ *rp = 0;
+ return r;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * util.h
+ *
+ */
+
+#ifndef LIBSOLV_UTIL_H
+#define LIBSOLV_UTIL_H
+
+#include <stddef.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * malloc
+ * exits with error message on error
+ */
+extern void *solv_malloc(size_t);
+extern void *solv_malloc2(size_t, size_t);
+extern void *solv_calloc(size_t, size_t);
+extern void *solv_realloc(void *, size_t);
+extern void *solv_realloc2(void *, size_t, size_t);
+extern void *solv_extend_realloc(void *, size_t, size_t, size_t);
+extern void *solv_free(void *);
+extern char *solv_strdup(const char *);
+extern void solv_oom(size_t, size_t);
+extern unsigned int solv_timems(unsigned int subtract);
+extern void solv_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *compard);
+extern char *solv_dupjoin(const char *str1, const char *str2, const char *str3);
+extern char *solv_dupappend(const char *str1, const char *str2, const char *str3);
+extern int solv_hex2bin(const char **strp, unsigned char *buf, int bufl);
+extern char *solv_bin2hex(const unsigned char *buf, int l, char *str);
+extern size_t solv_validutf8(const char *buf);
+extern char *solv_latin1toutf8(const char *buf);
+extern char *solv_replacebadutf8(const char *buf, int replchar);
+
+
+static inline void *solv_extend(void *buf, size_t len, size_t nmemb, size_t size, size_t block)
+{
+ if (nmemb == 1)
+ {
+ if ((len & block) == 0)
+ buf = solv_extend_realloc(buf, len + 1, size, block);
+ }
+ else
+ {
+ if (((len - 1) | block) != ((len + nmemb - 1) | block))
+ buf = solv_extend_realloc(buf, len + nmemb, size, block);
+ }
+ return buf;
+}
+
+/**
+ * extend an array by reallocation and zero's the new section
+ * buf old pointer
+ * len current size
+ * nmbemb number of elements to add
+ * size size of each element
+ * block block size used to allocate the elements
+ */
+static inline void *solv_zextend(void *buf, size_t len, size_t nmemb, size_t size, size_t block)
+{
+ buf = solv_extend(buf, len, nmemb, size, block);
+ memset((char *)buf + len * size, 0, nmemb * size);
+ return buf;
+}
+
+static inline void *solv_extend_resize(void *buf, size_t len, size_t size, size_t block)
+{
+ if (len)
+ buf = solv_extend_realloc(buf, len, size, block);
+ return buf;
+}
+
+static inline void *solv_calloc_block(size_t len, size_t size, size_t block)
+{
+ void *buf;
+ if (!len)
+ return 0;
+ buf = solv_extend_realloc((void *)0, len, size, block);
+ memset(buf, 0, ((len + block) & ~block) * size);
+ return buf;
+}
+
+static inline void *solv_memdup(void *buf, size_t len)
+{
+ void *newbuf;
+ if (!buf)
+ return 0;
+ newbuf = solv_malloc(len);
+ if (len)
+ memcpy(newbuf, buf, len);
+ return newbuf;
+}
+
+static inline void *solv_memdup2(void *buf, size_t num, size_t len)
+{
+ void *newbuf;
+ if (!buf)
+ return 0;
+ newbuf = solv_malloc2(num, len);
+ if (num)
+ memcpy(newbuf, buf, num * len);
+ return newbuf;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_UTIL_H */
--- /dev/null
+FOREACH(tcdir testcases libsolv-zypptestcases)
+ IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}")
+ FILE(GLOB dirs "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}/[_a-zA-Z0-9]*")
+ FOREACH(dir ${dirs})
+ IF(IS_DIRECTORY ${dir})
+ FILE(RELATIVE_PATH myname "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}" ${dir})
+ ADD_TEST(${myname} ${CMAKE_CURRENT_SOURCE_DIR}/runtestcases ${CMAKE_BINARY_DIR}/tools/testsolv ${dir})
+ ENDIF(IS_DIRECTORY ${dir})
+ ENDFOREACH(dir)
+ ENDIF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}")
+ENDFOREACH(tcdir)
--- /dev/null
+#!/bin/bash
+
+cmd=$1
+dir=$2
+
+if test -z "$cmd" -o -z "$dir"; then
+ echo "Usage: runtestcases <cmd> <dir>";
+ exit 1
+fi
+
+ex=0
+for tc in $(find $dir -name \*.t) ; do
+ $cmd $tc >/dev/null
+ tex=$?
+ tcn="${tc#$dir/} .................................................."
+ tcn="${tcn:0:50}"
+ if test "$tex" -eq 0 ; then
+ echo "$tcn Passed"
+ elif test "$tex" -eq 77 ; then
+ echo "$tcn Skipped"
+ else
+ echo "$tcn***Failed"
+ ex=1
+ fi
+done
+exit $ex
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: X 1 1 noarch
+#>=Req: Y
+#>=Pkg: B 1 1 noarch
+#>=Prv: Y
+#>=Pkg: A 1 1 noarch
+#>=Prv: Y
+system i686 rpm system
+job install name X
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install X-1-1.noarch@test
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: X 1 1 noarch
+#>=Req: Y
+#>=Pkg: B 1 1 noarch
+#>=Prv: Y
+#>=Enh: X
+#>=Pkg: A 1 1 noarch
+#>=Prv: Y
+system i686 rpm system
+job install name X
+result transaction,problems <inline>
+#>install B-1-1.noarch@test
+#>install X-1-1.noarch@test
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: X 1 1 noarch
+#>=Req: Y
+#>=Pkg: B 1 1 noarch
+#>=Prv: Y
+#>=Pkg: C 1 1 noarch
+#>=Prv: Y
+#>=Pkg: A 1 1 noarch
+#>=Prv: Y
+#>=Pkg: A 2 1 noarch
+system i686 rpm system
+job install name X
+result transaction,problems <inline>
+#>install B-1-1.noarch@test
+#>install X-1-1.noarch@test
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: X 1 1 noarch
+#>=Req: Y
+#>=Sug: B
+#>=Pkg: B 1 1 noarch
+#>=Prv: Y
+#>=Pkg: A 1 1 noarch
+#>=Prv: Y
+system i686 rpm system
+job install name X
+result transaction,problems <inline>
+#>install B-1-1.noarch@test
+#>install X-1-1.noarch@test
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: X 1 1 noarch
+#>=Req: Y
+#>=Pkg: B 1 1 noarch
+#>=Prv: Y = 2
+#>=Pkg: C 1 1 noarch
+#>=Prv: Y = 1.1
+#>=Pkg: A 1 1 noarch
+#>=Prv: Y = 1
+system i686 rpm system
+job install name X
+result transaction,problems <inline>
+#>install B-1-1.noarch@test
+#>install X-1-1.noarch@test
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: X 1 1 noarch
+#>=Req: Y
+#>=Pkg: B 1 1 noarch
+#>=Prv: Y < 2
+#>=Pkg: C 1 1 noarch
+#>=Prv: Y <= 2
+#>=Pkg: A 1 1 noarch
+#>=Prv: Y = 1
+system i686 rpm system
+job install name X
+result transaction,problems <inline>
+#>install C-1-1.noarch@test
+#>install X-1-1.noarch@test
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Req: B1
+#>=Pkg: B1 1 1 noarch
+repo test 0 testtags <inline>
+#>=Pkg: A 1 2 noarch
+#>=Req: B1
+#>=Pkg: A 2 1 noarch
+#>=Req: B2 = 1
+#>=Pkg: B1 1 1 noarch
+#>=Pkg: B2 1 1 noarch
+system i686 rpm system
+
+# check untargeted
+job distupgrade name A [cleandeps]
+result transaction,problems <inline>
+#>erase B1-1-1.noarch@system
+#>install B2-1-1.noarch@test
+#>upgrade A-1-1.noarch@system A-2-1.noarch@test
+
+# check targeted
+nextjob
+job distupgrade name A = 2 [cleandeps]
+result transaction,problems <inline>
+#>erase B1-1-1.noarch@system
+#>install B2-1-1.noarch@test
+#>upgrade A-1-1.noarch@system A-2-1.noarch@test
+
+# check targeted to 1-2
+nextjob
+job distupgrade name A = 1-2 [cleandeps]
+result transaction,problems <inline>
+#>upgrade A-1-1.noarch@system A-1-2.noarch@test
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Req: B1
+#>=Pkg: B1 1 1 noarch
+repo test 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Req: B2 = 1
+#>=Pkg: B1 1 1 noarch
+#>=Pkg: B2 1 1 noarch
+system i686 rpm system
+job install name A = 2 [cleandeps]
+result transaction,problems <inline>
+#>erase B1-1-1.noarch@system
+#>install B2-1-1.noarch@test
+#>upgrade A-1-1.noarch@system A-2-1.noarch@test
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Req: B1
+#>=Pkg: B1 1 1 noarch
+repo test 0 testtags <inline>
+#>=Pkg: A 1 2 noarch
+#>=Req: B1
+#>=Pkg: A 2 1 noarch
+#>=Req: B2 = 1
+#>=Pkg: B1 1 1 noarch
+#>=Pkg: B2 1 1 noarch
+system i686 rpm system
+
+# check untargeted
+job update name A [cleandeps]
+result transaction,problems <inline>
+#>erase B1-1-1.noarch@system
+#>install B2-1-1.noarch@test
+#>upgrade A-1-1.noarch@system A-2-1.noarch@test
+
+# check targeted
+nextjob
+job update name A = 2 [cleandeps]
+result transaction,problems <inline>
+#>erase B1-1-1.noarch@system
+#>install B2-1-1.noarch@test
+#>upgrade A-1-1.noarch@system A-2-1.noarch@test
+
+# check targeted to 1-2
+nextjob
+job update name A = 1-2 [cleandeps]
+result transaction,problems <inline>
+#>upgrade A-1-1.noarch@system A-1-2.noarch@test
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Req: B
+#>=Pkg: B 2 1 noarch
+repo test 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Req: B = 1-1
+#>=Pkg: B 1 1 noarch
+system unset deb system
+job install name A = 2-1 [cleandeps]
+result transaction,problems <inline>
+#>problem b5abcb9c info package A-2-1.noarch requires B = 1-1, but none of the providers can be installed
+#>problem b5abcb9c solution 3b3a37c0 deljob install name A = 2-1 [cleandeps]
+#>problem b5abcb9c solution 3c170283 replace B-2-1.noarch@system B-1-1.noarch@test
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: a 1 1 i686
+#>=Pkg: b 2 1 i686
+repo available 0 testtags <inline>
+#>=Pkg: a 2 1 i586
+#>=Con: b = 1-1
+#>=Pkg: b 1 1 i586
+system i686 * system
+solverflags !dupallowarchchange allowuninstall
+job distupgrade all packages
+result transaction,problems <inline>
+#>erase b-2-1.i686@system
+#>upgrade a-1-1.i686@system a-2-1.i586@available
--- /dev/null
+# test dup with multiversion packages
+#
+# part 1: simple update
+repo system 0 testtags <inline>
+#>=Pkg: a 1 1 i686
+repo available 0 testtags <inline>
+#>=Pkg: a 2 1 i686
+system i686 * system
+
+job multiversion name a
+job distupgrade all packages
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+nextjob
+
+job multiversion name a
+job distupgrade repo available
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+
+### same with keeporphans
+
+nextjob
+
+solverflags keeporphans
+job multiversion name a
+job distupgrade all packages
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+
+nextjob
+
+solverflags keeporphans
+job multiversion name a
+job distupgrade repo available
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+
+### same with allowuninstall
+
+nextjob
+
+solverflags allowuninstall
+job multiversion name a
+job distupgrade all packages
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+
+nextjob
+
+solverflags allowuninstall
+job multiversion name a
+job distupgrade repo available
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+
+### same with allowuninstall and keeporphans
+
+nextjob
+
+solverflags allowuninstall keeporphans
+job multiversion name a
+job distupgrade all packages
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+
+nextjob
+
+solverflags allowuninstall keeporphans
+job multiversion name a
+job distupgrade repo available
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+
+
+
--- /dev/null
+# test dup with multiversion packages
+# same as with dup_multiversion1, but we can't keep the orphan
+
+#
+# part 1: simple update
+repo system 0 testtags <inline>
+#>=Pkg: a 1 1 i686
+#>=Pkg: b 1 1 i686
+repo available 0 testtags <inline>
+#>=Pkg: a 2 1 i686
+#>=Pkg: b 2 1 i686
+#>=Con: a = 1-1
+system i686 * system
+
+job multiversion name a
+job distupgrade all packages
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+#>install a-2-1.i686@available
+#>upgrade b-1-1.i686@system b-2-1.i686@available
+
+nextjob
+
+job multiversion name a
+job distupgrade repo available
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+#>install a-2-1.i686@available
+#>upgrade b-1-1.i686@system b-2-1.i686@available
+
+
+### same with keeporphans, this will result in problems as we cannot keep the orphan
+
+nextjob
+
+solverflags keeporphans
+job multiversion name a
+job distupgrade all packages
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+#>problem 4d4de423 info package b-2-1.i686 conflicts with a = 1-1 provided by a-1-1.i686
+#>problem 4d4de423 solution 2cf4745c erase a-1-1.i686@system
+#>problem 4d4de423 solution 2cf4745c replace a-1-1.i686@system a-2-1.i686@available
+#>problem 4d4de423 solution 5a433aff allow b-1-1.i686@system
+#>problem 4d4de423 solution ce4305f2 erase b-1-1.i686@system
+
+nextjob
+
+solverflags keeporphans
+job multiversion name a
+job distupgrade repo available
+result transaction,problems <inline>
+#>install a-2-1.i686@available
+#>problem 4d4de423 info package b-2-1.i686 conflicts with a = 1-1 provided by a-1-1.i686
+#>problem 4d4de423 solution 2cf4745c erase a-1-1.i686@system
+#>problem 4d4de423 solution 2cf4745c replace a-1-1.i686@system a-2-1.i686@available
+#>problem 4d4de423 solution 5a433aff allow b-1-1.i686@system
+#>problem 4d4de423 solution ce4305f2 erase b-1-1.i686@system
+
+### same with allowuninstall
+
+nextjob
+
+solverflags allowuninstall
+job multiversion name a
+job distupgrade all packages
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+#>install a-2-1.i686@available
+#>upgrade b-1-1.i686@system b-2-1.i686@available
+
+nextjob
+
+solverflags allowuninstall
+job multiversion name a
+job distupgrade repo available
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+#>install a-2-1.i686@available
+#>upgrade b-1-1.i686@system b-2-1.i686@available
+
+
+### same with allowuninstall and keeporphans
+
+nextjob
+
+solverflags allowuninstall keeporphans
+job multiversion name a
+job distupgrade all packages
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>erase b-1-1.i686@system
+#>install a-2-1.i686@available
+
+
+nextjob
+
+solverflags allowuninstall keeporphans
+job multiversion name a
+job distupgrade repo available
+# a-1-1 is treated as orphaned and stays behind
+result transaction,problems <inline>
+#>erase b-1-1.i686@system
+#>install a-2-1.i686@available
+
+
--- /dev/null
+# test dup with multiversion packages where we cannot install the
+# target. Should give problems except for allowuninstall.
+#
+# part 1: simple update
+repo system 0 testtags <inline>
+#>=Pkg: a 1 1 i686
+repo available 0 testtags <inline>
+#>=Pkg: a 2 1 i686
+#>=Req: c
+system i686 * system
+
+job multiversion name a
+job distupgrade all packages
+result transaction,problems <inline>
+#>problem 251f1f35 info nothing provides c needed by a-2-1.i686
+#>problem 251f1f35 solution 2f2d254c allow a-1-1.i686@system
+
+nextjob
+
+job multiversion name a
+job distupgrade repo available
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+#>problem 251f1f35 info nothing provides c needed by a-2-1.i686
+#>problem 251f1f35 solution 2f2d254c allow a-1-1.i686@system
+
+### same with keeporphans
+
+nextjob
+
+solverflags keeporphans
+job multiversion name a
+job distupgrade all packages
+result transaction,problems <inline>
+#>problem 771581fd info nothing provides c needed by a-2-1.i686
+#>problem 771581fd solution 179b72ed allow a-1-1.i686@system
+#>problem 771581fd solution 2cf4745c erase a-1-1.i686@system
+
+nextjob
+
+solverflags keeporphans
+job multiversion name a
+job distupgrade repo available
+result transaction,problems <inline>
+#>problem 771581fd info nothing provides c needed by a-2-1.i686
+#>problem 771581fd solution 179b72ed allow a-1-1.i686@system
+#>problem 771581fd solution 2cf4745c erase a-1-1.i686@system
+
+### same with allowuninstall
+
+nextjob
+
+solverflags allowuninstall
+job multiversion name a
+job distupgrade all packages
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+
+
+nextjob
+
+solverflags allowuninstall
+job multiversion name a
+job distupgrade repo available
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+
+
+### same with allowuninstall and keeporphans
+
+nextjob
+
+solverflags allowuninstall keeporphans
+job multiversion name a
+job distupgrade all packages
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+
+
+nextjob
+
+solverflags allowuninstall keeporphans
+job multiversion name a
+job distupgrade repo available
+result transaction,problems <inline>
+#>erase a-1-1.i686@system
+
+
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: a 1 1 i686
+#>=Pkg: b 1 1 i686
+repo available 0 testtags <inline>
+#>=Pkg: a 2 1 i586
+#>=Pkg: b 2 1 i586
+#>=Pkg: b 2 1 i686
+system i686 * system
+solverflags !dupallowarchchange
+job distupgrade all packages
+result transaction,problems <inline>
+#>problem c43b1300 info problem with installed package a-1-1.i686
+#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-2-1.i586@available
+#>upgrade a-1-1.i686@system a-2-1.i586@available
+#>upgrade b-1-1.i686@system b-2-1.i686@available
--- /dev/null
+=Ver: 2.0
+#
+=Pkg: CEQ2 1 1 noarch
+=Con: B = 2
+=Pkg: CEQ22 1 1 noarch
+=Con: B = 2-2
+#
+=Pkg: CLT2 1 1 noarch
+=Con: B < 2
+=Pkg: CLT22 1 1 noarch
+=Con: B < 2-2
+#
+=Pkg: CGT2 1 1 noarch
+=Con: B > 2
+=Pkg: CGT22 1 1 noarch
+=Con: B > 2-2
+#
+=Pkg: CLE2 1 1 noarch
+=Con: B <= 2
+=Pkg: CLE22 1 1 noarch
+=Con: B <= 2-2
+#
+=Pkg: CGE2 1 1 noarch
+=Con: B >= 2
+=Pkg: CGE22 1 1 noarch
+=Con: B >= 2-2
--- /dev/null
+=Ver: 2.0
+#
+=Pkg: AEQ1 1 1 noarch
+=Prv: B = 1
+=Pkg: AEQ11 1 1 noarch
+=Prv: B = 1-1
+=Pkg: AEQ12 1 1 noarch
+=Prv: B = 1-2
+=Pkg: AEQ13 1 1 noarch
+=Prv: B = 1-3
+=Pkg: AEQ2 1 1 noarch
+=Prv: B = 2
+=Pkg: AEQ21 1 1 noarch
+=Prv: B = 2-1
+=Pkg: AEQ22 1 1 noarch
+=Prv: B = 2-2
+=Pkg: AEQ23 1 1 noarch
+=Prv: B = 2-3
+=Pkg: AEQ3 1 1 noarch
+=Prv: B = 3
+=Pkg: AEQ31 1 1 noarch
+=Prv: B = 3-1
+=Pkg: AEQ32 1 1 noarch
+=Prv: B = 3-2
+=Pkg: AEQ33 1 1 noarch
+=Prv: B = 3-3
+#
+=Pkg: ALT1 1 1 noarch
+=Prv: B < 1
+=Pkg: ALT11 1 1 noarch
+=Prv: B < 1-1
+=Pkg: ALT12 1 1 noarch
+=Prv: B < 1-2
+=Pkg: ALT13 1 1 noarch
+=Prv: B < 1-3
+=Pkg: ALT2 1 1 noarch
+=Prv: B < 2
+=Pkg: ALT21 1 1 noarch
+=Prv: B < 2-1
+=Pkg: ALT22 1 1 noarch
+=Prv: B < 2-2
+=Pkg: ALT23 1 1 noarch
+=Prv: B < 2-3
+=Pkg: ALT3 1 1 noarch
+=Prv: B < 3
+=Pkg: ALT31 1 1 noarch
+=Prv: B < 3-1
+=Pkg: ALT32 1 1 noarch
+=Prv: B < 3-2
+=Pkg: ALT33 1 1 noarch
+=Prv: B < 3-3
+#
+=Pkg: AGT1 1 1 noarch
+=Prv: B > 1
+=Pkg: AGT11 1 1 noarch
+=Prv: B > 1-1
+=Pkg: AGT12 1 1 noarch
+=Prv: B > 1-2
+=Pkg: AGT13 1 1 noarch
+=Prv: B > 1-3
+=Pkg: AGT2 1 1 noarch
+=Prv: B > 2
+=Pkg: AGT21 1 1 noarch
+=Prv: B > 2-1
+=Pkg: AGT22 1 1 noarch
+=Prv: B > 2-2
+=Pkg: AGT23 1 1 noarch
+=Prv: B > 2-3
+=Pkg: AGT3 1 1 noarch
+=Prv: B > 3
+=Pkg: AGT31 1 1 noarch
+=Prv: B > 3-1
+=Pkg: AGT32 1 1 noarch
+=Prv: B > 3-2
+=Pkg: AGT33 1 1 noarch
+=Prv: B > 3-3
+#
+=Pkg: ALE1 1 1 noarch
+=Prv: B <= 1
+=Pkg: ALE11 1 1 noarch
+=Prv: B <= 1-1
+=Pkg: ALE12 1 1 noarch
+=Prv: B <= 1-2
+=Pkg: ALE13 1 1 noarch
+=Prv: B <= 1-3
+=Pkg: ALE2 1 1 noarch
+=Prv: B <= 2
+=Pkg: ALE21 1 1 noarch
+=Prv: B <= 2-1
+=Pkg: ALE22 1 1 noarch
+=Prv: B <= 2-2
+=Pkg: ALE23 1 1 noarch
+=Prv: B <= 2-3
+=Pkg: ALE3 1 1 noarch
+=Prv: B <= 3
+=Pkg: ALE31 1 1 noarch
+=Prv: B <= 3-1
+=Pkg: ALE32 1 1 noarch
+=Prv: B <= 3-2
+=Pkg: ALE33 1 1 noarch
+=Prv: B <= 3-3
+#
+=Pkg: AGE1 1 1 noarch
+=Prv: B >= 1
+=Pkg: AGE11 1 1 noarch
+=Prv: B >= 1-1
+=Pkg: AGE12 1 1 noarch
+=Prv: B >= 1-2
+=Pkg: AGE13 1 1 noarch
+=Prv: B >= 1-3
+=Pkg: AGE2 1 1 noarch
+=Prv: B >= 2
+=Pkg: AGE21 1 1 noarch
+=Prv: B >= 2-1
+=Pkg: AGE22 1 1 noarch
+=Prv: B >= 2-2
+=Pkg: AGE23 1 1 noarch
+=Prv: B >= 2-3
+=Pkg: AGE3 1 1 noarch
+=Prv: B >= 3
+=Pkg: AGE31 1 1 noarch
+=Prv: B >= 3-1
+=Pkg: AGE32 1 1 noarch
+=Prv: B >= 3-2
+=Pkg: AGE33 1 1 noarch
+=Prv: B >= 3-3
+#
--- /dev/null
+#
+# these tests check all dependency match combinations,
+# both with release present and missing
+#
+repo system 0 testtags system.repo
+repo c 0 testtags conflicts.repo
+system i686 rpm system
+solverflags allowuninstall
+job install name CEQ2
+result transaction,problems <inline>
+#>erase AEQ2-1-1.noarch@system
+#>erase AEQ21-1-1.noarch@system
+#>erase AEQ22-1-1.noarch@system
+#>erase AEQ23-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGE22-1-1.noarch@system
+#>erase AGE23-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase AGT22-1-1.noarch@system
+#>erase AGT23-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE21-1-1.noarch@system
+#>erase ALE22-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT21-1-1.noarch@system
+#>erase ALT22-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CEQ2-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CEQ22
+result transaction,problems <inline>
+#>erase AEQ2-1-1.noarch@system
+#>erase AEQ22-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGE22-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE22-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CEQ22-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CLT2
+result transaction,problems <inline>
+#>erase AEQ1-1-1.noarch@system
+#>erase AEQ11-1-1.noarch@system
+#>erase AEQ12-1-1.noarch@system
+#>erase AEQ13-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase ALE1-1-1.noarch@system
+#>erase ALE11-1-1.noarch@system
+#>erase ALE12-1-1.noarch@system
+#>erase ALE13-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE21-1-1.noarch@system
+#>erase ALE22-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT1-1-1.noarch@system
+#>erase ALT11-1-1.noarch@system
+#>erase ALT12-1-1.noarch@system
+#>erase ALT13-1-1.noarch@system
+#>erase ALT2-1-1.noarch@system
+#>erase ALT21-1-1.noarch@system
+#>erase ALT22-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CLT2-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CLT22
+result transaction,problems <inline>
+#>erase AEQ1-1-1.noarch@system
+#>erase AEQ11-1-1.noarch@system
+#>erase AEQ12-1-1.noarch@system
+#>erase AEQ13-1-1.noarch@system
+#>erase AEQ2-1-1.noarch@system
+#>erase AEQ21-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase ALE1-1-1.noarch@system
+#>erase ALE11-1-1.noarch@system
+#>erase ALE12-1-1.noarch@system
+#>erase ALE13-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE21-1-1.noarch@system
+#>erase ALE22-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT1-1-1.noarch@system
+#>erase ALT11-1-1.noarch@system
+#>erase ALT12-1-1.noarch@system
+#>erase ALT13-1-1.noarch@system
+#>erase ALT2-1-1.noarch@system
+#>erase ALT21-1-1.noarch@system
+#>erase ALT22-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CLT22-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CGT2
+result transaction,problems <inline>
+#>erase AEQ3-1-1.noarch@system
+#>erase AEQ31-1-1.noarch@system
+#>erase AEQ32-1-1.noarch@system
+#>erase AEQ33-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGE22-1-1.noarch@system
+#>erase AGE23-1-1.noarch@system
+#>erase AGE3-1-1.noarch@system
+#>erase AGE31-1-1.noarch@system
+#>erase AGE32-1-1.noarch@system
+#>erase AGE33-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT2-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase AGT22-1-1.noarch@system
+#>erase AGT23-1-1.noarch@system
+#>erase AGT3-1-1.noarch@system
+#>erase AGT31-1-1.noarch@system
+#>erase AGT32-1-1.noarch@system
+#>erase AGT33-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CGT2-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CGT22
+result transaction,problems <inline>
+#>erase AEQ2-1-1.noarch@system
+#>erase AEQ23-1-1.noarch@system
+#>erase AEQ3-1-1.noarch@system
+#>erase AEQ31-1-1.noarch@system
+#>erase AEQ32-1-1.noarch@system
+#>erase AEQ33-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGE22-1-1.noarch@system
+#>erase AGE23-1-1.noarch@system
+#>erase AGE3-1-1.noarch@system
+#>erase AGE31-1-1.noarch@system
+#>erase AGE32-1-1.noarch@system
+#>erase AGE33-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT2-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase AGT22-1-1.noarch@system
+#>erase AGT23-1-1.noarch@system
+#>erase AGT3-1-1.noarch@system
+#>erase AGT31-1-1.noarch@system
+#>erase AGT32-1-1.noarch@system
+#>erase AGT33-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CGT22-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CLE2
+result transaction,problems <inline>
+#>erase AEQ1-1-1.noarch@system
+#>erase AEQ11-1-1.noarch@system
+#>erase AEQ12-1-1.noarch@system
+#>erase AEQ13-1-1.noarch@system
+#>erase AEQ2-1-1.noarch@system
+#>erase AEQ21-1-1.noarch@system
+#>erase AEQ22-1-1.noarch@system
+#>erase AEQ23-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGE22-1-1.noarch@system
+#>erase AGE23-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase AGT22-1-1.noarch@system
+#>erase AGT23-1-1.noarch@system
+#>erase ALE1-1-1.noarch@system
+#>erase ALE11-1-1.noarch@system
+#>erase ALE12-1-1.noarch@system
+#>erase ALE13-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE21-1-1.noarch@system
+#>erase ALE22-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT1-1-1.noarch@system
+#>erase ALT11-1-1.noarch@system
+#>erase ALT12-1-1.noarch@system
+#>erase ALT13-1-1.noarch@system
+#>erase ALT2-1-1.noarch@system
+#>erase ALT21-1-1.noarch@system
+#>erase ALT22-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CLE2-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CLE22
+result transaction,problems <inline>
+#>erase AEQ1-1-1.noarch@system
+#>erase AEQ11-1-1.noarch@system
+#>erase AEQ12-1-1.noarch@system
+#>erase AEQ13-1-1.noarch@system
+#>erase AEQ2-1-1.noarch@system
+#>erase AEQ21-1-1.noarch@system
+#>erase AEQ22-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGE22-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase ALE1-1-1.noarch@system
+#>erase ALE11-1-1.noarch@system
+#>erase ALE12-1-1.noarch@system
+#>erase ALE13-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE21-1-1.noarch@system
+#>erase ALE22-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT1-1-1.noarch@system
+#>erase ALT11-1-1.noarch@system
+#>erase ALT12-1-1.noarch@system
+#>erase ALT13-1-1.noarch@system
+#>erase ALT2-1-1.noarch@system
+#>erase ALT21-1-1.noarch@system
+#>erase ALT22-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CLE22-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CGE2
+result transaction,problems <inline>
+#>erase AEQ2-1-1.noarch@system
+#>erase AEQ21-1-1.noarch@system
+#>erase AEQ22-1-1.noarch@system
+#>erase AEQ23-1-1.noarch@system
+#>erase AEQ3-1-1.noarch@system
+#>erase AEQ31-1-1.noarch@system
+#>erase AEQ32-1-1.noarch@system
+#>erase AEQ33-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGE22-1-1.noarch@system
+#>erase AGE23-1-1.noarch@system
+#>erase AGE3-1-1.noarch@system
+#>erase AGE31-1-1.noarch@system
+#>erase AGE32-1-1.noarch@system
+#>erase AGE33-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT2-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase AGT22-1-1.noarch@system
+#>erase AGT23-1-1.noarch@system
+#>erase AGT3-1-1.noarch@system
+#>erase AGT31-1-1.noarch@system
+#>erase AGT32-1-1.noarch@system
+#>erase AGT33-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE21-1-1.noarch@system
+#>erase ALE22-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT21-1-1.noarch@system
+#>erase ALT22-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CGE2-1-1.noarch@c
+nextjob
+solverflags allowuninstall
+job install name CGE22
+result transaction,problems <inline>
+#>erase AEQ2-1-1.noarch@system
+#>erase AEQ22-1-1.noarch@system
+#>erase AEQ23-1-1.noarch@system
+#>erase AEQ3-1-1.noarch@system
+#>erase AEQ31-1-1.noarch@system
+#>erase AEQ32-1-1.noarch@system
+#>erase AEQ33-1-1.noarch@system
+#>erase AGE1-1-1.noarch@system
+#>erase AGE11-1-1.noarch@system
+#>erase AGE12-1-1.noarch@system
+#>erase AGE13-1-1.noarch@system
+#>erase AGE2-1-1.noarch@system
+#>erase AGE21-1-1.noarch@system
+#>erase AGE22-1-1.noarch@system
+#>erase AGE23-1-1.noarch@system
+#>erase AGE3-1-1.noarch@system
+#>erase AGE31-1-1.noarch@system
+#>erase AGE32-1-1.noarch@system
+#>erase AGE33-1-1.noarch@system
+#>erase AGT1-1-1.noarch@system
+#>erase AGT11-1-1.noarch@system
+#>erase AGT12-1-1.noarch@system
+#>erase AGT13-1-1.noarch@system
+#>erase AGT2-1-1.noarch@system
+#>erase AGT21-1-1.noarch@system
+#>erase AGT22-1-1.noarch@system
+#>erase AGT23-1-1.noarch@system
+#>erase AGT3-1-1.noarch@system
+#>erase AGT31-1-1.noarch@system
+#>erase AGT32-1-1.noarch@system
+#>erase AGT33-1-1.noarch@system
+#>erase ALE2-1-1.noarch@system
+#>erase ALE22-1-1.noarch@system
+#>erase ALE23-1-1.noarch@system
+#>erase ALE3-1-1.noarch@system
+#>erase ALE31-1-1.noarch@system
+#>erase ALE32-1-1.noarch@system
+#>erase ALE33-1-1.noarch@system
+#>erase ALT23-1-1.noarch@system
+#>erase ALT3-1-1.noarch@system
+#>erase ALT31-1-1.noarch@system
+#>erase ALT32-1-1.noarch@system
+#>erase ALT33-1-1.noarch@system
+#>install CGE22-1-1.noarch@c
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Vnd: foo
+#>=Pkg: D 1 1 noarch
+#>=Vnd: foo
+#>=Con: A = 3-1
+repo available 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Vnd: foo
+#>=Pkg: A 3 1 noarch
+#>=Vnd: bar
+system i686 rpm system
+
+job distupgrade name A [forcebest]
+result transaction,problems <inline>
+#>erase D-1-1.noarch@system
+#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
+#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
+#>problem 1210fdfb solution d85f7c4e allow A-2-1.noarch@available
+#>upgrade A-1-1.noarch@system A-3-1.noarch@available
+
+# test if bestobeypolicy is a noop for dup jobs
+nextjob
+solverflags bestobeypolicy
+job distupgrade name A [forcebest]
+result transaction,problems <inline>
+#>erase D-1-1.noarch@system
+#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
+#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
+#>problem 1210fdfb solution d85f7c4e allow A-2-1.noarch@available
+#>upgrade A-1-1.noarch@system A-3-1.noarch@available
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: D 1 1 noarch
+#>=Vnd: foo
+#>=Con: A = 3-1
+repo available 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Vnd: foo
+#>=Pkg: A 3 1 noarch
+#>=Vnd: bar
+system i686 rpm system
+
+job install name A [forcebest]
+result transaction,problems <inline>
+#>erase D-1-1.noarch@system
+#>install A-3-1.noarch@available
+#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
+#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
+#>problem 1210fdfb solution d85f7c4e deljob install name A [forcebest]
+
+# currently bestobeypolicy is a noop for install jobs
+nextjob
+solverflags bestobeypolicy
+job install name A [forcebest]
+result transaction,problems <inline>
+#>erase D-1-1.noarch@system
+#>install A-3-1.noarch@available
+#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
+#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
+#>problem 1210fdfb solution d85f7c4e deljob install name A [forcebest]
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Vnd: foo
+#>=Pkg: D 1 1 noarch
+#>=Vnd: foo
+#>=Con: A = 3-1
+repo available 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Vnd: foo
+#>=Pkg: A 3 1 noarch
+#>=Vnd: bar
+system i686 rpm system
+
+job update name A [forcebest]
+result transaction,problems <inline>
+#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
+#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
+#>problem 1210fdfb solution 0d75a914 replace A-1-1.noarch@system A-3-1.noarch@available
+#>problem 1210fdfb solution d85f7c4e allow A-2-1.noarch@available
+#>upgrade A-1-1.noarch@system A-2-1.noarch@available
+
+nextjob
+solverflags bestobeypolicy
+job update name A [forcebest]
+result transaction,problems <inline>
+#>upgrade A-1-1.noarch@system A-2-1.noarch@available
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: A 1 1 i586
+#>=Prv: A(x32)
+#>=Pkg: A 1 1 x86_64
+#>=Prv: A(x64)
+system x86_64 rpm system
+poolflags implicitobsoleteusescolors
+job install provides A(x32)
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 i586
+#>=Pkg: A 1 1 x86_64
+repo test 0 testtags <inline>
+#>=Pkg: A 2 1 i586
+#>=Pkg: A 2 1 x86_64
+#>=Pkg: A 3 1 i586
+#>=Pkg: A 4 1 x86_64
+system x86_64 rpm system
+poolflags implicitobsoleteusescolors
+job update all packages
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Pkg: B 1 1 noarch
+repo test 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Obs: B
+system i686 rpm system
+
+solverflags keepexplicitobsoletes
+job multiversion name A
+job install name A = 2
+result transaction,problems <inline>
+#>erase B-1-1.noarch@system
+#>install A-2-1.noarch@test
+
+nextjob
+solverflags keepexplicitobsoletes
+poolflags noobsoletesmultiversion
+job multiversion name A
+job install name A = 2
+result transaction,problems <inline>
+#>erase B-1-1.noarch@system
+#>install A-2-1.noarch@test
+
+nextjob
+poolflags !noobsoletesmultiversion
+job multiversion name A
+job install name A = 2
+result transaction,problems <inline>
+#>install A-2-1.noarch@test
--- /dev/null
+repo system 0 testtags <inline>
+#>=Ver: 2
+#>=Pkg: B 1 1 noarch
+#>=Prv: locale(en)
+#>=Pkg: C 1 1 noarch
+repo test 0 testtags <inline>
+#>=Ver: 2
+#>=Pkg: A 1 1 noarch
+#>=Prv: locale(de)
+#>=Pkg: C-de 1 1 noarch
+#>=Prv: locale(C:de)
+#>=Pkg: C-en 1 1 noarch
+#>=Prv: locale(C:en)
+system i686 rpm system
+
+# first test an empty job
+namespace namespace:language(de) @SYSTEM
+result transaction,problems <inline>
+
+# then test addalreadyrecommended
+nextjob
+namespace namespace:language(de) @SYSTEM
+solverflags addalreadyrecommended
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-de-1-1.noarch@test
+
+nextjob
+namespace namespace:language(de) @SYSTEM
+job install provides namespace:language(de)
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-de-1-1.noarch@test
+
+nextjob
+namespace namespace:language(de) @SYSTEM
+job erase provides namespace:language(en) [cleandeps]
+result transaction,problems <inline>
+#>erase B-1-1.noarch@system
+
+nextjob
+namespace namespace:language(de) @SYSTEM
+job install provides namespace:language(<NULL>)
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-de-1-1.noarch@test
+
+nextjob
+namespace namespace:language(de) @SYSTEM
+job erase provides namespace:language(<NULL>) [cleandeps]
+result transaction,problems <inline>
+#>erase B-1-1.noarch@system
+
+nextjob
+namespace namespace:language(de) @SYSTEM
+job install provides namespace:language(<NULL>)
+job erase provides namespace:language(<NULL>) [cleandeps]
+result transaction,problems <inline>
+#>erase B-1-1.noarch@system
+#>install A-1-1.noarch@test
+#>install C-de-1-1.noarch@test
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 x86_64
+#>=Prv: AA
+#>=Pkg: B 1 1 x86_64
+#>=Prv: AA
+system x86_64 * system
+job erase provides AA [weak]
+job install pkg B-1-1.x86_64@system
+result transaction,problems <inline>
--- /dev/null
+#
+# testcase to check enabling/disabling of learnt rules
+#
+repo system 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: A 1.0 1 noarch
+#>=Req: D
+#>=Prv: A = 1.0-1
+#>=Con: C
+#>=Pkg: C 1.0 1 noarch
+#>=Prv: foo
+#>=Prv: C = 1.0-1
+#>=Con: D
+#>=Pkg: D 1.0 1 noarch
+#>=Prv: D = 1.0-1
+#>=Pkg: A2 1.0 1 noarch
+#>=Req: D2
+#>=Prv: A2 = 1.0-1
+#>=Con: C2
+#>=Pkg: C2 1.0 1 noarch
+#>=Prv: foo
+#>=Prv: C2 = 1.0-1
+#>=Con: D2
+#>=Pkg: D2 1.0 1 noarch
+#>=Prv: D2 = 1.0-1
+repo test 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: C 2.0 1 noarch
+#>=Prv: C = 2.0-1
+#>=Pkg: A 2.0 1 noarch
+#>=Prv: A = 2.0-1
+#>=Pkg: D 2.0 1 noarch
+#>=Prv: D = 2.0-1
+#>=Pkg: C2 2.0 1 noarch
+#>=Prv: C2 = 2.0-1
+#>=Pkg: A2 2.0 1 noarch
+#>=Prv: A2 = 2.0-1
+#>=Pkg: D2 2.0 1 noarch
+#>=Prv: D2 = 2.0-1
+#>=Pkg: E 2.0 1 noarch
+#>=Req: foo
+#>=Prv: E = 2.0-1
+system unset * system
+job install provides E
+job verify all packages
+result transaction,problems <inline>
+#>erase D-1.0-1.noarch@system
+#>erase D2-1.0-1.noarch@system
+#>problem a3755a16 info package E-2.0-1.noarch requires foo, but none of the providers can be installed
+#>problem a3755a16 solution 6d40bce1 deljob install provides E
+#>problem a3755a16 solution c06ed43e erase D-1.0-1.noarch@system
+#>problem a3755a16 solution c8a04f77 erase D2-1.0-1.noarch@system
+#>upgrade A-1.0-1.noarch@system A-2.0-1.noarch@test
+#>upgrade A2-1.0-1.noarch@system A2-2.0-1.noarch@test
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Pkg: D 1 1 noarch
+#>=Pkg: Z 1 1 noarch
+#>=Con: D = 2-1
+repo available 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Pkg: B 1 0 noarch
+#>=Obs: A
+#>=Pkg: C 1 0 noarch
+#>=Obs: A = 1-1
+#>=Pkg: D 2 1 noarch
+#>=Pkg: D 3 1 noarch
+system unset * system
+
+# first check untargeted
+job distupgrade name A = 1-1
+result transaction,problems <inline>
+#>erase A-1-1.noarch@system B-1-0.noarch@available
+#>install B-1-0.noarch@available
+
+# then targeted to A-2-1
+nextjob
+job distupgrade name A = 2-1
+result transaction,problems <inline>
+#>upgrade A-1-1.noarch@system A-2-1.noarch@available
+
+# then targeted to B
+nextjob
+job distupgrade name B
+result transaction,problems <inline>
+#>erase A-1-1.noarch@system B-1-0.noarch@available
+#>install B-1-0.noarch@available
+
+# first check forced to targeted
+nextjob
+job distupgrade name A = 1-1 [targeted]
+result transaction,problems <inline>
+
+# second check forced to untargeted
+nextjob
+solverflags noautotarget
+job distupgrade name A = 2-1
+result transaction,problems <inline>
+
+# then targeted to D
+nextjob
+job distupgrade name D
+result transaction,problems <inline>
+#>upgrade D-1-1.noarch@system D-3-1.noarch@available
+
+# then targeted to D-2-1 (should not go to D-3-1)
+nextjob
+job distupgrade name D = 2-1
+result transaction,problems <inline>
+#>problem 840e2c39 info package Z-1-1.noarch conflicts with D = 2-1 provided by D-2-1.noarch
+#>problem 840e2c39 solution 3158736f erase Z-1-1.noarch@system
+#>problem 840e2c39 solution 42076df5 erase D-1-1.noarch@system
+#>problem 840e2c39 solution cdacbabe allow D-3-1.noarch@available
+#>upgrade D-1-1.noarch@system D-3-1.noarch@available
+
--- /dev/null
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Pkg: D 1 1 noarch
+#>=Pkg: Z 1 1 noarch
+#>=Con: D = 2-1
+repo available 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Pkg: B 1 0 noarch
+#>=Obs: A
+#>=Pkg: C 1 0 noarch
+#>=Obs: A = 1-1
+#>=Pkg: D 2 1 noarch
+#>=Pkg: D 3 1 noarch
+system unset * system
+
+# first check untargeted
+job update name A = 1-1
+result transaction,problems <inline>
+#>erase A-1-1.noarch@system B-1-0.noarch@available
+#>install B-1-0.noarch@available
+
+# then targeted to A-2-1
+nextjob
+job update name A = 2-1
+result transaction,problems <inline>
+#>upgrade A-1-1.noarch@system A-2-1.noarch@available
+
+# then targeted to B
+nextjob
+job update name B
+result transaction,problems <inline>
+#>erase A-1-1.noarch@system B-1-0.noarch@available
+#>install B-1-0.noarch@available
+
+# first check forced to targeted
+nextjob
+job update name A = 1-1 [targeted]
+result transaction,problems <inline>
+
+# second check forced to untargeted
+nextjob
+solverflags noautotarget
+job distupgrade name A = 2-1
+result transaction,problems <inline>
+
+# then targeted to D
+nextjob
+job update name D
+result transaction,problems <inline>
+#>upgrade D-1-1.noarch@system D-3-1.noarch@available
+
+# then targeted to D-2-1 (should not go to D-3-1)
+nextjob
+job update name D = 2-1
+result transaction,problems <inline>
+
--- /dev/null
+# testcase for testcase_str2dep and testcase_dep2str
+
+#
+# first test literal escaping
+#
+genid dep <NULL>
+result genid <inline>
+#>genid 1: genid null
+#>genid dep <NULL>
+nextjob
+
+genid dep \00
+result genid <inline>
+#>genid 1: genid lit
+#>genid dep \00
+nextjob
+
+genid dep \21\20\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f\3a\3b\3c\3d\3e\3f\40\5b\5c\5d\5e\5f\60\7b\7c\7d\7e
+result genid <inline>
+#>genid 1: genid lit ! "#$%&'()*+,-./:;<=>?@[\]^_`{|}~
+#>genid dep \21\20"#$%&'\28\29*+,-./:;<=>?@[\5c]^_`{|}~
+# make vim happy again: '
+nextjob
+
+genid dep foo(bar)
+result genid <inline>
+#>genid 1: genid lit foo(bar)
+#>genid dep foo(bar)
+nextjob
+
+genid dep foo()bar\29
+result genid <inline>
+#>genid 1: genid lit foo()bar)
+#>genid dep foo\28\29bar\29
+nextjob
+
+#
+# test namespace hack
+#
+genid dep namespace:foo(bar)
+result genid <inline>
+#>genid 1: genid lit namespace:foo
+#>genid 2: genid lit bar
+#>genid 3: genid op <NAMESPACE>
+#>genid dep namespace:foo(bar)
+nextjob
+genid lit namespace:foo(bar)
+result genid <inline>
+#>genid 1: genid lit namespace:foo(bar)
+#>genid dep namespace\3afoo\28bar\29
+nextjob
+
+#
+# test :any hack
+#
+genid dep foo:any
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit any
+#>genid 3: genid op <MULTIARCH>
+#>genid dep foo:any
+nextjob
+genid lit foo:any
+result genid <inline>
+#>genid 1: genid lit foo:any
+#>genid dep foo\3aany
+nextjob
+
+#
+# test simple ops
+#
+genid dep foo < 1-1
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit 1-1
+#>genid 3: genid op <
+#>genid dep foo < 1-1
+nextjob
+
+genid dep foo = 1-1
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit 1-1
+#>genid 3: genid op =
+#>genid dep foo = 1-1
+nextjob
+
+genid dep foo > 1-1
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit 1-1
+#>genid 3: genid op >
+#>genid dep foo > 1-1
+nextjob
+
+genid dep foo >= 1-1
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit 1-1
+#>genid 3: genid op >=
+#>genid dep foo >= 1-1
+nextjob
+
+genid dep foo <= 1-1
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit 1-1
+#>genid 3: genid op <=
+#>genid dep foo <= 1-1
+nextjob
+
+# test arch op
+genid dep foo . i586
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit i586
+#>genid 3: genid op .
+#>genid dep foo . i586
+nextjob
+
+# test haiku compat dep
+genid dep foo = 2-1 compat >= 1-1
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit 2-1
+#>genid 3: genid lit 1-1
+#>genid 4: genid op compat >=
+#>genid 5: genid op =
+#>genid dep foo = 2-1 compat >= 1-1
+nextjob
+
+#
+# test complex (aka rich) deps
+#
+
+genid dep foo & bar
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit bar
+#>genid 3: genid op &
+#>genid dep foo & bar
+nextjob
+
+genid dep foo & bar & baz
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit bar
+#>genid 3: genid lit baz
+#>genid 4: genid op &
+#>genid 5: genid op &
+#>genid dep foo & bar & baz
+nextjob
+
+genid dep foo & bar | baz
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit bar
+#>genid 3: genid lit baz
+#>genid 4: genid op |
+#>genid 5: genid op &
+#>genid dep foo & (bar | baz)
+nextjob
+
+genid dep (foo & bar) | baz
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit bar
+#>genid 3: genid op &
+#>genid 4: genid lit baz
+#>genid 5: genid op |
+#>genid dep (foo & bar) | baz
+nextjob
+
+genid dep (foo & bar > 2) | baz
+result genid <inline>
+#>genid 1: genid lit foo
+#>genid 2: genid lit bar
+#>genid 3: genid lit 2
+#>genid 4: genid op >
+#>genid 5: genid op &
+#>genid 6: genid lit baz
+#>genid 7: genid op |
+#>genid dep (foo & bar > 2) | baz
+nextjob
+
--- /dev/null
+repo system 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: c 27 1 x86_64
+repo available 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: d 28 1 x86_64
+#>=Obs: c
+#>=Pkg: e 28 1 x86_64
+#>=Obs: c
+
+system x86_64 rpm system
+
+job update all packages
+result transaction,problems <inline>
+#>erase c-27-1.x86_64@system d-28-1.x86_64@available
+#>install d-28-1.x86_64@available
+
+nextjob
+solverflags yumobsoletes
+job update all packages
+result transaction,problems <inline>
+#>erase c-27-1.x86_64@system d-28-1.x86_64@available
+#>install d-28-1.x86_64@available
+#>install e-28-1.x86_64@available
--- /dev/null
+#
+# CMakeLists.txt for tools
+#
+
+ADD_LIBRARY (toolstuff STATIC common_write.c)
+
+SET (tools_list mergesolv dumpsolv installcheck testsolv)
+
+IF (ENABLE_RPMDB)
+ADD_EXECUTABLE (rpmdb2solv rpmdb2solv.c)
+TARGET_LINK_LIBRARIES (rpmdb2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+ADD_EXECUTABLE (rpms2solv rpms2solv.c)
+TARGET_LINK_LIBRARIES (rpms2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+ADD_EXECUTABLE (findfileconflicts findfileconflicts.c)
+TARGET_LINK_LIBRARIES (findfileconflicts libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} rpmdb2solv rpms2solv)
+ENDIF (ENABLE_RPMDB)
+
+IF (ENABLE_RPMMD)
+ADD_EXECUTABLE (repomdxml2solv repomdxml2solv.c)
+TARGET_LINK_LIBRARIES (repomdxml2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+ADD_EXECUTABLE (rpmmd2solv rpmmd2solv.c)
+TARGET_LINK_LIBRARIES (rpmmd2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+ADD_EXECUTABLE (updateinfoxml2solv updateinfoxml2solv.c)
+TARGET_LINK_LIBRARIES (updateinfoxml2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+ADD_EXECUTABLE (deltainfoxml2solv deltainfoxml2solv.c)
+TARGET_LINK_LIBRARIES (deltainfoxml2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} repomdxml2solv rpmmd2solv updateinfoxml2solv deltainfoxml2solv)
+ENDIF (ENABLE_RPMMD)
+
+IF (ENABLE_HELIXREPO)
+ADD_EXECUTABLE (helix2solv helix2solv.c)
+TARGET_LINK_LIBRARIES (helix2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} helix2solv)
+ENDIF (ENABLE_HELIXREPO)
+
+IF (ENABLE_SUSEREPO)
+ADD_EXECUTABLE (susetags2solv susetags2solv.c)
+TARGET_LINK_LIBRARIES (susetags2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} susetags2solv)
+ENDIF (ENABLE_SUSEREPO)
+
+IF (ENABLE_COMPS)
+ADD_EXECUTABLE (comps2solv comps2solv.c)
+TARGET_LINK_LIBRARIES (comps2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} comps2solv)
+ENDIF (ENABLE_COMPS)
+
+IF (ENABLE_DEBIAN)
+ADD_EXECUTABLE (deb2solv deb2solv.c)
+TARGET_LINK_LIBRARIES (deb2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} deb2solv)
+ENDIF (ENABLE_DEBIAN)
+
+IF (ENABLE_MDKREPO)
+ADD_EXECUTABLE (mdk2solv mdk2solv.c)
+TARGET_LINK_LIBRARIES (mdk2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} mdk2solv)
+ENDIF (ENABLE_MDKREPO)
+
+IF (ENABLE_ARCHREPO)
+ADD_EXECUTABLE (archpkgs2solv archpkgs2solv.c)
+TARGET_LINK_LIBRARIES (archpkgs2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+ADD_EXECUTABLE (archrepo2solv archrepo2solv.c)
+TARGET_LINK_LIBRARIES (archrepo2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} archpkgs2solv archrepo2solv)
+ENDIF (ENABLE_ARCHREPO)
+
+IF (ENABLE_CUDFREPO)
+ADD_EXECUTABLE (cudftest cudftest.c)
+TARGET_LINK_LIBRARIES (cudftest libsolvext libsolv ${SYSTEM_LIBRARIES})
+ENDIF (ENABLE_CUDFREPO)
+
+ADD_EXECUTABLE (installcheck installcheck.c)
+TARGET_LINK_LIBRARIES (installcheck libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+IF (SUSE)
+ADD_EXECUTABLE (patchcheck patchcheck.c)
+TARGET_LINK_LIBRARIES (patchcheck libsolvext libsolv ${SYSTEM_LIBRARIES})
+ENDIF (SUSE)
+
+IF (ENABLE_APPDATA)
+ADD_EXECUTABLE (appdata2solv appdata2solv.c)
+TARGET_LINK_LIBRARIES (appdata2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} appdata2solv)
+ENDIF (ENABLE_APPDATA)
+
+ADD_EXECUTABLE (dumpsolv dumpsolv.c )
+TARGET_LINK_LIBRARIES (dumpsolv libsolv)
+
+ADD_EXECUTABLE (mergesolv mergesolv.c )
+TARGET_LINK_LIBRARIES (mergesolv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+ADD_EXECUTABLE (testsolv testsolv.c)
+TARGET_LINK_LIBRARIES (testsolv libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+INSTALL (TARGETS ${tools_list} DESTINATION ${BIN_INSTALL_DIR})
+
+INSTALL (PROGRAMS repo2solv.sh DESTINATION ${BIN_INSTALL_DIR})
--- /dev/null
+/*
+ * Copyright (c) 2013, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * appdata2solv.c
+ *
+ * parse AppStream appdata type xml and write out .solv file
+ *
+ * reads from stdin
+ * writes to stdout
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_appdata.h"
+#include "common_write.h"
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool = pool_create();
+ Repo *repo;
+ int c;
+ const char *appdatadir = 0;
+ const char *root = 0;
+
+ while ((c = getopt(argc, argv, "hd:r:")) >= 0)
+ {
+ switch (c)
+ {
+ case 'd':
+ appdatadir = optarg;
+ break;
+ case 'r':
+ root = optarg;
+ break;
+ default:
+ fprintf(stderr, "usage: appdata2solv [-d appdatadir]");
+ exit(c == 'h' ? 0 : 1);
+ }
+ }
+
+ if (root)
+ pool_set_rootdir(pool, root);
+
+ repo = repo_create(pool, "<stdin>");
+ if (!appdatadir)
+ {
+ if (repo_add_appdata(repo, stdin, 0))
+ {
+ fprintf(stderr, "appdata2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+ else
+ {
+ if (repo_add_appdata_dir(repo, appdatadir, REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "appdata2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+ tool_write(repo, 0, 0);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * archpkgs2solv - create a solv file from multiple arch packages
+ *
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "util.h"
+#include "pool.h"
+#include "repo.h"
+#include "repo_arch.h"
+#include "repo_solv.h"
+#include "common_write.h"
+
+static char *
+fgets0(char *s, int size, FILE *stream)
+{
+ char *p = s;
+ int c;
+
+ while (--size > 0)
+ {
+ c = getc(stream);
+ if (c == EOF)
+ {
+ if (p == s)
+ return 0;
+ c = 0;
+ }
+ *p++ = c;
+ if (!c)
+ return s;
+ }
+ *p = 0;
+ return s;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char **pkgs = 0;
+ char *manifest = 0;
+ int manifest0 = 0;
+ int i, c, res, npkgs = 0;
+ Pool *pool = pool_create();
+ Repo *repo;
+ FILE *fp;
+ char buf[4096], *p;
+ const char *basefile = 0;
+ int flags = 0;
+
+ while ((c = getopt(argc, argv, "0b:m:i")) >= 0)
+ {
+ switch(c)
+ {
+ case 'b':
+ basefile = optarg;
+ break;
+ case 'm':
+ manifest = optarg;
+ break;
+ case '0':
+ manifest0 = 1;
+ break;
+ case 'i':
+ flags |= ARCH_ADD_WITH_PKGID;
+ break;
+ default:
+ exit(1);
+ }
+ }
+ if (manifest)
+ {
+ if (!strcmp(manifest, "-"))
+ fp = stdin;
+ else if ((fp = fopen(manifest, "r")) == 0)
+ {
+ perror(manifest);
+ exit(1);
+ }
+ for (;;)
+ {
+ if (manifest0)
+ {
+ if (!fgets0(buf, sizeof(buf), fp))
+ break;
+ }
+ else
+ {
+ if (!fgets(buf, sizeof(buf), fp))
+ break;
+ if ((p = strchr(buf, '\n')) != 0)
+ *p = 0;
+ }
+ pkgs = solv_extend(pkgs, npkgs, 1, sizeof(char *), 15);
+ pkgs[npkgs++] = strdup(buf);
+ }
+ if (fp != stdin)
+ fclose(fp);
+ }
+ while (optind < argc)
+ {
+ pkgs = solv_extend(pkgs, npkgs, 1, sizeof(char *), 15);
+ pkgs[npkgs++] = solv_strdup(argv[optind++]);
+ }
+ repo = repo_create(pool, "archpkgs2solv");
+ repo_add_repodata(repo, 0);
+ res = 0;
+ for (i = 0; i < npkgs; i++)
+ if (repo_add_arch_pkg(repo, pkgs[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|flags) == 0)
+ {
+ fprintf(stderr, "archpkgs2solv: %s\n", pool_errstr(pool));
+ res = 1;
+ }
+ repo_internalize(repo);
+ tool_write(repo, basefile, 0);
+ pool_free(pool);
+ for (c = 0; c < npkgs; c++)
+ solv_free((char *)pkgs[c]);
+ solv_free(pkgs);
+ exit(res);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * archrepo2solv.c
+ *
+ * parse archlinux repo file
+ *
+ * reads from stdin
+ * writes to stdout
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_arch.h"
+#include "solv_xfopen.h"
+#include "common_write.h"
+
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "archrepo2solv\n"
+ " reads a repository from <stdin> and writes a .solv file to <stdout>\n"
+ " -l <dbdir> : read local database\n"
+ " -h : print help & exit\n"
+ );
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool;
+ Repo *repo;
+ int c, ret;
+ const char *localdb = 0;
+
+ while ((c = getopt(argc, argv, "hl:")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'l':
+ localdb = optarg;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ pool = pool_create();
+ repo = repo_create(pool, "<stdin>");
+ if (localdb)
+ ret = repo_add_arch_local(repo, localdb, 0);
+ else
+ ret = repo_add_arch_repo(repo, stdin, 0);
+ if (ret)
+ {
+ fprintf(stderr, "archrepo2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ tool_write(repo, 0, 0);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_write.h"
+#include "common_write.h"
+
+#define LIBSOLV_TOOLVERSION "1.0"
+
+static Id verticals[] = {
+ SOLVABLE_AUTHORS,
+ SOLVABLE_DESCRIPTION,
+ SOLVABLE_MESSAGEDEL,
+ SOLVABLE_MESSAGEINS,
+ SOLVABLE_EULA,
+ SOLVABLE_DISKUSAGE,
+ SOLVABLE_FILELIST,
+ SOLVABLE_CHANGELOG_AUTHOR,
+ SOLVABLE_CHANGELOG_TEXT,
+ 0
+};
+
+static char *languagetags[] = {
+ "solvable:summary:",
+ "solvable:description:",
+ "solvable:messageins:",
+ "solvable:messagedel:",
+ "solvable:eula:",
+ 0
+};
+
+static int test_separate = 0;
+
+struct keyfilter_data {
+ char **languages;
+ int nlanguages;
+ int haveaddedfileprovides;
+ int haveexternal;
+};
+
+static int
+keyfilter_solv(Repo *data, Repokey *key, void *kfdata)
+{
+ struct keyfilter_data *kd = kfdata;
+ int i;
+ const char *keyname;
+
+ if (test_separate && key->storage != KEY_STORAGE_SOLVABLE)
+ return KEY_STORAGE_DROPPED;
+ if (!kd->haveaddedfileprovides && key->name == REPOSITORY_ADDEDFILEPROVIDES)
+ return KEY_STORAGE_DROPPED;
+ if (!kd->haveexternal && key->name == REPOSITORY_EXTERNAL)
+ return KEY_STORAGE_DROPPED;
+ for (i = 0; verticals[i]; i++)
+ if (key->name == verticals[i])
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ keyname = pool_id2str(data->pool, key->name);
+ for (i = 0; languagetags[i] != 0; i++)
+ if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ return KEY_STORAGE_INCORE;
+}
+
+static int
+keyfilter_attr(Repo *data, Repokey *key, void *kfdata)
+{
+ int i;
+ const char *keyname;
+ if (key->storage == KEY_STORAGE_SOLVABLE)
+ return KEY_STORAGE_DROPPED;
+ /* those must only be in the main solv file */
+ if (key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_TOOLVERSION)
+ return KEY_STORAGE_DROPPED;
+ for (i = 0; verticals[i]; i++)
+ if (key->name == verticals[i])
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ keyname = pool_id2str(data->pool, key->name);
+ for (i = 0; languagetags[i] != 0; i++)
+ if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ return KEY_STORAGE_INCORE;
+}
+
+static int
+keyfilter_language(Repo *repo, Repokey *key, void *kfdata)
+{
+ Pool *pool = repo->pool;
+ const char *name, *p;
+ char *lang = kfdata;
+ int i;
+
+ name = pool_id2str(repo->pool, key->name);
+ p = strrchr(name, ':');
+ if (!p || strcmp(p + 1, lang) != 0)
+ return KEY_STORAGE_DROPPED;
+ for (i = 0; verticals[i]; i++)
+ {
+ const char *vname = pool_id2str(pool, verticals[i]);
+ if (!strncmp(name, vname, p - name) && vname[p - name] == 0)
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ }
+ return KEY_STORAGE_INCORE;
+}
+
+static int
+keyfilter_DU(Repo *repo, Repokey *key, void *kfdata)
+{
+ int i;
+ if (key->name != SOLVABLE_DISKUSAGE)
+ return KEY_STORAGE_DROPPED;
+ for (i = 0; verticals[i]; i++)
+ if (key->name == verticals[i])
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ return KEY_STORAGE_INCORE;
+}
+
+static int
+keyfilter_FL(Repo *repo, Repokey *key, void *kfdata)
+{
+ int i;
+ if (key->name != SOLVABLE_FILELIST)
+ return KEY_STORAGE_DROPPED;
+ for (i = 0; verticals[i]; i++)
+ if (key->name == verticals[i])
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ return KEY_STORAGE_INCORE;
+}
+
+static int
+keyfilter_other(Repo *repo, Repokey *key, void *kfdata)
+{
+ const char *name, *p;
+ struct keyfilter_data *kd = kfdata;
+ int i;
+
+ if (!kd->haveaddedfileprovides && key->name == REPOSITORY_ADDEDFILEPROVIDES)
+ return KEY_STORAGE_DROPPED;
+ if (!kd->haveexternal && key->name == REPOSITORY_EXTERNAL)
+ return KEY_STORAGE_DROPPED;
+
+ if (key->name == SOLVABLE_FILELIST || key->name == SOLVABLE_DISKUSAGE)
+ return KEY_STORAGE_DROPPED;
+
+ name = pool_id2str(repo->pool, key->name);
+ p = strrchr(name, ':');
+ if (p)
+ {
+ for (i = 0; i < kd->nlanguages; i++)
+ if (!strcmp(p + 1, kd->languages[i]))
+ return KEY_STORAGE_DROPPED;
+ }
+ for (i = 0; verticals[i]; i++)
+ if (key->name == verticals[i])
+ return KEY_STORAGE_VERTICAL_OFFSET;
+ return KEY_STORAGE_INCORE;
+}
+
+/*
+ * Write <repo> to stdout
+ * If <attrname> is given, write attributes to <attrname>
+ * If <basename> is given, split attributes
+ */
+
+#define REPODATAFILE_BLOCK 15
+
+static void
+write_info(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Repodata *info, const char *location)
+{
+ Id h;
+ Queue keyq;
+
+ queue_init(&keyq);
+ if (repo_write_filtered(repo, fp, keyfilter, kfdata, &keyq) != 0)
+ {
+ fprintf(stderr, "repo_write failed\n");
+ exit(1);
+ }
+ h = repodata_new_handle(info);
+ if (keyq.count)
+ repodata_set_idarray(info, h, REPOSITORY_KEYS, &keyq);
+ queue_free(&keyq);
+ repodata_set_str(info, h, REPOSITORY_LOCATION, location);
+ repodata_add_flexarray(info, SOLVID_META, REPOSITORY_EXTERNAL, h);
+}
+
+void
+tool_write(Repo *repo, const char *basename, const char *attrname)
+{
+ Repodata *data;
+ Repodata *info = 0;
+ Repokey *key;
+ char **languages = 0;
+ int nlanguages = 0;
+ int i, j, k, l;
+ struct keyfilter_data kd;
+ Queue addedfileprovides;
+
+ memset(&kd, 0, sizeof(kd));
+ info = repo_add_repodata(repo, 0);
+ repodata_set_str(info, SOLVID_META, REPOSITORY_TOOLVERSION, LIBSOLV_TOOLVERSION);
+ queue_init(&addedfileprovides);
+ pool_addfileprovides_queue(repo->pool, &addedfileprovides, 0);
+ if (addedfileprovides.count)
+ {
+ kd.haveaddedfileprovides = 1;
+ repodata_set_idarray(info, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &addedfileprovides);
+ }
+ queue_free(&addedfileprovides);
+
+ pool_freeidhashes(repo->pool); /* free some mem */
+
+ if (basename)
+ {
+ char fn[4096];
+ FILE *fp;
+ int has_DU = 0;
+ int has_FL = 0;
+
+ /* find languages and other info */
+ FOR_REPODATAS(repo, i, data)
+ {
+ for (j = 1, key = data->keys + j; j < data->nkeys; j++, key++)
+ {
+ const char *keyname = pool_id2str(repo->pool, key->name);
+ if (key->name == SOLVABLE_DISKUSAGE)
+ has_DU = 1;
+ if (key->name == SOLVABLE_FILELIST)
+ has_FL = 1;
+ for (k = 0; languagetags[k] != 0; k++)
+ if (!strncmp(keyname, languagetags[k], strlen(languagetags[k])))
+ break;
+ if (!languagetags[k])
+ continue;
+ l = strlen(languagetags[k]);
+ if (strlen(keyname + l) > 5)
+ continue;
+ for (k = 0; k < nlanguages; k++)
+ if (!strcmp(languages[k], keyname + l))
+ break;
+ if (k < nlanguages)
+ continue;
+ languages = solv_realloc2(languages, nlanguages + 1, sizeof(char *));
+ languages[nlanguages++] = strdup(keyname + l);
+ }
+ }
+ /* write language subfiles */
+ for (i = 0; i < nlanguages; i++)
+ {
+ sprintf(fn, "%s.%s.solv", basename, languages[i]);
+ if (!(fp = fopen(fn, "w")))
+ {
+ perror(fn);
+ exit(1);
+ }
+ write_info(repo, fp, keyfilter_language, languages[i], info, fn);
+ fclose(fp);
+ kd.haveexternal = 1;
+ }
+ /* write DU subfile */
+ if (has_DU)
+ {
+ sprintf(fn, "%s.DU.solv", basename);
+ if (!(fp = fopen(fn, "w")))
+ {
+ perror(fn);
+ exit(1);
+ }
+ write_info(repo, fp, keyfilter_DU, 0, info, fn);
+ fclose(fp);
+ kd.haveexternal = 1;
+ }
+ /* write filelist */
+ if (has_FL)
+ {
+ sprintf(fn, "%s.FL.solv", basename);
+ if (!(fp = fopen(fn, "w")))
+ {
+ perror(fn);
+ exit(1);
+ }
+ write_info(repo, fp, keyfilter_FL, 0, info, fn);
+ fclose(fp);
+ kd.haveexternal = 1;
+ }
+ /* write everything else */
+ sprintf(fn, "%s.solv", basename);
+ if (!(fp = fopen(fn, "w")))
+ {
+ perror(fn);
+ exit(1);
+ }
+ kd.languages = languages;
+ kd.nlanguages = nlanguages;
+ repodata_internalize(info);
+ if (repo_write_filtered(repo, fp, keyfilter_other, &kd, 0) != 0)
+ {
+ fprintf(stderr, "repo_write failed\n");
+ exit(1);
+ }
+ if (fclose(fp) != 0)
+ {
+ perror("fclose");
+ exit(1);
+ }
+ for (i = 0; i < nlanguages; i++)
+ free(languages[i]);
+ solv_free(languages);
+ repodata_free(info);
+ }
+ if (attrname)
+ {
+ FILE *fp;
+ test_separate = 1;
+ fp = fopen(attrname, "w");
+ write_info(repo, fp, keyfilter_attr, 0, info, attrname);
+ fclose(fp);
+ kd.haveexternal = 1;
+ }
+ repodata_internalize(info);
+ if (repo_write_filtered(repo, stdout, keyfilter_solv, &kd, 0) != 0)
+ {
+ fprintf(stderr, "repo_write failed\n");
+ exit(1);
+ }
+ repodata_free(info);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#ifndef COMMON_WRITE_H
+#define COMMON_WRITE_H
+
+#include "repo.h"
+
+void tool_write(Repo *repo, const char *basename, const char *attrname);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * comps2solv.c
+ *
+ * parse Fedora Comps type xml and write out .solv file
+ *
+ * reads from stdin
+ * writes to stdout
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_comps.h"
+#include "common_write.h"
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool = pool_create();
+ Repo *repo = repo_create(pool, "<stdin>");
+ if (repo_add_comps(repo, stdin, 0))
+ {
+ fprintf(stderr, "comps2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ tool_write(repo, 0, 0);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "evr.h"
+#include "solver.h"
+#include "solverdebug.h"
+#include "repo_cudf.h"
+#include "repo_write.h"
+#include "solv_xfopen.h"
+
+static void
+dump_repo(Repo *repo, char *name)
+{
+ FILE *fp;
+ if ((fp = fopen(name, "w")) == 0)
+ {
+ perror(name);
+ exit(1);
+ }
+ repo_write(repo, fp);
+ fclose(fp);
+}
+
+static int
+sortfunc(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = dp;
+ Solvable *sa, *sb;
+ sa = pool->solvables + *(Id *)ap;
+ sb = pool->solvables + *(Id *)bp;
+ if (sa->name != sb->name)
+ {
+ int r = strcmp(pool_id2str(pool, sa->name), pool_id2str(pool, sb->name));
+ if (r)
+ return r;
+ }
+ if (sa->evr != sb->evr)
+ {
+ int r = pool_evrcmp(pool, sa->evr, sb->evr, EVRCMP_COMPARE);
+ if (r)
+ return r;
+ }
+ return *(Id *)ap - *(Id *)bp;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *cudfin;
+ char *cudfout = 0;
+ Pool *pool;
+ Repo *installed, *repo;
+ FILE *fp, *ofp;
+ Solver *solv;
+ Transaction *trans;
+ Queue job;
+ Queue dq;
+ int i;
+ int debug = 0;
+
+ while (argc > 1 && !strcmp(argv[1], "-d"))
+ {
+ debug++;
+ argc--;
+ argv++;
+ }
+ if (argc < 2)
+ {
+ fprintf(stderr, "Usage: cudftest <cudfin> [cudfout]\n");
+ exit(1);
+ }
+ cudfin = argv[1];
+ cudfout = argc > 2 ? argv[2] : 0;
+
+ if ((fp = solv_xfopen(cudfin, 0)) == 0)
+ {
+ perror(cudfin);
+ exit(1);
+ }
+ pool = pool_create();
+ if (debug > 1)
+ pool_setdebuglevel(pool, debug - 1);
+ installed = repo_create(pool, "installed");
+ pool_set_installed(pool, installed);
+ repo = repo_create(pool, "repo");
+ queue_init(&job);
+ repo_add_cudf(repo, installed, fp, &job, 0);
+ fclose(fp);
+
+ pool_createwhatprovides(pool);
+
+ /* debug */
+ if (debug)
+ {
+ dump_repo(installed, "cudf_installed.solv");
+ dump_repo(repo, "cudf_repo.solv");
+ }
+
+ solv = solver_create(pool);
+ solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);
+ /* solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1); */
+
+ queue_push2(&job, SOLVER_VERIFY | SOLVER_SOLVABLE_ALL, 0);
+ if (solver_solve(solv, &job) != 0)
+ {
+ int problem;
+ int pcnt = solver_problem_count(solv);
+ printf("Found %d problems:\n", pcnt);
+ for (problem = 1; problem <= pcnt; problem++)
+ {
+ printf("Problem %d:\n", problem);
+ solver_printprobleminfo(solv, problem);
+ printf("\n");
+ }
+ }
+ trans = solver_create_transaction(solv);
+ solver_free(solv);
+
+ if (debug)
+ transaction_print(trans);
+
+ queue_init(&dq);
+ transaction_installedresult(trans, &dq);
+ solv_sort(dq.elements, dq.count, sizeof(Id), sortfunc, pool);
+
+ ofp = stdout;
+ if (cudfout && ((ofp = fopen(cudfout, "w")) == 0))
+ {
+ perror(cudfout);
+ exit(1);
+ }
+ for (i = 0; i < dq.count; i++)
+ {
+ Solvable *s = pool_id2solvable(pool, dq.elements[i]);
+ fprintf(ofp, "package: %s\n", pool_id2str(pool, s->name));
+ fprintf(ofp, "version: %s\n", pool_id2str(pool, s->evr));
+ fprintf(ofp, "installed: true\n");
+ if (s->repo == pool->installed)
+ fprintf(ofp, "was-installed: true\n");
+ fprintf(ofp, "\n");
+ }
+ queue_free(&dq);
+ transaction_free(trans);
+ queue_free(&job);
+ pool_free(pool);
+ if (ofp != stdout)
+ {
+ if (fclose(ofp))
+ {
+ perror("fclose");
+ exit(1);
+ }
+ }
+ exit(0);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * deb2solv - create a solv file from one or multiple debs
+ *
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "util.h"
+#include "pool.h"
+#include "repo.h"
+#include "repo_deb.h"
+#include "repo_solv.h"
+#include "common_write.h"
+
+static char *
+fgets0(char *s, int size, FILE *stream)
+{
+ char *p = s;
+ int c;
+
+ while (--size > 0)
+ {
+ c = getc(stream);
+ if (c == EOF)
+ {
+ if (p == s)
+ return 0;
+ c = 0;
+ }
+ *p++ = c;
+ if (!c)
+ return s;
+ }
+ *p = 0;
+ return s;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char **debs = 0;
+ char *manifest = 0;
+ int manifest0 = 0;
+ int c, i, res, ndebs = 0;
+ Pool *pool = pool_create();
+ Repo *repo;
+ FILE *fp;
+ char buf[4096], *p;
+ const char *basefile = 0;
+
+ while ((c = getopt(argc, argv, "0b:m:")) >= 0)
+ {
+ switch(c)
+ {
+ case 'b':
+ basefile = optarg;
+ break;
+ case 'm':
+ manifest = optarg;
+ break;
+ case '0':
+ manifest0 = 1;
+ break;
+ default:
+ exit(1);
+ }
+ }
+ if (manifest)
+ {
+ if (!strcmp(manifest, "-"))
+ fp = stdin;
+ else if ((fp = fopen(manifest, "r")) == 0)
+ {
+ perror(manifest);
+ exit(1);
+ }
+ for (;;)
+ {
+ if (manifest0)
+ {
+ if (!fgets0(buf, sizeof(buf), fp))
+ break;
+ }
+ else
+ {
+ if (!fgets(buf, sizeof(buf), fp))
+ break;
+ if ((p = strchr(buf, '\n')) != 0)
+ *p = 0;
+ }
+ debs = solv_extend(debs, ndebs, 1, sizeof(char *), 15);
+ debs[ndebs++] = strdup(buf);
+ }
+ if (fp != stdin)
+ fclose(fp);
+ }
+ while (optind < argc)
+ {
+ debs = solv_extend(debs, ndebs, 1, sizeof(char *), 15);
+ debs[ndebs++] = strdup(argv[optind++]);
+ }
+ repo = repo_create(pool, "deb2solv");
+ repo_add_repodata(repo, 0);
+ res = 0;
+ for (i = 0; i < ndebs; i++)
+ {
+ if (repo_add_deb(repo, debs[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE) == 0)
+ {
+ fprintf(stderr, "deb2solv: %s\n", pool_errstr(pool));
+ res = 1;
+ }
+ }
+ repo_internalize(repo);
+ tool_write(repo, basefile, 0);
+ pool_free(pool);
+ for (c = 0; c < ndebs; c++)
+ free((char *)debs[c]);
+ solv_free(debs);
+ exit(res);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_deltainfoxml.h"
+#include "common_write.h"
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "deltainfoxml2solv [-a][-h][-n <attrname>]\n"
+ " reads a 'deltainfo.xml' file from <stdin> and writes a .solv file to <stdout>\n"
+ " -h : print help & exit\n"
+ " -n <name>: save attributes as <name>.attr\n"
+ );
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, flags = 0;
+ char *attrname = 0;
+
+ Pool *pool = pool_create();
+ Repo *repo = repo_create(pool, "<stdin>");
+
+ while ((c = getopt(argc, argv, "hn:")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'n':
+ attrname = optarg;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ if (repo_add_deltainfoxml(repo, stdin, flags))
+ {
+ fprintf(stderr, "deltainfoxml2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ tool_write(repo, 0, attrname);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_diskusagexml.h"
+#include "common_write.h"
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "diskusagexml2solv [-a][-h][-n <attrname>]\n"
+ " reads a 'diskusage.xml' file from <stdin> and writes a .solv file to <stdout>\n"
+ " -h : print help & exit\n"
+ " -n <name>: save attributes as <name>.attr\n"
+ );
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, flags = 0;
+ char *attrname = 0;
+
+ Pool *pool = pool_create();
+ Repo *repo = repo_create(pool, "<stdin>");
+
+ while ((c = getopt(argc, argv, "hn:")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'n':
+ attrname = optarg;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ if (repo_add_diskusagexml(repo, stdin, flags))
+ {
+ fprintf(stderr, "diskusagexml2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ tool_write(repo, 0, attrname);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+static int with_attr;
+static int dump_json;
+
+#include "pool.h"
+#include "chksum.h"
+#include "repo_solv.h"
+
+
+static int
+dump_attr(Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
+{
+ const char *keyname;
+ KeyValue *kvp;
+ int indent = 0;
+
+ keyname = pool_id2str(repo->pool, key->name);
+ for (kvp = kv; (kvp = kvp->parent) != 0; indent += 2)
+ printf(" ");
+ switch(key->type)
+ {
+ case REPOKEY_TYPE_ID:
+ if (data && data->localpool)
+ kv->str = stringpool_id2str(&data->spool, kv->id);
+ else
+ kv->str = pool_dep2str(repo->pool, kv->id);
+ printf("%s: %s\n", keyname, kv->str);
+ break;
+ case REPOKEY_TYPE_CONSTANTID:
+ printf("%s: %s\n", keyname, pool_dep2str(repo->pool, kv->id));
+ break;
+ case REPOKEY_TYPE_IDARRAY:
+ if (!kv->entry)
+ printf("%s:\n%*s", keyname, indent, "");
+ if (data && data->localpool)
+ printf(" %s\n", stringpool_id2str(&data->spool, kv->id));
+ else
+ printf(" %s\n", pool_dep2str(repo->pool, kv->id));
+ break;
+ case REPOKEY_TYPE_STR:
+ printf("%s: %s\n", keyname, kv->str);
+ break;
+ case REPOKEY_TYPE_VOID:
+ printf("%s: (void)\n", keyname);
+ break;
+ case REPOKEY_TYPE_U32:
+ case REPOKEY_TYPE_CONSTANT:
+ printf("%s: %u\n", keyname, kv->num);
+ break;
+ case REPOKEY_TYPE_NUM:
+ printf("%s: %llu\n", keyname, SOLV_KV_NUM64(kv));
+ break;
+ case REPOKEY_TYPE_BINARY:
+ if (kv->num)
+ printf("%s: %02x..%02x len %u\n", keyname, (unsigned char)kv->str[0], (unsigned char)kv->str[kv->num - 1], kv->num);
+ else
+ printf("%s: len 0\n", keyname);
+ break;
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ if (!kv->entry)
+ printf("%s:\n%*s", keyname, indent, "");
+ printf(" %s %u %u\n", repodata_dir2str(data, kv->id, 0), kv->num, kv->num2);
+ break;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ if (!kv->entry)
+ printf("%s:\n%*s", keyname, indent, "");
+ printf(" %s\n", repodata_dir2str(data, kv->id, kv->str));
+ break;
+ case REPOKEY_TYPE_FIXARRAY:
+ case REPOKEY_TYPE_FLEXARRAY:
+ if (!kv->entry)
+ printf("%s:\n", keyname);
+ else
+ printf("\n");
+ break;
+ default:
+ if (solv_chksum_len(key->type))
+ {
+ printf("%s: %s (%s)\n", keyname, repodata_chk2str(data, key->type, (unsigned char *)kv->str), solv_chksum_type2str(key->type));
+ break;
+ }
+ printf("%s: ?\n", keyname);
+ break;
+ }
+ return 0;
+}
+
+static const char *
+jsonstring(Pool *pool, const char *s)
+{
+ int needed = 0;
+ const unsigned char *s1;
+ char *r, *rp;
+
+ for (s1 = (const unsigned char *)s; *s1; s1++)
+ {
+ if (*s1 < 32)
+ needed += *s1 == '\n' ? 2 : 6;
+ else if (*s1 == '\\' || *s1 == '\"')
+ needed += 2;
+ else
+ needed++;
+ }
+ r = rp = pool_alloctmpspace(pool, needed + 3);
+ *rp++ = '\"';
+ for (s1 = (const unsigned char *)s; *s1; s1++)
+ {
+ if (*s1 < 32)
+ {
+ int x;
+ if (*s1 == '\n')
+ {
+ *rp++ = '\\';
+ *rp++ = 'n';
+ continue;
+ }
+ *rp++ = '\\';
+ *rp++ = 'u';
+ *rp++ = '0';
+ *rp++ = '0';
+ x = *s1 / 16;
+ *rp++ = (x < 10 ? '0' : 'a' - 10) + x;
+ x = *s1 & 15;
+ *rp++ = (x < 10 ? '0' : 'a' - 10) + x;
+ }
+ else if (*s1 == '\\' || *s1 == '\"')
+ {
+ *rp++ = '\\';
+ *rp++ = *s1;
+ }
+ else
+ *rp++ = *s1;
+ }
+ *rp++ = '\"';
+ *rp = 0;
+ return r;
+}
+
+struct cbdata {
+ unsigned char *first;
+ int nfirst;
+ int baseindent;
+};
+
+static int
+dump_attr_json(Repo *repo, Repodata *data, Repokey *key, KeyValue *kv, struct cbdata *cbdata)
+{
+ Pool *pool = repo->pool;
+ const char *keyname;
+ KeyValue *kvp;
+ int indent = cbdata->baseindent;
+ int isarray = 0;
+ const char *str;
+ int depth = 0;
+
+ keyname = pool_id2str(repo->pool, key->name);
+ for (kvp = kv; (kvp = kvp->parent) != 0; indent += 4)
+ depth++;
+ if (cbdata->nfirst < depth + 1)
+ {
+ cbdata->first = solv_realloc(cbdata->first, depth + 16);
+ memset(cbdata->first + cbdata->nfirst, 0, depth + 16 - cbdata->nfirst);
+ cbdata->nfirst = depth + 16;
+ }
+ switch(key->type)
+ {
+ case REPOKEY_TYPE_IDARRAY:
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ isarray = 1;
+ break;
+ case REPOKEY_TYPE_FIXARRAY:
+ case REPOKEY_TYPE_FLEXARRAY:
+ isarray = 2;
+ break;
+ default:
+ break;
+ }
+ if (!isarray || !kv->entry)
+ {
+ if (cbdata->first[depth])
+ printf(",\n");
+ printf("%*s%s: ", indent, "", jsonstring(pool, keyname));
+ cbdata->first[depth] = 1;
+ }
+ if (isarray == 1 && !kv->entry)
+ printf("[\n%*s", indent + 2, "");
+ else if (isarray == 1 && kv->entry)
+ printf("%*s", indent + 2, "");
+ switch(key->type)
+ {
+ case REPOKEY_TYPE_ID:
+ if (data && data->localpool)
+ str = stringpool_id2str(&data->spool, kv->id);
+ else
+ str = pool_dep2str(repo->pool, kv->id);
+ printf("%s", jsonstring(pool, str));
+ break;
+ case REPOKEY_TYPE_CONSTANTID:
+ str = pool_dep2str(repo->pool, kv->id);
+ printf("%s", jsonstring(pool, str));
+ break;
+ case REPOKEY_TYPE_IDARRAY:
+ if (data && data->localpool)
+ str = stringpool_id2str(&data->spool, kv->id);
+ else
+ str = pool_dep2str(repo->pool, kv->id);
+ printf("%s", jsonstring(pool, str));
+ break;
+ case REPOKEY_TYPE_STR:
+ str = kv->str;
+ printf("%s", jsonstring(pool, str));
+ break;
+ case REPOKEY_TYPE_VOID:
+ printf("null");
+ break;
+ case REPOKEY_TYPE_U32:
+ case REPOKEY_TYPE_CONSTANT:
+ printf("%u", kv->num);
+ break;
+ case REPOKEY_TYPE_NUM:
+ printf("%llu", SOLV_KV_NUM64(kv));
+ break;
+ case REPOKEY_TYPE_BINARY:
+ printf("\"<binary>\"");
+ break;
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ printf("{\n");
+ printf("%*s \"dir\": %s,\n", indent, "", jsonstring(pool, repodata_dir2str(data, kv->id, 0)));
+ printf("%*s \"num1\": %u,\n", indent, "", kv->num);
+ printf("%*s \"num2\": %u\n", indent, "", kv->num2);
+ printf("%*s }", indent, "");
+ break;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ printf("%s", jsonstring(pool, repodata_dir2str(data, kv->id, kv->str)));
+ break;
+ case REPOKEY_TYPE_FIXARRAY:
+ case REPOKEY_TYPE_FLEXARRAY:
+ cbdata->first[depth + 1] = 0;
+ if (!kv->entry)
+ printf("[\n");
+ else
+ {
+ if (kv->eof != 2)
+ printf("\n%*s },\n", indent, "");
+ else
+ printf("\n%*s }\n", indent, "");
+ }
+ if (kv->eof != 2)
+ printf("%*s {\n", indent, "");
+ else
+ printf("%*s]", indent, "");
+ break;
+ default:
+ if (solv_chksum_len(key->type))
+ {
+ printf("{\n");
+ printf("%*s \"value\": %s,\n", indent, "", jsonstring(pool, repodata_chk2str(data, key->type, (unsigned char *)kv->str)));
+ printf("%*s \"type\": %s\n", indent, "", jsonstring(pool, solv_chksum_type2str(key->type)));
+ printf("%*s}", indent, "");
+ break;
+ }
+ printf("\"?\"");
+ break;
+ }
+ if (isarray == 1)
+ {
+ if (!kv->eof)
+ printf(",\n");
+ else
+ printf("\n%*s]", indent, "");
+ }
+ return 0;
+}
+
+static int
+dump_repodata_cb(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
+{
+ if (key->name == REPOSITORY_SOLVABLES)
+ return SEARCH_NEXT_SOLVABLE;
+ if (!dump_json)
+ return dump_attr(data->repo, data, key, kv);
+ else
+ return dump_attr_json(data->repo, data, key, kv, vcbdata);
+}
+
+static void
+dump_repodata(Repo *repo)
+{
+ int i;
+ Repodata *data;
+ if (repo->nrepodata == 0)
+ return;
+ printf("repo contains %d repodata sections:\n", repo->nrepodata - 1);
+ FOR_REPODATAS(repo, i, data)
+ {
+ unsigned int j;
+ printf("\nrepodata %d has %d keys, %d schemata\n", i, data->nkeys - 1, data->nschemata - 1);
+ for (j = 1; j < data->nkeys; j++)
+ printf(" %s (type %s size %d storage %d)\n", pool_id2str(repo->pool, data->keys[j].name), pool_id2str(repo->pool, data->keys[j].type), data->keys[j].size, data->keys[j].storage);
+ if (data->localpool)
+ printf(" localpool has %d strings, size is %d\n", data->spool.nstrings, data->spool.sstrings);
+ if (data->dirpool.ndirs)
+ printf(" localpool has %d directories\n", data->dirpool.ndirs);
+ printf("\n");
+ repodata_search(data, SOLVID_META, 0, SEARCH_ARRAYSENTINEL|SEARCH_SUB, dump_repodata_cb, 0);
+ }
+ printf("\n");
+}
+
+static void
+dump_repodata_json(Repo *repo, struct cbdata *cbdata)
+{
+ int i;
+ Repodata *data;
+ if (repo->nrepodata == 0)
+ return;
+ cbdata->baseindent = 6;
+ FOR_REPODATAS(repo, i, data)
+ repodata_search(data, SOLVID_META, 0, SEARCH_ARRAYSENTINEL|SEARCH_SUB, dump_repodata_cb, cbdata);
+}
+
+/*
+ * dump all attributes for Id <p>
+ */
+
+void
+dump_solvable(Repo *repo, Id p, struct cbdata *cbdata)
+{
+ Dataiterator di;
+ dataiterator_init(&di, repo->pool, repo, p, 0, 0, SEARCH_ARRAYSENTINEL|SEARCH_SUB);
+ if (cbdata && cbdata->first)
+ cbdata->first[0] = 0;
+ if (cbdata)
+ cbdata->baseindent = 10;
+ while (dataiterator_step(&di))
+ {
+ if (!dump_json)
+ dump_attr(repo, di.data, di.key, &di.kv);
+ else
+ dump_attr_json(repo, di.data, di.key, &di.kv, cbdata);
+ }
+ dataiterator_free(&di);
+}
+
+static int
+loadcallback(Pool *pool, Repodata *data, void *vdata)
+{
+ FILE *fp = 0;
+ int r;
+ const char *location;
+
+ location = repodata_lookup_str(data, SOLVID_META, REPOSITORY_LOCATION);
+ if (!location || !with_attr)
+ return 0;
+ fprintf(stderr, "[Loading SOLV file %s]\n", location);
+ fp = fopen (location, "r");
+ if (!fp)
+ {
+ perror(location);
+ return 0;
+ }
+ r = repo_add_solv(data->repo, fp, REPO_USE_LOADING|REPO_LOCALPOOL);
+ fclose(fp);
+ return !r ? 1 : 0;
+}
+
+
+static void
+usage(int status)
+{
+ fprintf( stderr, "\nUsage:\n"
+ "dumpsolv [-a] [-j] [<solvfile>]\n"
+ " -a read attributes.\n"
+ " -j dump json format.\n"
+ );
+ exit(status);
+}
+
+int main(int argc, char **argv)
+{
+ Repo *repo;
+ Pool *pool;
+ int c, i, j, n;
+ Solvable *s;
+
+ pool = pool_create();
+ pool_setloadcallback(pool, loadcallback, 0);
+
+ while ((c = getopt(argc, argv, "haj")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'a':
+ with_attr = 1;
+ break;
+ case 'j':
+ dump_json = 1;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ if (!dump_json)
+ pool_setdebuglevel(pool, 1);
+ if (dump_json)
+ pool->debugmask |= SOLV_DEBUG_TO_STDERR;
+ for (; optind < argc; optind++)
+ {
+ if (freopen(argv[optind], "r", stdin) == 0)
+ {
+ perror(argv[optind]);
+ exit(1);
+ }
+ repo = repo_create(pool, argv[optind]);
+ if (repo_add_solv(repo, stdin, 0))
+ {
+ fprintf(stderr, "could not read repository: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+ if (!pool->urepos)
+ {
+ repo = repo_create(pool, argc != 1 ? argv[1] : "<stdin>");
+ if (repo_add_solv(repo, stdin, 0))
+ {
+ fprintf(stderr, "could not read repository: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+
+ if (dump_json)
+ {
+ int openrepo = 0;
+ struct cbdata cbdata;
+
+ memset(&cbdata, 0, sizeof(cbdata));
+ printf("{\n");
+ printf(" \"repositories\": [\n");
+ FOR_REPOS(j, repo)
+ {
+ int open = 0;
+
+ if (openrepo)
+ printf("\n },");
+ printf(" {\n");
+ openrepo = 1;
+ if (cbdata.first)
+ cbdata.first[0] = 0;
+ dump_repodata_json(repo, &cbdata);
+ if (cbdata.first[0])
+ printf(",\n");
+ printf(" \"solvables\": [\n");
+ FOR_REPO_SOLVABLES(repo, i, s)
+ {
+ if (open)
+ printf("\n },\n");
+ printf(" {\n");
+ open = 1;
+ dump_solvable(repo, i, &cbdata);
+ }
+ if (open)
+ printf("\n }\n");
+ printf(" ]\n");
+ }
+ if (openrepo)
+ printf(" }\n");
+ printf(" ]\n");
+ printf("}\n");
+ solv_free(cbdata.first);
+ }
+ else
+ {
+ printf("pool contains %d strings, %d rels, string size is %d\n", pool->ss.nstrings, pool->nrels, pool->ss.sstrings);
+ n = 0;
+ FOR_REPOS(j, repo)
+ {
+ dump_repodata(repo);
+ printf("repo %d contains %d solvables\n", j, repo->nsolvables);
+ printf("repo start: %d end: %d\n", repo->start, repo->end);
+ FOR_REPO_SOLVABLES(repo, i, s)
+ {
+ n++;
+ printf("\n");
+ printf("solvable %d (%d):\n", n, i);
+ dump_solvable(repo, i, 0);
+ }
+ }
+ }
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/* vim: sw=2 et
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "solver.h"
+#include "solverdebug.h"
+#include "hash.h"
+#include "repo_rpmdb.h"
+#include "pool_fileconflicts.h"
+
+static void *
+iterate_handle(Pool *pool, Id p, void *cbdata)
+{
+ Solvable *s = pool->solvables + p;
+ Id rpmdbid;
+ void *handle;
+
+ if (!s->repo->rpmdbid)
+ return 0;
+ rpmdbid = s->repo->rpmdbid[p - s->repo->start];
+ if (!rpmdbid)
+ return 0;
+ handle = rpm_byrpmdbid(cbdata, rpmdbid);
+ if (!handle)
+ fprintf(stderr, "rpm_byrpmdbid: %s\n", pool_errstr(pool));
+ return handle;
+}
+
+int main(int argc, char **argv)
+{
+ Pool *pool;
+ Repo *installed;
+ Solvable *s;
+ Id p;
+ int i;
+ Queue todo, conflicts;
+ void *state = 0;
+ char *rootdir = 0;
+
+ if (argc == 3 && !strcmp(argv[1], "--root"))
+ rootdir = argv[2];
+ pool = pool_create();
+ if (rootdir)
+ pool_set_rootdir(pool, rootdir);
+ pool_setdebuglevel(pool, 1);
+ installed = repo_create(pool, "@System");
+ pool_set_installed(pool, installed);
+ if (repo_add_rpmdb(installed, 0, REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "findfileconflicts: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ queue_init(&todo);
+ queue_init(&conflicts);
+ FOR_REPO_SOLVABLES(installed, p, s)
+ queue_push(&todo, p);
+ state = rpm_state_create(pool, pool_get_rootdir(pool));
+ pool_findfileconflicts(pool, &todo, 0, &conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &iterate_handle, state);
+ rpm_state_free(state);
+ queue_free(&todo);
+ for (i = 0; i < conflicts.count; i += 6)
+ {
+ if (conflicts.elements[i] != conflicts.elements[i + 3])
+ printf("%s - %s: %s[%s] %s[%s]\n", pool_id2str(pool, conflicts.elements[i]), pool_id2str(pool, conflicts.elements[i + 3]), pool_solvid2str(pool, conflicts.elements[i + 1]), pool_id2str(pool, conflicts.elements[i + 2]), pool_solvid2str(pool, conflicts.elements[i + 4]), pool_id2str(pool, conflicts.elements[i + 5]));
+ else
+ printf("%s: %s[%s] %s[%s]\n", pool_id2str(pool, conflicts.elements[i]), pool_solvid2str(pool, conflicts.elements[i + 1]), pool_id2str(pool, conflicts.elements[i + 2]), pool_solvid2str(pool, conflicts.elements[i + 4]), pool_id2str(pool, conflicts.elements[i + 5]));
+ }
+ if (conflicts.count)
+ {
+ Queue job;
+ int problemcnt;
+
+ queue_init(&job);
+ pool_add_fileconflicts_deps(pool, &conflicts);
+ pool_addfileprovides(pool);
+ pool_createwhatprovides(pool);
+ pool_setdebuglevel(pool, 0);
+ Solver *solv = solver_create(pool);
+ queue_push2(&job, SOLVER_VERIFY|SOLVER_SOLVABLE_ALL, 0);
+#if 0
+ solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);
+#endif
+ problemcnt = solver_solve(solv, &job);
+ if (problemcnt)
+ solver_printallsolutions(solv);
+ else
+ {
+ Transaction *trans = solver_create_transaction(solv);
+ transaction_print(trans);
+ transaction_free(trans);
+ }
+ queue_free(&job);
+ solver_free(solv);
+ }
+ queue_free(&conflicts);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * helix2solv.c
+ *
+ * parse 'helix' type xml and write out .solv file
+ *
+ * reads from stdin
+ * writes to stdout
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "repo_helix.h"
+#include "common_write.h"
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool = pool_create();
+ Repo *repo = repo_create(pool, "<stdin>");
+ if (repo_add_helix(repo, stdin, 0))
+ {
+ fprintf(stderr, "helix2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ tool_write(repo, 0, 0);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/* vim: sw=2 et cino=>4,n-2,{1s
+ */
+
+/*
+ * Copyright (c) 2009-2015, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <zlib.h>
+
+#include "pool.h"
+#include "poolarch.h"
+#include "repo_solv.h"
+#ifdef ENABLE_SUSEREPO
+#include "repo_susetags.h"
+#endif
+#ifdef ENABLE_RPMMD
+#include "repo_rpmmd.h"
+#endif
+#ifdef ENABLE_DEBIAN
+#include "repo_deb.h"
+#endif
+#ifdef ENABLE_ARCHREPO
+#include "repo_arch.h"
+#endif
+#include "solver.h"
+#include "solv_xfopen.h"
+
+
+void
+usage(char** argv)
+{
+ printf("Usage:\n%s: <arch> [options..] repo [--nocheck repo]...\n"
+ "\t--exclude <pattern>\twhitespace-separated list of (sub-)"
+ "packagenames to ignore\n"
+ "\t--withobsoletes\t\tCheck for obsoletes on packages contained in repos\n"
+ "\t--nocheck\t\tDo not warn about all following repos (only use them to fulfill dependencies)\n"
+ "\t--withsrc\t\tAlso check dependencies of src.rpm\n\n"
+ , argv[0]);
+ exit(1);
+}
+
+static int
+strlen_comp(const char *str)
+{
+ size_t l = strlen(str);
+ if (l > 3 && !strcmp(str + l - 3, ".gz"))
+ return l - 3;
+ if (l > 3 && !strcmp(str + l - 3, ".xz"))
+ return l - 3;
+ if (l > 4 && !strcmp(str + l - 4, ".bz2"))
+ return l - 4;
+ if (l > 5 && !strcmp(str + l - 4, ".lzma"))
+ return l - 5;
+ return l;
+}
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool;
+ Solver *solv;
+ Repo *repo;
+ Queue job;
+ Queue rids;
+ Queue cand;
+ char *arch, *exclude_pat;
+ int i, j;
+ Id p;
+ Id archid, noarchid;
+ Id rpmrel;
+#ifndef DEBIAN
+ Id rpmid;
+#endif
+ int status = 0;
+ int nocheck = 0;
+ int withsrc = 0;
+ int obsoletepkgcheck = 0;
+
+ exclude_pat = 0;
+ if (argc < 3)
+ usage(argv);
+
+ arch = argv[1];
+ pool = pool_create();
+ pool_setarch(pool, arch);
+ noarchid = pool->solvables[SYSTEMSOLVABLE].arch;
+ for (i = 2; i < argc; i++)
+ {
+ FILE *fp;
+ int r, l;
+
+ if (!strcmp(argv[i], "--withsrc"))
+ {
+ withsrc++;
+ continue;
+ }
+ if (!strcmp(argv[i], "--withobsoletes"))
+ {
+ obsoletepkgcheck++;
+ continue;
+ }
+ if (!strcmp(argv[i], "--nocheck"))
+ {
+ if (!nocheck)
+ nocheck = pool->nsolvables;
+ continue;
+ }
+ if (!strcmp(argv[i], "--exclude"))
+ {
+ if (i + 1 >= argc)
+ {
+ printf("--exclude needs a whitespace separated list of substrings as parameter\n");
+ exit(1);
+ }
+ exclude_pat = argv[i + 1];
+ ++i;
+ continue;
+ }
+ l = strlen_comp(argv[i]);
+ if (!strcmp(argv[i], "-"))
+ fp = stdin;
+ else if ((fp = solv_xfopen(argv[i], 0)) == 0)
+ {
+ perror(argv[i]);
+ exit(1);
+ }
+ repo = repo_create(pool, argv[i]);
+ r = 0;
+ if (0)
+ {
+ }
+#ifdef ENABLE_SUSEREPO
+ else if (l >= 8 && !strncmp(argv[i] + l - 8, "packages", 8))
+ {
+ r = repo_add_susetags(repo, fp, 0, 0, 0);
+ }
+#endif
+#ifdef ENABLE_RPMMD
+ else if (l >= 11 && !strncmp(argv[i] + l - 11, "primary.xml", 11))
+ {
+ r = repo_add_rpmmd(repo, fp, 0, 0);
+ if (!r && i + 1 < argc)
+ {
+ l = strlen_comp(argv[i + 1]);
+ if (l >= 13 && !strncmp(argv[i + 1] + l - 13, "filelists.xml", 13))
+ {
+ i++;
+ fclose(fp);
+ if ((fp = solv_xfopen(argv[i], 0)) == 0)
+ {
+ perror(argv[i]);
+ exit(1);
+ }
+ r = repo_add_rpmmd(repo, fp, 0, REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+ }
+ }
+ }
+#endif
+#ifdef ENABLE_DEBIAN
+ else if (l >= 8 && !strncmp(argv[i] + l - 8, "Packages", 8))
+ {
+ r = repo_add_debpackages(repo, fp, 0);
+ }
+#endif
+#ifdef ENABLE_ARCHREPO
+ else if (l >= 7 && (!strncmp(argv[i] + l - 7, ".db.tar", 7)))
+ {
+ r = repo_add_arch_repo(repo, fp, 0);
+ }
+#endif
+ else
+ r = repo_add_solv(repo, fp, 0);
+ if (r)
+ {
+ fprintf(stderr, "could not add repo %s: %s\n", argv[i], pool_errstr(pool));
+ exit(1);
+ }
+ if (fp != stdin)
+ fclose(fp);
+ }
+ pool_addfileprovides(pool);
+ pool_createwhatprovides(pool);
+ archid = pool_str2id(pool, arch, 0);
+#ifndef DEBIAN
+ rpmid = pool_str2id(pool, "rpm", 0);
+ rpmrel = 0;
+ if (rpmid && archid)
+ {
+ for (p = 1; p < pool->nsolvables; p++)
+ {
+ Solvable *s = pool->solvables + p;
+ if (s->name == rpmid && s->arch == archid && pool_installable(pool, s))
+ break;
+ }
+ if (p < pool->nsolvables)
+ rpmrel = pool_rel2id(pool, rpmid, archid, REL_ARCH, 1);
+ }
+#else
+ rpmrel = 0;
+#endif
+
+ queue_init(&job);
+ queue_init(&rids);
+ queue_init(&cand);
+ for (p = 1; p < (nocheck ? nocheck : pool->nsolvables); p++)
+ {
+ Solvable *s = pool->solvables + p;
+ if (!s->repo)
+ continue;
+ if (withsrc && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC))
+ {
+ queue_push(&cand, p);
+ continue;
+ }
+ if (!pool_installable(pool, s))
+ continue;
+ if (archid && s->arch != archid && s->arch != noarchid)
+ {
+ /* check if we will conflict with a infarch rule, if yes,
+ * don't bother checking the package */
+ Id rp, rpp;
+ FOR_PROVIDES(rp, rpp, s->name)
+ {
+ if (pool->solvables[rp].name != s->name)
+ continue;
+ if (pool->solvables[rp].arch == archid)
+ break;
+ }
+ if (rp)
+ continue;
+ }
+ queue_push(&cand, p);
+ }
+ if (obsoletepkgcheck)
+ {
+ int obsoleteusesprovides = pool_get_flag(pool, POOL_FLAG_OBSOLETEUSESPROVIDES);
+ int obsoleteusescolors = pool_get_flag(pool, POOL_FLAG_OBSOLETEUSESCOLORS);
+
+ for (i = 0; i < cand.count; i++)
+ {
+ Solvable *s;
+ s = pool->solvables + cand.elements[i];
+
+ if (s->obsoletes)
+ {
+ Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+
+ while ((obs = *obsp++) != 0)
+ {
+ Id op, opp;
+ FOR_PROVIDES(op, opp, obs)
+ {
+ Solvable *os = pool->solvables + op;
+ if (nocheck && op >= nocheck)
+ continue;
+ if (solvable_identical(s, os))
+ continue;
+ if (!obsoleteusesprovides && !pool_match_nevr(pool, os, obs))
+ continue;
+ if (obsoleteusescolors && !pool_colormatch(pool, s, os))
+ continue;
+ status = 2;
+ printf("can't install %s:\n", pool_solvid2str(pool, op));
+ printf(" package is obsoleted by %s\n", pool_solvable2str(pool, s));
+ }
+ }
+ }
+ }
+ }
+
+ solv = solver_create(pool);
+
+ /* prune cand by doing weak installs */
+ while (cand.count)
+ {
+ queue_empty(&job);
+ for (i = 0; i < cand.count; i++)
+ {
+ p = cand.elements[i];
+ queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK, p);
+ }
+ if (rpmrel)
+ queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_NAME, rpmrel);
+ solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1);
+ solver_solve(solv, &job);
+ /* prune... */
+ for (i = j = 0; i < cand.count; i++)
+ {
+ p = cand.elements[i];
+ if (solver_get_decisionlevel(solv, p) <= 0)
+ {
+ cand.elements[j++] = p;
+ continue;
+ }
+ }
+ cand.count = j;
+ if (i == j)
+ break;
+ }
+
+ /* now check every candidate */
+ for (i = 0; i < cand.count; i++)
+ {
+ Solvable *s;
+ int problemcount;
+
+ p = cand.elements[i];
+ if (exclude_pat)
+ {
+ char *ptr, *save = 0, *pattern;
+ int match = 0;
+ pattern = solv_strdup(exclude_pat);
+
+ for (ptr = strtok_r(pattern, " ", &save);
+ ptr;
+ ptr = strtok_r(NULL, " ", &save))
+ {
+ if (*ptr && strstr(pool_solvid2str(pool, p), ptr))
+ {
+ match = 1;
+ break;
+ }
+ }
+ solv_free(pattern);
+ if (match)
+ continue;
+ }
+ s = pool->solvables + p;
+ queue_empty(&job);
+ queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE, p);
+ if (rpmrel)
+ queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_NAME, rpmrel);
+ solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1);
+ problemcount = solver_solve(solv, &job);
+ if (problemcount)
+ {
+ Id problem = 0;
+ Solvable *s2;
+
+ status = 1;
+ printf("can't install %s:\n", pool_solvable2str(pool, s));
+ while ((problem = solver_next_problem(solv, problem)) != 0)
+ {
+ solver_findallproblemrules(solv, problem, &rids);
+ for (j = 0; j < rids.count; j++)
+ {
+ Id probr = rids.elements[j];
+ int k;
+ Queue rinfo;
+ queue_init(&rinfo);
+
+ solver_allruleinfos(solv, probr, &rinfo);
+ for (k = 0; k < rinfo.count; k += 4)
+ {
+ Id dep, source, target;
+ source = rinfo.elements[k + 1];
+ target = rinfo.elements[k + 2];
+ dep = rinfo.elements[k + 3];
+ switch (rinfo.elements[k])
+ {
+ case SOLVER_RULE_DISTUPGRADE:
+ break;
+ case SOLVER_RULE_INFARCH:
+ s = pool_id2solvable(pool, source);
+ printf(" %s has inferior architecture\n", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_RULE_UPDATE:
+ s = pool_id2solvable(pool, source);
+ printf(" %s can not be updated\n", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_RULE_JOB:
+ case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
+ case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
+ case SOLVER_RULE_JOB_UNSUPPORTED:
+ break;
+ case SOLVER_RULE_RPM:
+ printf(" some dependency problem\n");
+ break;
+ case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
+ printf(" nothing provides requested %s\n", pool_dep2str(pool, dep));
+ break;
+ case SOLVER_RULE_RPM_NOT_INSTALLABLE:
+ s = pool_id2solvable(pool, source);
+ printf(" package %s is not installable\n", pool_solvable2str(pool, s));
+ break;
+ case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
+ s = pool_id2solvable(pool, source);
+ printf(" nothing provides %s needed by %s\n", pool_dep2str(pool, dep), pool_solvable2str(pool, s));
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (!ISRELDEP(rd->name))
+ {
+ Id rp, rpp;
+ FOR_PROVIDES(rp, rpp, rd->name)
+ printf(" (we have %s)\n", pool_solvable2str(pool, pool->solvables + rp));
+ }
+ }
+ break;
+ case SOLVER_RULE_RPM_SAME_NAME:
+ s = pool_id2solvable(pool, source);
+ s2 = pool_id2solvable(pool, target);
+ printf(" cannot install both %s and %s\n", pool_solvable2str(pool, s), pool_solvable2str(pool, s2));
+ break;
+ case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
+ s = pool_id2solvable(pool, source);
+ s2 = pool_id2solvable(pool, target);
+ printf(" package %s conflicts with %s provided by %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep), pool_solvable2str(pool, s2));
+ break;
+ case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
+ s = pool_id2solvable(pool, source);
+ s2 = pool_id2solvable(pool, target);
+ printf(" package %s obsoletes %s provided by %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep), pool_solvable2str(pool, s2));
+ break;
+ case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
+ s = pool_id2solvable(pool, source);
+ printf(" package %s requires %s, but none of the providers can be installed\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep));
+ break;
+ case SOLVER_RULE_RPM_SELF_CONFLICT:
+ s = pool_id2solvable(pool, source);
+ printf(" package %s conflicts with %s provided by itself\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ solver_free(solv);
+ exit(status);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * mdk2solv.c
+ *
+ * parse Mandriva/Mageie synthesis file
+ *
+ * reads from stdin
+ * writes to stdout
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_mdk.h"
+#include "solv_xfopen.h"
+#include "common_write.h"
+
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "mdk2solv [-i <infoxml>]\n"
+ " reads a 'synthesis' repository from <stdin> and writes a .solv file to <stdout>\n"
+ " -i : info.xml file for extra attributes\n"
+ " -f : files.xml file for extra attributes\n"
+ " -h : print help & exit\n"
+ );
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool;
+ Repo *repo;
+ char *infofile = 0, *filesfile = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "hi:f:")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'i':
+ infofile = optarg;
+ break;
+ case 'f':
+ filesfile = optarg;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ pool = pool_create();
+ repo = repo_create(pool, "<stdin>");
+ if (repo_add_mdk(repo, stdin, REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "mdk2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ if (infofile)
+ {
+ FILE *fp = solv_xfopen(infofile, "r");
+ if (!fp)
+ {
+ perror(infofile);
+ exit(1);
+ }
+ if (repo_add_mdk_info(repo, fp, REPO_EXTEND_SOLVABLES | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "mdk2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+ if (filesfile)
+ {
+ FILE *fp = solv_xfopen(filesfile, "r");
+ if (!fp)
+ {
+ perror(filesfile);
+ exit(1);
+ }
+ if (repo_add_mdk_info(repo, fp, REPO_EXTEND_SOLVABLES | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "mdk2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+ repo_internalize(repo);
+ tool_write(repo, 0, 0);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * mergesolv
+ *
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pool.h"
+#include "repo_solv.h"
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+#include "common_write.h"
+
+static void
+usage()
+{
+ fprintf(stderr, "\nUsage:\n"
+ "mergesolv [file] [file] [...]\n"
+ " merges multiple solv files into one and writes it to stdout\n"
+ );
+ exit(0);
+}
+
+static int
+loadcallback (Pool *pool, Repodata *data, void *vdata)
+{
+ FILE *fp;
+ const char *location = repodata_lookup_str(data, SOLVID_META, REPOSITORY_LOCATION);
+ int r;
+
+ if (!location)
+ return 0;
+ fprintf(stderr, "Loading SOLV file %s\n", location);
+ fp = fopen (location, "r");
+ if (!fp)
+ {
+ perror(location);
+ return 0;
+ }
+ r = repo_add_solv(data->repo, fp, REPO_USE_LOADING|REPO_LOCALPOOL);
+ fclose(fp);
+ return r ? 0 : 1;
+}
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool;
+ Repo *repo;
+ const char *basefile = 0;
+ int with_attr = 0;
+#ifdef SUSE
+ int add_auto = 0;
+#endif
+ int c;
+
+ pool = pool_create();
+ repo = repo_create(pool, "<mergesolv>");
+
+ while ((c = getopt(argc, argv, "ahb:X")) >= 0)
+ {
+ switch (c)
+ {
+ case 'h':
+ usage();
+ break;
+ case 'a':
+ with_attr = 1;
+ break;
+ case 'b':
+ basefile = optarg;
+ break;
+ case 'X':
+#ifdef SUSE
+ add_auto = 1;
+#endif
+ break;
+ default:
+ usage();
+ exit(1);
+ }
+ }
+ if (with_attr)
+ pool_setloadcallback(pool, loadcallback, 0);
+
+ for (; optind < argc; optind++)
+ {
+ FILE *fp;
+ if ((fp = fopen(argv[optind], "r")) == NULL)
+ {
+ perror(argv[optind]);
+ exit(1);
+ }
+ if (repo_add_solv(repo, fp, 0))
+ {
+ fprintf(stderr, "repo %s: %s\n", argv[optind], pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+#ifdef SUSE
+ if (add_auto)
+ repo_add_autopattern(repo, 0);
+#endif
+ tool_write(repo, basefile, 0);
+ pool_free(pool);
+ return 0;
+}
--- /dev/null
+/* vim: sw=2 et
+ */
+
+/*
+ * Copyright (c) 2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <zlib.h>
+
+#include "pool.h"
+#include "evr.h"
+#include "poolarch.h"
+#include "repo_solv.h"
+#ifdef ENABLE_SUSEREPO
+#include "repo_susetags.h"
+#endif
+#ifdef ENABLE_RPMMD
+#include "repo_updateinfoxml.h"
+#include "repo_rpmmd.h"
+#endif
+#include "solver.h"
+#include "solverdebug.h"
+
+#include "solv_xfopen.h"
+
+void
+showproblems(Solver *solv, Solvable *s, Queue *cand, Queue *badguys)
+{
+ Pool *pool = solv->pool;
+ Queue rids, rinfo;
+ Id problem = 0;
+ int jj;
+ int rerun = 0;
+
+ queue_init(&rids);
+ queue_init(&rinfo);
+ printf("can't install %s:\n", pool_solvable2str(pool, s));
+ while ((problem = solver_next_problem(solv, problem)) != 0)
+ {
+ solver_findallproblemrules(solv, problem, &rids);
+ for (jj = 0; jj < rids.count; jj++)
+ {
+ Id probr = rids.elements[jj];
+ int k, l;
+
+ queue_empty(&rinfo);
+ solver_allruleinfos(solv, probr, &rinfo);
+ for (k = 0; k < rinfo.count; k += 4)
+ {
+ Id dep, source, target;
+ source = rinfo.elements[k + 1];
+ target = rinfo.elements[k + 2];
+ dep = rinfo.elements[k + 3];
+ switch (rinfo.elements[k])
+ {
+ case SOLVER_RULE_DISTUPGRADE:
+ break;
+ case SOLVER_RULE_INFARCH:
+ printf(" %s has inferior architecture\n", pool_solvid2str(pool, source));
+ break;
+ case SOLVER_RULE_UPDATE:
+ printf(" update rule for %s\n", pool_solvid2str(pool, source));
+ if (badguys)
+ queue_pushunique(badguys, source);
+ if (!cand)
+ break;
+ /* only drop update problem packages from cand so that we see all problems of this patch */
+ for (l = 0; l < cand->count; l++)
+ if (cand->elements[l] == source || cand->elements[l] == -source)
+ break;
+ if (l == cand->count)
+ break;
+ if (!rerun)
+ {
+ for (l = 0; l < cand->count; l++)
+ if (cand->elements[l] < 0)
+ cand->elements[l] = -cand->elements[l];
+ rerun = 1;
+ }
+ for (l = 0; l < cand->count; l++)
+ if (cand->elements[l] == source)
+ {
+ cand->elements[l] = -source;
+ }
+ break;
+ case SOLVER_RULE_JOB:
+ case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
+ case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
+ case SOLVER_RULE_JOB_UNSUPPORTED:
+ break;
+ case SOLVER_RULE_RPM:
+ printf(" some dependency problem\n");
+ break;
+ case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
+ printf(" nothing provides requested %s\n", pool_dep2str(pool, dep));
+ break;
+ case SOLVER_RULE_RPM_NOT_INSTALLABLE:
+ printf(" package %s is not installable\n", pool_solvid2str(pool, source));
+ break;
+ case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
+ printf(" nothing provides %s needed by %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, source));
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (!ISRELDEP(rd->name))
+ {
+ Id rp, rpp;
+ FOR_PROVIDES(rp, rpp, rd->name)
+ printf(" (we have %s)\n", pool_solvid2str(pool, rp));
+ }
+ }
+ break;
+ case SOLVER_RULE_RPM_SAME_NAME:
+ printf(" cannot install both %s and %s\n", pool_solvid2str(pool, source), pool_solvid2str(pool, target));
+ break;
+ case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
+ printf(" package %s conflicts with %s provided by %s\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep), pool_solvid2str(pool, target));
+ break;
+ case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
+ printf(" package %s obsoletes %s provided by %s\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep), pool_solvid2str(pool, target));
+ break;
+ case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
+ printf(" package %s requires %s, but none of the providers can be installed\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep));
+ break;
+ case SOLVER_RULE_RPM_SELF_CONFLICT:
+ printf(" package %s conflicts with %s provided by itself\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep));
+ break;
+ }
+ }
+ }
+ }
+ queue_free(&rids);
+ queue_free(&rinfo);
+}
+
+void
+toinst(Solver *solv, Repo *repo, Repo *instrepo)
+{
+ Pool *pool = solv->pool;
+ Queue q;
+ int k;
+ Id p;
+
+ queue_init(&q);
+ solver_get_decisionqueue(solv, &q);
+ for (k = 0; k < q.count; k++)
+ {
+ p = q.elements[k];
+ if (p < 0 || p == SYSTEMSOLVABLE)
+ continue;
+
+ /* printf(" toinstall %s\n", pool_solvid2str(pool, p));*/
+ /* oh my! */
+ pool->solvables[p].repo = instrepo;
+ }
+ queue_free(&q);
+}
+
+void
+dump_instrepo(Repo *instrepo, Pool *pool)
+{
+ Solvable *s;
+ Id p;
+
+ printf("instrepo..\n");
+ FOR_REPO_SOLVABLES(instrepo, p, s)
+ printf(" %s\n", pool_solvable2str(pool, s));
+ printf("done.\n");
+}
+
+void
+frominst(Solver *solv, Repo *repo, Repo *instrepo)
+{
+ Pool *pool = solv->pool;
+ int k;
+
+ for (k = 1; k < pool->nsolvables; k++)
+ if (pool->solvables[k].repo == instrepo)
+ pool->solvables[k].repo = repo;
+}
+
+void
+usage(char** argv)
+{
+
+ printf("%s: <arch> <patchnameprefix> [--install-available] [repos] [--updaterepos] [repos]...\n"
+ "\t --install-available: installation repository is available during update\n"
+ "\t repos: repository ending in\n"
+ "\t\tpackages, packages.gz, primary.xml.gz, updateinfo.xml.gz or .solv\n",
+ argv[0]);
+
+ exit(1);
+}
+
+typedef struct {
+ int updatestart;
+ int shown;
+ int status;
+ int install_available;
+ Repo *repo;
+ Repo *instrepo;
+} context_t;
+
+#define SHOW_PATCH(c) if (!(c)->shown++) printf("%s:\n", pool_solvable2str(pool, s));
+#define PERF_DEBUGGING 0
+
+static Pool *pool;
+
+void
+test_all_old_patches_included(context_t *c, Id pid)
+{
+ Id p, pp;
+ Id con, *conp;
+ Solvable *s = pool->solvables + pid;
+ /* Test 1: are all old patches included */
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ Solvable *s2 = pool->solvables + p;
+ Id con2, *conp2;
+
+ if (!s2->conflicts)
+ continue;
+ if (pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) <= 0)
+ continue;
+ conp2 = s2->repo->idarraydata + s2->conflicts;
+ while ((con2 = *conp2++) != 0)
+ {
+ Reldep *rd2, *rd;
+ if (!ISRELDEP(con2))
+ continue;
+ rd2 = GETRELDEP(pool, con2);
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ if (!ISRELDEP(con))
+ continue;
+ rd = GETRELDEP(pool, con);
+ if (rd->name == rd2->name)
+ break;
+ }
+ if (!con)
+ {
+ SHOW_PATCH(c);
+ printf(" %s contained %s\n", pool_solvable2str(pool, s2), pool_dep2str(pool, rd2->name));
+ }
+ else
+ {
+ if (pool_evrcmp(pool, rd->evr, rd2->evr, EVRCMP_COMPARE) < 0)
+ {
+ SHOW_PATCH(c);
+ printf(" %s required newer version %s-%s of %s-%s\n",
+ pool_solvable2str(pool, s2), pool_dep2str(pool, rd2->name), pool_dep2str(pool, rd2->evr),
+ pool_dep2str(pool, rd->name), pool_dep2str(pool, rd->evr));
+ }
+ }
+
+ }
+ }
+}
+
+void
+test_all_packages_installable(context_t *c, Id pid)
+{
+ Solver *solv;
+ Queue job;
+ Id p, pp;
+ Id con, *conp;
+ unsigned int now, solver_runs;
+ int i;
+ Solvable *s = pool->solvables + pid;
+
+ queue_init(&job);
+
+ now = solv_timems(0);
+ solver_runs = 0;
+
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ FOR_PROVIDES(p, pp, con)
+ {
+ queue_empty(&job);
+ queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
+ queue_push(&job, p);
+
+ /* also set up some minimal system */
+ queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
+ queue_push(&job, pool_str2id(pool, "rpm", 1));
+ queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
+ queue_push(&job, pool_str2id(pool, "aaa_base", 1));
+
+ solv = solver_create(pool);
+ /* solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1); */
+ ++solver_runs;
+ if (solver_solve(solv, &job))
+ {
+ c->status = 1;
+ printf("error installing original package\n");
+ showproblems(solv, s, 0, 0);
+ }
+ toinst(solv, c->repo, c->instrepo);
+ solver_free(solv);
+
+#if 0
+ dump_instrepo(instrepo, pool);
+
+#endif
+ if (!c->install_available)
+ {
+ queue_empty(&job);
+ for (i = 1; i < c->updatestart; i++)
+ {
+ if (pool->solvables[i].repo != c->repo || i == pid)
+ continue;
+ queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
+ queue_push(&job, i);
+ }
+ }
+ queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE);
+ queue_push(&job, pid);
+ solv = solver_create(pool);
+ /* solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1); */
+ ++solver_runs;
+ if (solver_solve(solv, &job))
+ {
+ c->status = 1;
+ showproblems(solv, s, 0, 0);
+ }
+ frominst(solv, c->repo, c->instrepo);
+ solver_free(solv);
+ }
+ }
+
+ if (PERF_DEBUGGING)
+ printf(" test_all_packages_installable took %d ms in %d runs\n", solv_timems(now), solver_runs);
+}
+
+void
+test_can_upgrade_all_packages(context_t *c, Id pid)
+{
+ Solver *solv;
+ Id p;
+ Id con, *conp;
+ Queue job;
+ Queue cand;
+ Queue badguys;
+ int i, j;
+ unsigned int now, solver_runs;
+ Solvable *s = pool->solvables + pid;
+
+ queue_init(&job);
+ queue_init(&cand);
+ queue_init(&badguys);
+
+ now = solv_timems(0);
+ solver_runs = 0;
+
+ /* Test 3: can we upgrade all packages? */
+ for (p = 1; p < pool->nsolvables; p++)
+ {
+ Solvable *s = pool->solvables + p;
+ if (!s->repo)
+ continue;
+ if (strchr(pool_id2str(pool, s->name), ':'))
+ continue; /* only packages, please */
+ if (!pool_installable(pool, s))
+ continue;
+ queue_push(&cand, p);
+ }
+ while (cand.count)
+ {
+ solv = solver_create(pool);
+ queue_empty(&job);
+ for (i = 0; i < badguys.count; i++)
+ {
+ queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE|SOLVER_WEAK);
+ queue_push(&job, badguys.elements[i]);
+ }
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
+ queue_push(&job, con);
+ }
+ for (i = 0; i < cand.count; i++)
+ {
+ p = cand.elements[i];
+ queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
+ queue_push(&job, p);
+ }
+ ++solver_runs;
+ solver_solve(solv, &job);
+#if 0
+ solver_printdecisions(solv);
+#endif
+ /* put packages into installed repo and prune them from cand */
+ toinst(solv, c->repo, c->instrepo);
+ for (i = 0; i < cand.count; i++)
+ {
+ p = cand.elements[i];
+ if (p > 0 && solver_get_decisionlevel(solv, p) > 0)
+ cand.elements[i] = -p; /* drop candidate */
+ }
+ solver_free(solv);
+
+ /* now the interesting part: test patch */
+ queue_empty(&job);
+ if (!c->install_available)
+ {
+ for (i = 1; i < c->updatestart; i++)
+ {
+ if (pool->solvables[i].repo != c->repo || i == pid)
+ continue;
+ queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
+ queue_push(&job, i);
+ }
+ }
+ queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE);
+ queue_push(&job, pid);
+ solv = solver_create(pool);
+ solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1);
+ ++solver_runs;
+ if (solver_solve(solv, &job))
+ {
+ c->status = 1;
+ showproblems(solv, s, &cand, &badguys);
+ }
+ frominst(solv, c->repo, c->instrepo);
+ solver_free(solv);
+ /* now drop all negative elements from cand */
+ for (i = j = 0; i < cand.count; i++)
+ {
+ if (cand.elements[i] < 0)
+ continue;
+ cand.elements[j++] = cand.elements[i];
+ }
+ if (i == j)
+ break; /* no progress */
+ cand.count = j;
+ }
+ if (PERF_DEBUGGING)
+ printf(" test_can_upgrade_all_packages took %d ms in %d runs\n", solv_timems(now), solver_runs);
+}
+
+void
+test_no_ga_package_fulfills_dependency(context_t *c, Id pid)
+{
+ Id con, *conp;
+ Solvable *s = pool->solvables + pid;
+
+ /* Test 4: no GA package fulfills patch dependency */
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ Reldep *rd;
+ Id rp, rpp;
+
+ if (!ISRELDEP(con))
+ continue;
+ rd = GETRELDEP(pool, con);
+ FOR_PROVIDES(rp, rpp, rd->name)
+ {
+ Solvable *s2 = pool_id2solvable(pool, rp);
+ if (rp < c->updatestart
+ && pool_evrcmp(pool, rd->evr, s2->evr, EVRCMP_COMPARE) < 0
+ && pool_match_nevr_rel(pool, s2, rd->name)
+ )
+ {
+ SHOW_PATCH(c);
+ printf(" conflict %s < %s satisfied by non-updated package %s\n",
+ pool_dep2str(pool, rd->name), pool_dep2str(pool, rd->evr), pool_solvable2str(pool, s2));
+ break;
+ }
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ char *arch, *mypatch;
+ const char *pname;
+ int l, r;
+ FILE *fp;
+ int i;
+ Id pid, p, pp;
+ int tests = 0;
+ context_t c;
+ static const char* langs[] = {"en"};
+
+ c.install_available = 0;
+ c.updatestart = 0;
+ c.status = 0;
+
+ if (argc <= 3)
+ usage(argv);
+
+ arch = argv[1];
+ pool = pool_create();
+ pool_setarch(pool, arch);
+ pool_set_languages(pool, langs, 1);
+
+#if 0
+ pool_setdebuglevel(pool, 2);
+#endif
+
+ mypatch = argv[2];
+
+ c.repo = repo_create(pool, 0);
+ c.instrepo = repo_create(pool, 0);
+ for (i = 3; i < argc; i++)
+ {
+ if (!strcmp(argv[i], "--updaterepos"))
+ {
+ c.updatestart = pool->nsolvables;
+ continue;
+ }
+
+ if (!strcmp(argv[i], "--install-available"))
+ {
+ c.install_available = 1;
+ continue;
+ }
+
+ l = strlen(argv[i]);
+ if (!strcmp(argv[i], "-"))
+ fp = stdin;
+ else if ((fp = solv_xfopen(argv[i], 0)) == 0)
+ {
+ perror(argv[i]);
+ exit(1);
+ }
+ r = 0;
+ if (0)
+ {
+ }
+#ifdef ENABLE_SUSEREPO
+ else if (l >= 8 && !strcmp(argv[i] + l - 8, "packages"))
+ {
+ r = repo_add_susetags(c.repo, fp, 0, 0, 0);
+ }
+ else if (l >= 11 && !strcmp(argv[i] + l - 11, "packages.gz"))
+ {
+ r = repo_add_susetags(c.repo, fp, 0, 0, 0);
+ }
+#endif
+#ifdef ENABLE_RPMMD
+ else if (l >= 14 && !strcmp(argv[i] + l - 14, "primary.xml.gz"))
+ {
+ r = repo_add_rpmmd(c.repo, fp, 0, 0);
+ }
+ else if (l >= 17 && !strcmp(argv[i] + l - 17, "updateinfo.xml.gz"))
+ {
+ r = repo_add_updateinfoxml(c.repo, fp, 0);
+ }
+#endif
+ else
+ r = repo_add_solv(c.repo, fp, 0);
+ if (r)
+ {
+ fprintf(stderr, "could not add repo %s: %s\n", argv[i], pool_errstr(pool));
+ exit(1);
+ }
+ if (fp != stdin)
+ fclose(fp);
+ }
+
+ pool_addfileprovides(pool);
+
+ /* bad hack ahead: clone repo */
+ c.instrepo->idarraydata = c.repo->idarraydata;
+ c.instrepo->idarraysize = c.repo->idarraysize;
+ c.instrepo->start = c.repo->start;
+ c.instrepo->end = c.repo->end;
+ c.instrepo->nsolvables = c.repo->nsolvables; /* sic! */
+ pool_set_installed(pool, c.instrepo);
+ pool_createwhatprovides(pool);
+
+ for (pid = 1; pid < pool->nsolvables; pid++)
+ {
+ Solvable *s;
+ c.shown = 0;
+ s = pool->solvables + pid;
+ if (!s->repo)
+ continue;
+ if (!pool_installable(pool, s))
+ continue;
+ pname = pool_id2str(pool, s->name);
+ if (strncmp(pname, "patch:", 6) != 0)
+ continue;
+
+ if (*mypatch)
+ {
+ if (strncmp(mypatch, pname + 6, strlen(pname + 6)) != 0)
+ continue;
+ if (strcmp(mypatch, pname + 6) != 0)
+ {
+ l = strlen(pname + 6);
+ if (mypatch[l] != '-')
+ continue;
+ if (strcmp(mypatch + l + 1, pool_id2str(pool, s->evr)) != 0)
+ continue;
+ }
+ }
+ else
+ {
+ FOR_PROVIDES(p, pp, s->name)
+ {
+ Solvable *s2 = pool->solvables + p;
+ if (pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) < 0)
+ break;
+ }
+ if (p) {
+ /* printf("found a newer one for %s\n", pname+6); */
+ continue; /* found a newer one */
+ }
+ }
+ tests++;
+ if (!s->conflicts)
+ continue;
+
+#if 0
+ printf("testing patch %s-%s\n", pname + 6, pool_id2str(pool, s->evr));
+#endif
+
+ test_all_old_patches_included(&c, pid);
+ test_all_packages_installable(&c, pid);
+ test_can_upgrade_all_packages(&c, pid);
+ test_no_ga_package_fulfills_dependency(&c, pid);
+ }
+
+ exit(c.status);
+}
--- /dev/null
+#! /bin/sh
+# repo2solv
+#
+# give it a directory of a local mirror of a repo and this
+# tries to detect the repo type and generate one SOLV file on stdout
+
+get_DESCRDIR () {
+ local d=$(grep '^DESCRDIR' content | sed 's/^DESCRDIR[[:space:]]\+\(.*[^[:space:]]\)[[:space:]]*$/\1/')
+ if test -z "$d"; then
+ echo suse/setup/descr
+ else
+ echo ${d}
+ fi
+}
+
+test_susetags() {
+ if test -s content; then
+ DESCR=$(get_DESCRDIR)
+ test -d $DESCR
+ return $?
+ else
+ return 1
+ fi
+}
+
+repomd_findfile() {
+ local t=$1
+ local p=$2
+ local f
+ if test -n "$t" -a -s repomd.xml ; then
+ f=`repomdxml2solv -q $t:location < repomd.xml 2>/dev/null`
+ f=${f##*/}
+ if test -f "$f" ; then
+ echo "$f"
+ return
+ fi
+ fi
+ if test -f "$p.bz2" ; then
+ echo "$p.bz2"
+ elif test -f "$p.gz" ; then
+ echo "$p.gz"
+ elif test -f "$p" ; then
+ echo "$p"
+ fi
+}
+
+repomd_decompress() {
+ case $1 in
+ *.gz) gzip -dc "$1" ;;
+ *.bz2) bzip2 -dc "$1" ;;
+ *.lzma) lzma -dc "$1" ;;
+ *.xz) xz -dc "$1" ;;
+ *) cat "$1" ;;
+ esac
+}
+
+susetags_findfile() {
+ if test -s "$1.xz" ; then
+ echo "$1.xz"
+ elif test -s "$1.lzma" ; then
+ echo "$1.lzma"
+ elif test -s "$1.bz2" ; then
+ echo "$1.bz2"
+ elif test -s "$1.gz" ; then
+ echo "$1.gz"
+ fi
+}
+
+susetags_findfile_cat() {
+ if test -s "$1.xz" ; then
+ xz -dc "$1.xz"
+ elif test -s "$1.lzma" ; then
+ lzma -dc "$1.lzma"
+ elif test -s "$1.bz2" ; then
+ bzip2 -dc "$1.bz2"
+ elif test -s "$1.gz" ; then
+ gzip -dc "$1.gz"
+ elif test -s "$1" ; then
+ cat "$1"
+ fi
+}
+
+# signal an error if there is a problem
+set -e
+
+LANG=C
+unset CDPATH
+parser_options=${PARSER_OPTIONS:-}
+
+findopt="-prune"
+repotype=
+addautooption=
+
+while true ; do
+ if test "$1" = "-o" ; then
+ exec > "$2"
+ shift
+ shift
+ elif test "$1" = "-R" ; then
+ # recursive
+ findopt=
+ repotype=plaindir
+ shift
+ elif test "$1" = "-X" ; then
+ addautooption=-X
+ shift
+ elif test "$1" = "-A" ; then
+ shift
+ else
+ break
+ fi
+done
+
+dir="$1"
+cd "$dir" || exit 1
+
+if test -z "$repotype" ; then
+ # autodetect repository type
+ if test -d repodata -o -f repomd.xml; then
+ repotype=rpmmd
+ elif test_susetags ; then
+ repotype=susetags
+ else
+ repotype=plaindir
+ fi
+fi
+
+if test "$repotype" = rpmmd ; then
+ test -d repodata && {
+ cd repodata || exit 2
+ }
+
+ primfile=
+ primxml=`repomd_findfile primary primary.xml`
+ if test -n "$primxml" -a -s "$primxml" ; then
+ primfile=`mktemp` || exit 3
+ (
+ # fake tag to combine primary.xml and extensions
+ # like susedata.xml, other.xml, filelists.xml
+ echo '<rpmmd>'
+ if test -f $primxml ; then
+ repomd_decompress $primxml
+ # add a newline
+ echo
+ fi
+ susedataxml=`repomd_findfile susedata susedata.xml`
+ if test -f "$susedataxml" ; then
+ repomd_decompress "$susedataxml"
+ fi
+ echo '</rpmmd>'
+ ) | sed 's/<?xml[^>]*>//g' | sed '1i\<?xml version="1.0" encoding="UTF-8"?>' | rpmmd2solv $parser_options > $primfile || exit 4
+ fi
+
+ prodfile=
+ prodxml=`repomd_findfile products products.xml`
+ if test -z "$prodxml" ; then
+ prodxml=`repomd_findfile product product.xml`
+ fi
+ if test -n "$prodxml" -a -s "$prodxml" ; then
+ prodfile=`mktemp` || exit 3
+ repomd_decompress "$prodxml" | rpmmd2solv $parser_options > $prodfile || exit 4
+ fi
+
+ patternfile=
+ patternxml=`repomd_findfile 'patterns' patterns.xml`
+ if test -n "$patternxml" -a -s "$patternxml" ; then
+ patternfile=`mktemp` || exit 3
+ repomd_decompress "$patternxml" | rpmmd2solv $parser_options > $patternfile || exit 4
+ fi
+
+ # This contains repomd.xml
+ # for now we only read some keys like timestamp
+ repomdfile=
+ repomdxml=`repomd_findfile '' repomd.xml`
+ if test -n "$repomdxml" -a -s "$repomdxml" ; then
+ repomdfile=`mktemp` || exit 3
+ repomd_decompress "$repomdxml" | repomdxml2solv $parser_options > $repomdfile || exit 4
+ fi
+
+ # This contains suseinfo.xml, which is an extension to repomd.xml
+ # for now we only read some keys like expiration and products
+ suseinfofile=
+ suseinfoxml=`repomd_findfile suseinfo suseinfo.xml`
+ if test -n "$suseinfoxml" -a -s "$suseinfoxml" ; then
+ suseinfofile=`mktemp` || exit 3
+ repomd_decompress "$suseinfoxml" | repomdxml2solv $parser_options > $suseinfofile || exit 4
+ fi
+
+ # This contains a updateinfo.xml* and maybe patches
+ updateinfofile=
+ updateinfoxml=`repomd_findfile updateinfo updateinfo.xml`
+ if test -n "$updateinfoxml" -a -s "$updateinfoxml" ; then
+ updateinfofile=`mktemp` || exit 3
+ repomd_decompress "$updateinfoxml" | updateinfoxml2solv $parser_options > $updateinfofile || exit 4
+ fi
+
+ # This contains a deltainfo.xml*
+ deltainfofile=
+ deltainfoxml=`repomd_findfile deltainfo deltainfo.xml`
+ if test -z "$deltainfoxml"; then
+ deltainfoxml=`repomd_findfile prestodelta prestodelta.xml`
+ fi
+ if test -n "$deltainfoxml" -a -s "$deltainfoxml" ; then
+ deltainfofile=`mktemp` || exit 3
+ repomd_decompress "$deltainfoxml" | deltainfoxml2solv $parser_options > $deltainfofile || exit 4
+ fi
+
+ # This contains appdata
+ appdataxml=
+ appdatafile=
+ if test -x /usr/bin/appdata2solv ; then
+ appdataxml=`repomd_findfile appdata appdata.xml`
+ fi
+ if test -n "$appdataxml" -a -s "$appdataxml" ; then
+ appdatafile=`mktemp` || exit 3
+ repomd_decompress "$appdataxml" | appdata2solv $parser_options > $appdatafile || exit 4
+ fi
+
+ # Now merge primary, patches, updateinfo, and deltainfo
+ mergesolv $addautooption $repomdfile $suseinfofile $primfile $prodfile $patternfile $updateinfofile $deltainfofile $appdatafile
+ rm -f $repomdfile $suseinfofile $primfile $patternfile $prodfile $updateinfofile $deltainfofile $appdatafile
+
+elif test "$repotype" = susetags ; then
+ olddir=`pwd`
+ DESCR=$(get_DESCRDIR)
+ cd ${DESCR} || exit 2
+ appdataxml=
+ appdatafile=
+ if test -x /usr/bin/appdata2solv ; then
+ appdataxml=`susetags_findfile appdata.xml`
+ fi
+ if test -n "$appdataxml" ; then
+ appdatafile=`mktemp` || exit 3
+ repomd_decompress "$appdataxml" | appdata2solv $parser_options > $appdatafile || exit 4
+ parser_options="-M $appdatafile $parser_options"
+ fi
+ (
+ # First packages
+ susetags_findfile_cat packages
+
+ # DU
+ susetags_findfile_cat packages.DU
+
+ # Now default language
+ susetags_findfile_cat packages.en
+
+ # Now patterns. Not simply those files matching *.pat{,.gz,bz2},
+ # but only those mentioned in the file 'patterns'
+ if test -f patterns ; then
+ for i in `cat patterns`; do
+ if test -s "$i" ; then
+ repomd_decompress "$i"
+ fi
+ done
+ fi
+
+ # Now all other packages.{lang}. Needs to come last as it switches
+ # languages for all following susetags files
+ for i in packages.* ; do
+ case $i in
+ *.gz|*.bz2|*.xz|*.lzma) name="${i%.*}" ;;
+ *) name="$i" ;;
+ esac
+ case $name in
+ # ignore files we handled already
+ *.DU | *.en | *.FL | packages ) continue ;;
+ *)
+ suff=${name#packages.}
+ echo "=Lan: $suff"
+ repomd_decompress "$i"
+ esac
+ done
+
+ ) | susetags2solv $addautooption -c "${olddir}/content" $parser_options || exit 4
+ test -n "$appdatafile" && rm -f "$appdatafile"
+ cd "$olddir"
+elif test "$repotype" = plaindir ; then
+ find * -name .\* -prune -o $findopt -name \*.delta.rpm -o -name \*.patch.rpm -o -name \*.rpm -a -type f -print0 | rpms2solv $addautooption -0 -m -
+else
+ echo "unknown repository type '$repotype'" >&2
+ exit 1
+fi
--- /dev/null
+/*
+ * Copyright (c) 2007-2009, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_repomdxml.h"
+#include "common_write.h"
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "repomdxml2solv [-q query]\n"
+ " reads a 'repomd.xml' file from <stdin> and writes a .solv file to <stdout>\n"
+ " -q : query a repomd data entry\n"
+ " -h : print help & exit\n"
+ );
+ exit(status);
+}
+
+static void
+doquery(Pool *pool, Repo *repo, const char *query)
+{
+ Id id, type = 0;
+ char qbuf[256];
+ const char *qp;
+ Dataiterator di;
+
+ qp = strchr(query, ':');
+ if (qp)
+ {
+ type = pool_strn2id(pool, query, qp - query, 0);
+ if (!type)
+ exit(0);
+ qp++;
+ }
+ else
+ qp = query;
+ snprintf(qbuf, sizeof(qbuf), "repository:repomd:%s", qp);
+ id = pool_str2id(pool, qbuf, 0);
+ if (!id)
+ exit(0);
+ dataiterator_init(&di, pool, repo, SOLVID_META, id, 0, 0);
+ dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
+ while (dataiterator_step(&di))
+ {
+ if (type)
+ {
+ dataiterator_setpos_parent(&di);
+ if (pool_lookup_id(pool, SOLVID_POS, REPOSITORY_REPOMD_TYPE) != type)
+ continue;
+ }
+ switch (di.key->type)
+ {
+ case REPOKEY_TYPE_ID:
+ case REPOKEY_TYPE_CONSTANTID:
+ printf("%s\n", pool_id2str(pool, di.kv.id));
+ break;
+ case REPOKEY_TYPE_STR:
+ printf("%s\n", di.kv.str);
+ break;
+ case REPOKEY_TYPE_NUM:
+ case REPOKEY_TYPE_CONSTANT:
+ printf("%llu\n", SOLV_KV_NUM64(&di.kv));
+ break;
+ default:
+ if (solv_chksum_len(di.key->type))
+ printf("%s:%s\n", solv_chksum_type2str(di.key->type), repodata_chk2str(di.data, di.key->type, (unsigned char *)di.kv.str));
+ break;
+ }
+ }
+ dataiterator_free(&di);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, flags = 0;
+ const char *query = 0;
+
+ Pool *pool = pool_create();
+ Repo *repo = repo_create(pool, "<stdin>");
+
+ while ((c = getopt (argc, argv, "hq:")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'q':
+ query = optarg;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ if (repo_add_repomdxml(repo, stdin, flags))
+ {
+ fprintf(stderr, "repomdxml2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ if (query)
+ doquery(pool, repo, query);
+ else
+ tool_write(repo, 0, 0);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * rpmdb2solv
+ *
+ * Reads rpm database (and evtl. more, like product metadata) to build
+ * a .solv file of 'installed' solvables.
+ * Writes .solv to stdout
+ *
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+#ifdef ENABLE_PUBKEY
+#include "repo_pubkey.h"
+#endif
+#include "repo_products.h"
+#include "repo_solv.h"
+#include "common_write.h"
+#ifdef ENABLE_APPDATA
+#include "repo_appdata.h"
+#endif
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "rpmdb2solv [-n] [-b <basefile>] [-p <productsdir>] [-r <root>]\n"
+ " -n : No packages, do not read rpmdb, useful to only parse products\n"
+ " -b <basefile> : Write .solv to <basefile>.solv instead of stdout\n"
+ " -p <productsdir> : Scan <productsdir> for .prod files, representing installed products\n"
+ " -r <root> : Prefix rpmdb path and <productsdir> with <root>\n"
+ " -o <solv> : Write .solv to file instead of stdout\n"
+ );
+ exit(status);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ FILE *reffp = 0;
+ Pool *pool = pool_create();
+ Repo *repo;
+ Repodata *data;
+ int c, percent = 0;
+ int nopacks = 0;
+ const char *root = 0;
+ const char *basefile = 0;
+ const char *refname = 0;
+#ifdef ENABLE_SUSEREPO
+ char *proddir = 0;
+#endif
+ char *outfile = 0;
+#ifdef ENABLE_PUBKEY
+ int pubkeys = 0;
+#endif
+#ifdef ENABLE_APPDATA
+ int add_appdata = 0;
+#endif
+#ifdef SUSE
+ int add_auto = 0;
+#endif
+
+ /*
+ * parse arguments
+ */
+
+ while ((c = getopt(argc, argv, "APhnkxXb:r:p:o:")) >= 0)
+ switch (c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'r':
+ root = optarg;
+ break;
+ case 'b':
+ basefile = optarg;
+ break;
+ case 'n':
+ nopacks = 1;
+ break;
+ case 'P':
+ percent = 1;
+ break;
+ case 'p':
+#ifdef ENABLE_SUSEREPO
+ proddir = optarg;
+#endif
+ break;
+ case 'x':
+ break; /* extrapool no longer supported */
+ case 'X':
+#ifdef SUSE
+ add_auto = 1;
+#endif
+ break;
+ case 'A':
+#ifdef ENABLE_APPDATA
+ add_appdata = 1;
+#endif
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+#ifdef ENABLE_PUBKEY
+ case 'k':
+ nopacks = 1;
+ pubkeys = 1;
+ break;
+#endif
+ default:
+ usage(1);
+ }
+
+ if (outfile && !freopen(outfile, "w", stdout))
+ {
+ perror(outfile);
+ exit(1);
+ }
+
+ /*
+ * optional arg is old version of rpmdb solv file
+ * should make this a real option instead
+ */
+
+ if (optind < argc)
+ refname = argv[optind];
+
+ if (refname && !nopacks)
+ {
+ if ((reffp = fopen(refname, "r")) == NULL)
+ perror(refname);
+ }
+
+ /*
+ * create 'installed' repository
+ * add products
+ * add rpmdb
+ * write .solv
+ */
+
+ if (root && *root)
+ pool_set_rootdir(pool, root);
+
+ repo = repo_create(pool, "installed");
+ data = repo_add_repodata(repo, 0);
+
+ if (!nopacks)
+ {
+ if (repo_add_rpmdb_reffp(repo, reffp, REPO_USE_ROOTDIR | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | (percent ? RPMDB_REPORT_PROGRESS : 0)))
+ {
+ fprintf(stderr, "rpmdb2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+#ifdef ENABLE_PUBKEY
+ if (pubkeys)
+ {
+ if (repo_add_rpmdb_pubkeys(repo, REPO_USE_ROOTDIR | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | ADD_WITH_KEYSIGNATURES))
+ {
+ fprintf(stderr, "rpmdb2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+#endif
+
+#ifdef ENABLE_SUSEREPO
+ if (proddir && *proddir)
+ {
+ if (root && *root)
+ {
+ int rootlen = strlen(root);
+ if (!strncmp(root, proddir, rootlen))
+ {
+ proddir += rootlen;
+ if (*proddir != '/' && proddir[-1] == '/')
+ proddir--;
+ }
+ }
+ if (repo_add_products(repo, proddir, REPO_USE_ROOTDIR | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "rpmdb2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+#endif
+
+#ifdef ENABLE_APPDATA
+ if (add_appdata)
+ repo_add_appdata_dir(repo, "/usr/share/appdata", REPO_USE_ROOTDIR | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | APPDATA_SEARCH_UNINTERNALIZED_FILELIST);
+#endif
+ repodata_internalize(data);
+
+ if (reffp)
+ fclose(reffp);
+
+#ifdef SUSE
+ if (add_auto)
+ repo_add_autopattern(repo, ADD_NO_AUTOPRODUCTS);
+#endif
+
+ tool_write(repo, basefile, 0);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmmd.h"
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+#include "common_write.h"
+#include "solv_xfopen.h"
+
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "rpmmd2solv [-a][-h][-n <attrname>][-l <locale>]\n"
+ " reads 'primary' from a 'rpmmd' repository from <stdin> and writes a .solv file to <stdout>\n"
+ " -h : print help & exit\n"
+ " -n <name>: save attributes as <name>.attr\n"
+ " -l <locale>: parse localization data for <locale>\n"
+ );
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, flags = 0;
+ const char *attrname = 0;
+ const char *basefile = 0;
+ const char *dir = 0;
+ const char *locale = 0;
+#ifdef SUSE
+ int add_auto = 0;
+#endif
+
+ Pool *pool = pool_create();
+ Repo *repo = repo_create(pool, "<stdin>");
+
+ while ((c = getopt (argc, argv, "hn:b:d:l:X")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'n':
+ attrname = optarg;
+ break;
+ case 'b':
+ basefile = optarg;
+ break;
+ case 'd':
+ dir = optarg;
+ break;
+ case 'l':
+ locale = optarg;
+ break;
+ case 'X':
+#ifdef SUSE
+ add_auto = 1;
+#endif
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ if (dir)
+ {
+ FILE *fp;
+ int l;
+ char *fnp;
+ l = strlen(dir) + 128;
+ fnp = solv_malloc(l+1);
+ snprintf(fnp, l, "%s/primary.xml.gz", dir);
+ if (!(fp = solv_xfopen(fnp, 0)))
+ {
+ perror(fnp);
+ exit(1);
+ }
+ if (repo_add_rpmmd(repo, fp, 0, flags))
+ {
+ fprintf(stderr, "rpmmd2solv: %s: %s\n", fnp, pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ snprintf(fnp, l, "%s/diskusagedata.xml.gz", dir);
+ if ((fp = solv_xfopen(fnp, 0)))
+ {
+ if (repo_add_rpmmd(repo, fp, 0, flags))
+ {
+ fprintf(stderr, "rpmmd2solv: %s: %s\n", fnp, pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+ if (locale)
+ {
+ if (snprintf(fnp, l, "%s/translation-%s.xml.gz", dir, locale) >= l)
+ {
+ fprintf(stderr, "-l parameter too long\n");
+ exit(1);
+ }
+ while (!(fp = solv_xfopen(fnp, 0)))
+ {
+ fprintf(stderr, "not opened %s\n", fnp);
+ if (strlen(locale) > 2)
+ {
+ if (snprintf(fnp, l, "%s/translation-%.2s.xml.gz", dir, locale) >= l)
+ {
+ fprintf(stderr, "-l parameter too long\n");
+ exit(1);
+ }
+ if ((fp = solv_xfopen(fnp, 0)))
+ break;
+ }
+ perror(fnp);
+ exit(1);
+ }
+ fprintf(stderr, "opened %s\n", fnp);
+ if (repo_add_rpmmd(repo, fp, 0, flags))
+ {
+ fprintf(stderr, "rpmmd2solv: %s: %s\n", fnp, pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+ solv_free(fnp);
+ }
+ else
+ {
+ if (repo_add_rpmmd(repo, stdin, 0, flags))
+ {
+ fprintf(stderr, "rpmmd2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+#ifdef SUSE
+ if (add_auto)
+ repo_add_autopattern(repo, 0);
+#endif
+ tool_write(repo, basefile, attrname);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * rpms2solv - create a solv file from multiple rpms
+ *
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "util.h"
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+#ifdef ENABLE_PUBKEY
+#include "repo_pubkey.h"
+#include "solv_xfopen.h"
+#endif
+#include "repo_solv.h"
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+#include "common_write.h"
+
+static char *
+fgets0(char *s, int size, FILE *stream)
+{
+ char *p = s;
+ int c;
+
+ while (--size > 0)
+ {
+ c = getc(stream);
+ if (c == EOF)
+ {
+ if (p == s)
+ return 0;
+ c = 0;
+ }
+ *p++ = c;
+ if (!c)
+ return s;
+ }
+ *p = 0;
+ return s;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char **rpms = 0;
+ char *manifest = 0;
+ int manifest0 = 0;
+ int c, i, res, nrpms = 0;
+ Pool *pool = pool_create();
+ Repo *repo;
+ FILE *fp;
+ char buf[4096], *p;
+ const char *basefile = 0;
+#ifdef ENABLE_PUBKEY
+ int pubkeys = 0;
+#endif
+#ifdef SUSE
+ int add_auto = 0;
+#endif
+ int filtered_filelist = 0;
+
+ while ((c = getopt(argc, argv, "0XkKb:m:F")) >= 0)
+ {
+ switch(c)
+ {
+ case 'b':
+ basefile = optarg;
+ break;
+ case 'm':
+ manifest = optarg;
+ break;
+ case '0':
+ manifest0 = 1;
+ break;
+ case 'F':
+ filtered_filelist = 1;
+ break;
+#ifdef ENABLE_PUBKEY
+ case 'k':
+ pubkeys = 1;
+ break;
+ case 'K':
+ pubkeys = 2;
+ break;
+#endif
+ case 'X':
+#ifdef SUSE
+ add_auto = 1;
+#endif
+ break;
+ default:
+ exit(1);
+ }
+ }
+ if (manifest)
+ {
+ if (!strcmp(manifest, "-"))
+ fp = stdin;
+ else if ((fp = fopen(manifest, "r")) == 0)
+ {
+ perror(manifest);
+ exit(1);
+ }
+ for (;;)
+ {
+ if (manifest0)
+ {
+ if (!fgets0(buf, sizeof(buf), fp))
+ break;
+ }
+ else
+ {
+ if (!fgets(buf, sizeof(buf), fp))
+ break;
+ if ((p = strchr(buf, '\n')) != 0)
+ *p = 0;
+ }
+ rpms = solv_extend(rpms, nrpms, 1, sizeof(char *), 15);
+ rpms[nrpms++] = strdup(buf);
+ }
+ if (fp != stdin)
+ fclose(fp);
+ }
+ while (optind < argc)
+ {
+ rpms = solv_extend(rpms, nrpms, 1, sizeof(char *), 15);
+ rpms[nrpms++] = strdup(argv[optind++]);
+ }
+ repo = repo_create(pool, "rpms2solv");
+ repo_add_repodata(repo, 0);
+ res = 0;
+ for (i = 0; i < nrpms; i++)
+ {
+#ifdef ENABLE_PUBKEY
+ if (pubkeys == 2)
+ {
+ FILE *fp = solv_xfopen(rpms[i], "r");
+ if (!fp)
+ {
+ perror(rpms[i]);
+ res = 1;
+ continue;
+ }
+ if (repo_add_keyring(repo, fp, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|ADD_WITH_KEYSIGNATURES))
+ {
+ fprintf(stderr, "rpms2solv: %s\n", pool_errstr(pool));
+ res = 1;
+ }
+ fclose(fp);
+ continue;
+ }
+ if (pubkeys)
+ {
+ if (repo_add_pubkey(repo, rpms[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|ADD_WITH_KEYSIGNATURES) == 0)
+ {
+ fprintf(stderr, "rpms2solv: %s\n", pool_errstr(pool));
+ res = 1;
+ }
+ continue;
+ }
+#endif
+ if (repo_add_rpm(repo, rpms[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|(filtered_filelist ? RPM_ADD_FILTERED_FILELIST : 0)) == 0)
+ {
+ fprintf(stderr, "rpms2solv: %s\n", pool_errstr(pool));
+ res = 1;
+ }
+ }
+ repo_internalize(repo);
+#ifdef SUSE
+ if (add_auto)
+ repo_add_autopattern(repo, 0);
+#endif
+ tool_write(repo, basefile, 0);
+ pool_free(pool);
+ for (c = 0; c < nrpms; c++)
+ free((char *)rpms[c]);
+ solv_free(rpms);
+ exit(res);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <zlib.h>
+#include <getopt.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_solv.h"
+#include "repo_susetags.h"
+#include "repo_content.h"
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+#include "common_write.h"
+#include "solv_xfopen.h"
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "susetags2solv [-b <base>][-c <content>][-d <descrdir>][-h][-n <name>]\n"
+ " reads a 'susetags' repository from <stdin> and writes a .solv file to <stdout>\n"
+ " -b <base>: save as multiple files starting with <base>\n"
+ " -c <contentfile> : parse given contentfile (for product information)\n"
+ " -d <descrdir> : do not read from stdin, but use data in descrdir\n"
+ " -h : print help & exit\n"
+ " -n <name>: save attributes as <name>.attr\n"
+ );
+ exit(status);
+}
+
+/* content file query */
+static void
+doquery(Pool *pool, Repo *repo, const char *arg)
+{
+ char qbuf[256];
+ const char *str;
+ Id id;
+
+ snprintf(qbuf, sizeof(qbuf), "susetags:%s", arg);
+ id = pool_str2id(pool, qbuf, 0);
+ if (!id)
+ return;
+ str = repo_lookup_str(repo, SOLVID_META, id);
+ if (str)
+ printf("%s\n", str);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *contentfile = 0;
+ const char *attrname = 0;
+ const char *descrdir = 0;
+ const char *basefile = 0;
+ const char *query = 0;
+ const char *mergefile = 0;
+ Id defvendor = 0;
+ int flags = 0;
+#ifdef SUSE
+ int add_auto = 0;
+#endif
+ int c;
+ Pool *pool;
+ Repo *repo;
+
+ while ((c = getopt(argc, argv, "hn:c:d:b:q:M:X")) >= 0)
+ {
+ switch (c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'n':
+ attrname = optarg;
+ break;
+ case 'c':
+ contentfile = optarg;
+ break;
+ case 'd':
+ descrdir = optarg;
+ break;
+ case 'b':
+ basefile = optarg;
+ break;
+ case 'q':
+ query = optarg;
+ break;
+ case 'M':
+ mergefile = optarg;
+ break;
+ case 'X':
+#ifdef SUSE
+ add_auto = 1;
+#endif
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ pool = pool_create();
+ repo = repo_create(pool, "<susetags>");
+
+ repo_add_repodata(repo, 0);
+
+ if (contentfile)
+ {
+ FILE *fp = fopen(contentfile, "r");
+ if (!fp)
+ {
+ perror(contentfile);
+ exit(1);
+ }
+ if (repo_add_content(repo, fp, REPO_REUSE_REPODATA))
+ {
+ fprintf(stderr, "susetags2solv: %s: %s\n", contentfile, pool_errstr(pool));
+ exit(1);
+ }
+ defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
+ fclose(fp);
+ }
+
+ if (attrname)
+ {
+ /* ensure '.attr' suffix */
+ const char *dot = strrchr(attrname, '.');
+ if (!dot || strcmp(dot, ".attr"))
+ {
+ int len = strlen (attrname);
+ char *newname = (char *)malloc(len + 6); /* alloc for <attrname>+'.attr'+'\0' */
+ strcpy (newname, attrname);
+ strcpy (newname+len, ".attr");
+ attrname = newname;
+ }
+ }
+
+ /*
+ * descrdir path given, open files and read from there
+ */
+
+ if (descrdir)
+ {
+ char *fnp;
+ int ndirs, i;
+ struct dirent **files;
+
+ ndirs = scandir(descrdir, &files, 0, alphasort);
+ if (ndirs < 0)
+ {
+ perror(descrdir);
+ exit(1);
+ }
+
+ /* bring packages to front */
+ for (i = 0; i < ndirs; i++)
+ {
+ char *fn = files[i]->d_name;
+ if (!strcmp(fn, "packages") || !strcmp(fn, "packages.gz"))
+ break;
+ }
+ if (i == ndirs)
+ {
+ fprintf(stderr, "found no packages file\n");
+ exit(1);
+ }
+ if (i)
+ {
+ struct dirent *de = files[i];
+ memmove(files + 1, files, i * sizeof(de));
+ files[0] = de;
+ }
+
+ fnp = solv_malloc(strlen(descrdir) + 128);
+ for (i = 0; i < ndirs; i++)
+ {
+ char *fn = files[i]->d_name;
+
+ if (!strcmp(fn, "packages") || !strcmp(fn, "packages.gz"))
+ {
+ FILE *fp;
+ sprintf(fnp, "%s/%s", descrdir, fn);
+ fp = solv_xfopen(fnp, 0);
+ if (!fp)
+ {
+ perror(fn);
+ exit(1);
+ }
+ if (repo_add_susetags(repo, fp, defvendor, 0, flags | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "susetags2solv: %s: %s\n", fnp, pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+ else if (!strcmp(fn, "packages.DU") || !strcmp(fn, "packages.DU.gz"))
+ {
+ FILE *fp;
+ sprintf(fnp, "%s/%s", descrdir, fn);
+ fp = solv_xfopen(fnp, 0);
+ if (!fp)
+ {
+ perror(fn);
+ exit(1);
+ }
+ if (repo_add_susetags(repo, fp, defvendor, 0, flags | SUSETAGS_EXTEND | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "susetags2solv: %s: %s\n", fnp, pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+ else if (!strcmp(fn, "packages.FL") || !strcmp(fn, "packages.FL.gz"))
+ {
+#if 0
+ sprintf(fnp, "%s/%s", descrdir, fn);
+ FILE *fp = solv_xfopen(fnp, 0);
+ if (!fp)
+ {
+ perror(fn);
+ exit(1);
+ }
+ if (repo_add_susetags(repo, fp, defvendor, 0, flags | SUSETAGS_EXTEND | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "susetags2solv: %s: %s\n", fnp, pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+#else
+ /* ignore for now. reactivate when filters work */
+ continue;
+#endif
+ }
+ else if (!strncmp(fn, "packages.", 9))
+ {
+ char lang[6];
+ char *p;
+ FILE *fp;
+ sprintf(fnp, "%s/%s", descrdir, fn);
+ p = strrchr(fnp, '.');
+ if (p && !strcmp(p, ".gz"))
+ {
+ *p = 0;
+ p = strrchr(fnp, '.');
+ }
+ if (!p || !p[1] || strlen(p + 1) > 5)
+ continue;
+ strcpy(lang, p + 1);
+ sprintf(fnp, "%s/%s", descrdir, fn);
+ fp = solv_xfopen(fnp, 0);
+ if (!fp)
+ {
+ perror(fn);
+ exit(1);
+ }
+ if (repo_add_susetags(repo, fp, defvendor, lang, flags | SUSETAGS_EXTEND | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "susetags2solv: %s: %s\n", fnp, pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+ }
+ for (i = 0; i < ndirs; i++)
+ free(files[i]);
+ free(files);
+ free(fnp);
+ repo_internalize(repo);
+ }
+ else
+ {
+ /* read data from stdin */
+ if (repo_add_susetags(repo, stdin, defvendor, 0, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
+ {
+ fprintf(stderr, "susetags2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ }
+ repo_internalize(repo);
+ if (mergefile)
+ {
+ FILE *fp = fopen(mergefile, "r");
+ if (!fp)
+ {
+ perror(mergefile);
+ exit(1);
+ }
+ if (repo_add_solv(repo, fp, 0))
+ {
+ fprintf(stderr, "susetags2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ fclose(fp);
+ }
+#ifdef SUSE
+ if (add_auto)
+ repo_add_autopattern(repo, 0);
+#endif
+
+ if (query)
+ doquery(pool, repo, query);
+ else
+ tool_write(repo, basefile, attrname);
+ pool_free(pool);
+ exit(0);
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "solver.h"
+#include "selection.h"
+#include "solverdebug.h"
+#include "testcase.h"
+
+static struct resultflags2str {
+ Id flag;
+ const char *str;
+} resultflags2str[] = {
+ { TESTCASE_RESULT_TRANSACTION, "transaction" },
+ { TESTCASE_RESULT_PROBLEMS, "problems" },
+ { TESTCASE_RESULT_ORPHANED, "orphaned" },
+ { TESTCASE_RESULT_RECOMMENDED, "recommended" },
+ { TESTCASE_RESULT_UNNEEDED, "unneeded" },
+ { TESTCASE_RESULT_ALTERNATIVES, "alternatives" },
+ { TESTCASE_RESULT_RULES, "rules" },
+ { TESTCASE_RESULT_GENID, "genid" },
+ { 0, 0 }
+};
+
+static void
+usage(int ex)
+{
+ fprintf(ex ? stderr : stdout, "Usage: testsolv <testcase>\n");
+ exit(ex);
+}
+
+struct reportsolutiondata {
+ int count;
+ char *result;
+};
+
+static int
+reportsolutioncb(Solver *solv, void *cbdata)
+{
+ struct reportsolutiondata *sd = cbdata;
+ char *res;
+
+ sd->count++;
+ res = testcase_solverresult(solv, TESTCASE_RESULT_TRANSACTION);
+ if (*res)
+ {
+ char prefix[64];
+ char *p2, *p = res;
+ sprintf(prefix, "callback%d:", sd->count);
+ while ((p2 = strchr(p, '\n')) != 0)
+ {
+ char c = p2[1];
+ p2[1] = 0;
+ sd->result = solv_dupappend(sd->result, prefix, p);
+ p2[1] = c;
+ p = p2 + 1;
+ }
+ }
+ solv_free(res);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool;
+ Queue job;
+ Queue solq;
+ Solver *solv;
+ char *result = 0;
+ int resultflags = 0;
+ int debuglevel = 0;
+ int writeresult = 0;
+ char *writetestcase = 0;
+ int multijob = 0;
+ int rescallback = 0;
+ int c;
+ int ex = 0;
+ const char *list = 0;
+ FILE *fp;
+ const char *p;
+
+ queue_init(&solq);
+ while ((c = getopt(argc, argv, "vmrhl:s:T:")) >= 0)
+ {
+ switch (c)
+ {
+ case 'v':
+ debuglevel++;
+ break;
+ case 'r':
+ writeresult++;
+ break;
+ case 'm':
+ rescallback = 1;
+ break;
+ case 'h':
+ usage(0);
+ break;
+ case 'l':
+ list = optarg;
+ break;
+ case 's':
+ if ((p = strchr(optarg, ':')))
+ queue_push2(&solq, atoi(optarg), atoi(p + 1));
+ else
+ queue_push2(&solq, 1, atoi(optarg));
+ break;
+ case 'T':
+ writetestcase = optarg;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ if (optind == argc)
+ usage(1);
+ for (; optind < argc; optind++)
+ {
+ pool = pool_create();
+ pool_setdebuglevel(pool, debuglevel);
+
+ fp = fopen(argv[optind], "r");
+ if (!fp)
+ {
+ perror(argv[optind]);
+ exit(0);
+ }
+ while (!feof(fp))
+ {
+ queue_init(&job);
+ result = 0;
+ resultflags = 0;
+ solv = testcase_read(pool, fp, argv[optind], &job, &result, &resultflags);
+ if (!solv)
+ {
+ pool_free(pool);
+ exit(resultflags == 77 ? 77 : 1);
+ }
+
+ if (!multijob && !feof(fp))
+ multijob = 1;
+
+ if (multijob)
+ printf("test %d:\n", multijob++);
+ if (list)
+ {
+ int selflags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL|SELECTION_GLOB|SELECTION_FLAT;
+ if (*list == '/')
+ selflags |= SELECTION_FILELIST;
+ queue_empty(&job);
+ selection_make(pool, &job, list, selflags);
+ if (!job.elements)
+ printf("No match\n");
+ else
+ {
+ Queue q;
+ int i;
+ queue_init(&q);
+ selection_solvables(pool, &job, &q);
+ for (i = 0; i < q.count; i++)
+ printf(" - %s\n", testcase_solvid2str(pool, q.elements[i]));
+ queue_free(&q);
+ }
+ }
+ else if (result || writeresult)
+ {
+ char *myresult, *resultdiff;
+ struct reportsolutiondata reportsolutiondata;
+ memset(&reportsolutiondata, 0, sizeof(reportsolutiondata));
+ if (rescallback)
+ {
+ solv->solution_callback = reportsolutioncb;
+ solv->solution_callback_data = &reportsolutiondata;
+ }
+ solver_solve(solv, &job);
+ solv->solution_callback = 0;
+ solv->solution_callback_data = 0;
+ if (!resultflags)
+ resultflags = TESTCASE_RESULT_TRANSACTION | TESTCASE_RESULT_PROBLEMS;
+ myresult = testcase_solverresult(solv, resultflags);
+ if (rescallback && reportsolutiondata.result)
+ {
+ reportsolutiondata.result = solv_dupjoin(reportsolutiondata.result, myresult, 0);
+ solv_free(myresult);
+ myresult = reportsolutiondata.result;
+ }
+ if (writeresult)
+ {
+ if (*myresult)
+ {
+ if (writeresult > 1)
+ {
+ const char *p;
+ int i;
+
+ printf("result ");
+ p = "%s";
+ for (i = 0; resultflags2str[i].str; i++)
+ if ((resultflags & resultflags2str[i].flag) != 0)
+ {
+ printf(p, resultflags2str[i].str);
+ p = ",%s";
+ }
+ printf(" <inline>\n");
+ p = myresult;
+ while (*p)
+ {
+ const char *p2 = strchr(p, '\n');
+ p2 = p2 ? p2 + 1 : p + strlen(p);
+ printf("#>%.*s", (int)(p2 - p), p);
+ p = p2;
+ }
+ }
+ else
+ printf("%s", myresult);
+ }
+ }
+ else
+ {
+ resultdiff = testcase_resultdiff(result, myresult);
+ if (resultdiff)
+ {
+ printf("Results differ:\n%s", resultdiff);
+ ex = 1;
+ solv_free(resultdiff);
+ }
+ }
+ solv_free(result);
+ solv_free(myresult);
+ }
+ else
+ {
+ int pcnt = solver_solve(solv, &job);
+ if (writetestcase)
+ testcase_write(solv, writetestcase, resultflags, 0, 0);
+ if (pcnt && solq.count)
+ {
+ int i, taken = 0;
+ for (i = 0; i < solq.count; i += 2)
+ {
+ if (solq.elements[i] > 0 && solq.elements[i] <= pcnt)
+ if (solq.elements[i + 1] > 0 && solq.elements[i + 1] <= solver_solution_count(solv, solq.elements[i]))
+ {
+ printf("problem %d: taking solution %d\n", solq.elements[i], solq.elements[i + 1]);
+ solver_take_solution(solv, solq.elements[i], solq.elements[i + 1], &job);
+ taken = 1;
+ }
+ }
+ if (taken)
+ pcnt = solver_solve(solv, &job);
+ }
+ if (pcnt)
+ {
+ int problem, solution, scnt;
+ printf("Found %d problems:\n", pcnt);
+ for (problem = 1; problem <= pcnt; problem++)
+ {
+ printf("Problem %d:\n", problem);
+#if 1
+ solver_printprobleminfo(solv, problem);
+#else
+ {
+ Queue pq;
+ int j;
+ queue_init(&pq);
+ solver_findallproblemrules(solv, problem, &pq);
+ for (j = 0; j < pq.count; j++)
+ solver_printproblemruleinfo(solv, pq.elements[j]);
+ queue_free(&pq);
+ }
+#endif
+ printf("\n");
+ scnt = solver_solution_count(solv, problem);
+ for (solution = 1; solution <= scnt; solution++)
+ {
+ printf("Solution %d:\n", solution);
+ solver_printsolution(solv, problem, solution);
+ printf("\n");
+ }
+ }
+ }
+ else
+ {
+ Transaction *trans = solver_create_transaction(solv);
+ printf("Transaction summary:\n\n");
+ transaction_print(trans);
+ transaction_free(trans);
+ }
+ }
+ queue_free(&job);
+ solver_free(solv);
+ }
+ pool_free(pool);
+ fclose(fp);
+ }
+ queue_free(&solq);
+ exit(ex);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_updateinfoxml.h"
+#include "common_write.h"
+
+static void
+usage(int status)
+{
+ fprintf(stderr, "\nUsage:\n"
+ "updateinfoxml2solv [-h][-n <attrname>]\n"
+ " reads a 'updateinfo.xml' file from <stdin> and writes a .solv file to <stdout>\n"
+ " -h : print help & exit\n"
+ " -n <name>: save attributes as <name>.attr\n"
+ );
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, flags = 0;
+ char *attrname = 0;
+
+ Pool *pool = pool_create();
+ Repo *repo = repo_create(pool, "<stdin>");
+
+ while ((c = getopt(argc, argv, "hn:")) >= 0)
+ {
+ switch(c)
+ {
+ case 'h':
+ usage(0);
+ break;
+ case 'n':
+ attrname = optarg;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+ if (repo_add_updateinfoxml(repo, stdin, flags))
+ {
+ fprintf(stderr, "updateinfoxml2solv: %s\n", pool_errstr(pool));
+ exit(1);
+ }
+ tool_write(repo, 0, attrname);
+ pool_free(pool);
+ exit(0);
+}
+++ /dev/null
-libdir=@LIB_INSTALL_DIR@
-includedir=@INCLUDE_INSTALL_DIR@
-
-Name: libsolv
-Description: Library for solving packages and reading repositories
-Version: @VERSION@
-Libs: -L${libdir} -lsolvext -lsolv
-Cflags: -I${includedir}
+++ /dev/null
--------------------------------------------------------------------
-Mon Dec 14 15:48:01 CET 2015 - mls@suse.de
-
-- change product links to also look at timestamps [bnc#956443]
-- rework multiversion orphaned handling [bnc#957606]
-- support key type changes in repodata_internalize()
-- allow serialization of REPOKEY_TYPE_DELETED
-- improve appdata handling of installed packages
-- improve performance when run under xen
-- bump version to 0.6.15
-
--------------------------------------------------------------------
-Mon Oct 5 13:27:25 CEST 2015 - mls@suse.de
-
-- fix bug in recommends handling [bnc#948482]
-- also check installed packages in multiversion handling
-- fix build on Mageia
-- bump version to 0.6.14
-
--------------------------------------------------------------------
-Fri Sep 25 11:54:02 CEST 2015 - mls@suse.de
-
-- support a generic string for pattern-visible() [bnc#900769]
-- add a SOLVER_ALLOWUNINSTALL job type
-- add ordercycle introspection
-- fix mkmask handling of a zero size
-- support 'recommends' in repo_mdk.c
-- support filelist parsing in installcheck
-- bump version to 0.6.13
-
--------------------------------------------------------------------
-Tue Sep 1 13:37:11 CEST 2015 - mls@suse.de
-
-- added tcl bindings
-- improve debian ar archive handling
-- bindings: set the CLOEXEC flags in xfopen
-- bindings: support testcase writing [bnc#946752]
-- support REL_ELSE as evr of REL_COND
-- bump version to 0.6.12
-
--------------------------------------------------------------------
-Tue Jun 2 11:41:10 CEST 2015 - mls@suse.de
-
-- add forgotten sha-512 support to data_skip
-- speed up whatprovides lookup with a new helper array
-- fix dup with allowuninstall
-- improve alreadyinstalled handling of supplements
-- some code cleanup
-- bump version to 0.6.11
-
--------------------------------------------------------------------
-Sat May 2 11:44:08 UTC 2015 - mrueckert@suse.de
-
-- you really want to use rbconfig there
-
--------------------------------------------------------------------
-Wed Mar 18 11:04:34 CET 2015 - mls@suse.de
-
-- fix bug in dislike_old_versions that could lead to a segfault
- [bnc#922352]
-- bump version to 0.6.10
-
--------------------------------------------------------------------
-Mon Mar 9 16:42:35 CET 2015 - mls@suse.de
-
-- rework splitprovides handling [bnc#921332]
-- improve package choosing code
-- new testcase dependency format
-- add alternatives introspection
-- make reorder_dq_for_jobrules also look at recommends/suggests
-- rework branch handling
-- add parser for rpm rich deps
-- bump version to 0.6.9
-
--------------------------------------------------------------------
-Wed Jan 14 08:58:46 CET 2015 - ma@suse.de
-
-- fixes to build with swig 3.0.3
-- bump version to 0.6.8
-
--------------------------------------------------------------------
-Fri Dec 19 08:59:27 CET 2014 - ma@suse.de
-
-- add product:regflavor attribute [bnc#896224]
-- bump version to 0.6.7
-
--------------------------------------------------------------------
-Tue Oct 7 14:39:23 CEST 2014 - mls@suse.de
-
-- fix bug in reorder_dq_for_jobrules leading to crashes
- [bnc#899907]
-- rename rpm rules to pkg rules
-- add manpages for the tools
-- bump version to 0.6.6
-
--------------------------------------------------------------------
-Thu Sep 11 17:33:04 CEST 2014 - mls@suse.de
-
-- support DUCHANGES_ONLYADD flag in diskusage calculation
- [bnc#783100]
-- add whatmatchesdep to bindings
-- support pool->considered in testcases
-- always call selection_filelist if SELECTION_FILELIST is set
-- support yum style obsolete handling for package splits
-- bump version to 0.6.5
-
--------------------------------------------------------------------
-Tue Jul 8 14:13:40 CEST 2014 - mls@suse.de
-
-- expand solver_identical fix to applications [bnc#885830]
-- fix instbuddy generation code
-- improve autominimizing implementation to also look at
- supplements
-- bump version to 0.6.4
-
--------------------------------------------------------------------
-Fri Jun 13 08:28:12 CEST 2014 - ma@suse.de
-
-- quick fix for [bnc#881320]
-- bump version to 0.6.3
-
--------------------------------------------------------------------
-Fri Jun 6 11:37:00 CEST 2014 - ma@suse.de
-
-- Provide PRODUCT_REGISTER_TARGET for available products [bnc#881320]
-- bump version to 0.6.2
-
--------------------------------------------------------------------
-Thu Apr 17 14:47:42 CEST 2014 - mls@suse.de
-
-- support BREAK_ORPHANS and KEEP_ORPHANS solver flags
-- adapt to AppStream 0.6
-- reduce memory usage in repo_write and repodata_internalize
-- make repodata_stringify return the result string
-- bump version to 0.6.1
-
--------------------------------------------------------------------
-Mon Apr 7 15:36:07 CEST 2014 - mls@suse.de
-
-- add support for sha224/sha384/sha512
-- add userinstalled helper functions
-- improve dataiterator bindings (in an incompatible way)
-- automatically free pool in bindings
-- bump version to 0.6.0 (ABI + bindings API breakage)
-
--------------------------------------------------------------------
-Wed Mar 26 15:08:46 CET 2014 - mls@suse.de
-
-- fix handling of packages with no update/feature rule [bnc#870195]
-- fix crash when in internalize() when the schemadata gets
- reallocated
-- fix access to uninitialized memory in repo_empty()
-- a couple of optimizations
-- support REPOKEYWORDS in content parser
-- bindings: don't let str(Datamatch) change the strings
-- fix basename optimization for STRINGEND
-- bump version to 0.5.1
-
--------------------------------------------------------------------
-Wed Feb 26 15:08:35 CET 2014 - mls@suse.de
-
-- improve appdata.xml parsing [bnc#865293]
-- repo_helix: parse application elements
-- bump version to 0.5.0
-
--------------------------------------------------------------------
-Fri Feb 21 16:23:58 CET 2014 - mls@suse.de
-
-- fix bug in solver_get_unneeded that could lead to an
- endless loop [bnc#828764]
-- adapt to new rpm tags for weak dependencies
-- fix pseudo packages obsoleting other pseudo packages
-- optimize unfulfilled rule handling a bit
-- bump version to 0.4.5
-
--------------------------------------------------------------------
-Fri Feb 14 11:03:18 CET 2014 - mls@suse.de
-
-- always keep job/jobflags in selection_filter()
-- implement COND handling in supplements/enhances
-- improve OR handling in supplements/enhances
-- fix typo in application backlink creation
-- support PRODUCT_ENDOFLIFE
-- store repoid as flexarray
-- also translate autoproduct strings
-- bump version to 0.4.4
-
--------------------------------------------------------------------
-Mon Jan 27 17:15:41 CET 2014 - mls@suse.de
-
-- add support for autogenerated products
-- support repoid array in product definition
-- bump version to 0.4.3
-
--------------------------------------------------------------------
-Fri Jan 24 13:50:03 CET 2014 - mls@suse.de
-
-- add -X option to susetags2solv [bnc#860271]
-- fix typos in pool_job2str
-- support DISTRO in content parser
-- make addfilelist more resistant against corrupt rpms
-- encode flags into rpmdb cookie
-- add identical and evrcmp methods in bindings
-- copy checksums in repo_add_rpmdb_reffp
-- repo_autopattern: make sure that the category is valid utf8
-- fix double-free if the number of languages is reduced to zero
-- bump version to 0.4.2
-
--------------------------------------------------------------------
-Mon Dec 9 11:53:06 CET 2013 - mls@suse.de
-
-- make repo2solv.sh work when appdata support is off
-- enable appdata support for SUSE
-
--------------------------------------------------------------------
-Tue Dec 3 14:30:17 CET 2013 - mls@suse.de
-
-- support appdata parsing and auto-pattern generation in tools
-- adapt to python3
-- tweak findproblemrule heuristic for conflicts
-- add m68k/ppc64le architectures
-- plug weakrulemap memory hole
-- support debian multiarch annotation
-- support product/pattern/application links
-- bump version to 0.4.1
-
--------------------------------------------------------------------
-Tue Sep 24 11:14:25 CEST 2013 - mls@suse.de
-
-- do not add back cleandeps_updatepkgs packages [bnc#841781]
-
--------------------------------------------------------------------
-Mon Sep 23 11:31:28 CEST 2013 - mls@suse.de
-
-- added repodata_lookup_binary
-- fix pattern obsoleting real packages [bnc#834376]
-- add selection_make_matchdeps function to query dependencies
- other than provides
-- bump version to 0.4.0
-
--------------------------------------------------------------------
-Wed Aug 21 14:39:54 CEST 2013 - mls@suse.de
-
-- add describe_decision() and unset() methods to bindings
-- do recommends pruning after selecting the highest versions
-- improve filelist handling
-- be more tolerant about bad xml: an empty epoch attribute means no epoch
-- fix another edge case will dup mode and multiversion packages [bnc#828389]
-- bring installcheck up to speed
-
--------------------------------------------------------------------
-Mon Jun 17 15:54:44 CEST 2013 - mls@suse.de
-
-- add SOLVER_RULE_JOB_UNSUPPORTED and SOLVER_RULE_JOB_UNKNOWN_PACKAGE
- for better problem reporting
-- start with library documentation
-- adapt to obsoletes handling of new rpm versions
-
--------------------------------------------------------------------
-Wed Mar 6 16:55:57 CET 2013 - mls@suse.de
-
-- fix dataiterator returning random data in some cases
-- add changelog parser
-- fix nasty bug in selection_filter_rel
-- allow re-run of an existing solver
-- bump version to 0.3.0
-
--------------------------------------------------------------------
-Mon Jan 14 16:01:04 CET 2013 - mls@suse.de
-
-- trivial_installable: check vendor of affected package to see if
- a patch should be ignored [bnc#736100]
-- fix trivial installable requires handling
-- bump version to 0.2.4
-
--------------------------------------------------------------------
-Tue Dec 18 19:20:19 CET 2012 - mls@suse.de
-
-- fix potential access to freed memory
-- improve find_problemrule speed
-- add support for special namespaceprovides jobs
-- bump version to 0.2.3
-
--------------------------------------------------------------------
-Wed Dec 5 14:37:39 CET 2012 - mls@suse.de
-
-- many Selection improvements
-- fix perl binding memory issue
-- improve file list matching
-- support targeted up/dup with cleandeps
-- bump version to 0.2.2
-
--------------------------------------------------------------------
-Fri Nov 23 13:57:19 CET 2012 - mls@suse.de
-
-- support targeted up/dup
-- support best rules to enforce the installation of the best package
-- implement selection class to ease package selection
-- fix obsolete handling for debian packages
-- add pool_lookup_deltalocation helper
-- bump version to 0.2.1
-
--------------------------------------------------------------------
-Thu Oct 18 16:58:10 CEST 2012 - mls@suse.de
-
-- fix susetags parser, it ignored the filelist of the last
- solvable
-- fix encoding of large values
-- bump version to 0.2.0
-
--------------------------------------------------------------------
-Mon Jun 25 13:40:58 CEST 2012 - mls@suse.de
-
-- fix typo in repodata_merge_attrs [bnc#767510]
-
--------------------------------------------------------------------
-Wed May 30 14:46:48 CEST 2012 - mls@suse.de
-
-- fix build for older suse versions
-- fix memory corruption in unneeded calculation when there are
- product buddies
-
--------------------------------------------------------------------
-Tue May 8 10:59:39 CEST 2012 - ma@suse.de
-
-- build with swig-2.0.6
-
--------------------------------------------------------------------
-Mon Apr 23 15:52:26 CEST 2012 - mls@suse.de
-
-- added testcase framework
-- add solver_get_unneeded() to get a list of no longer needed
- installed packages
-- changed duprule generation to ignore uninstallable packages [bnc#750485]
-- fix memory leaks
-- speed up whatprovides generation
-- support 64bit nums, return files sizes in bytes
-- return errors instead of calling exit()
-- support tilde in rpm version comparison [bnc#466994]
-- 0.1.0
-
--------------------------------------------------------------------
-Tue Feb 7 16:33:10 CET 2012 - mls@suse.de
-
-- add findutils to the requires of libsolv-tools [bnc#743634]
-
--------------------------------------------------------------------
-Wed Feb 1 14:06:59 CET 2012 - mls@suse.de
-
-- add cleandeps support for install/update
-- check for cleandeps mistakes (untested)
-
--------------------------------------------------------------------
-Fri Jan 27 14:10:34 CET 2012 - mls@suse.de
-
-- make repo type code selection modular
-- add more solvable_ functions
-- add dependency getter/setter code to bindings
-- cleanup dup handling
-- hide more internals
-
--------------------------------------------------------------------
-Mon Oct 24 13:26:18 CEST 2011 - ma@suse.de
-
-- mls fixed package provides/obsoletes, but forgot to write
- a changes entry.
-
--------------------------------------------------------------------
-Tue Oct 18 16:18:39 CEST 2011 - ma@suse.de
-
-- Add arch arvm7tnhl and armv7thl
-
--------------------------------------------------------------------
-Fri Apr 29 14:35:59 CEST 2011 - mls@suse.de
-
-- bup version to 0.17.0 to make it different from 11.4
-- 0.17.0
-
--------------------------------------------------------------------
-Thu Mar 24 10:04:16 UTC 2011 - mls@suse.de
-
-- add missing else part in rpmdbid2db()
-
--------------------------------------------------------------------
-Thu Feb 24 17:44:05 CET 2011 - mls@suse.de
-
-- ignore to be dropped orhaned packages when calculating
- candidates for recommends/supplements installation
-
--------------------------------------------------------------------
-Wed Feb 2 09:24:42 UTC 2011 - kkaempf@novell.com
-
-- Split off 'applayer' and 'bindings' as a separate package
- to make the bindings more independant of libsatsolver
-- 0.16.4
-
--------------------------------------------------------------------
-Tue Jan 25 09:52:48 CET 2011 - ma@suse.de
-
-- Apply patch introducing armv7nhl:armv7h
-
--------------------------------------------------------------------
-Fri Oct 22 15:51:11 CEST 2010 - ma@suse.de
-
-- updateinfoxml: Correctly parse 'issued date' field.
-- 0.16.1
-
--------------------------------------------------------------------
-Thu Sep 9 17:30:36 CEST 2010 - mls@suse.de
-
-- bump version to 0.16 to make it different from code11_3 branch
-- 0.16.0
-
--------------------------------------------------------------------
-Mon Sep 6 13:50:02 UTC 2010 - dmacvicar@novell.com
-
-- ruby bindings: fix bugs regarding include path loading
- (was hardcoded) and refactor the way the library path is defined
- (only once in a helper)
-
--------------------------------------------------------------------
-Mon Sep 6 12:38:21 UTC 2010 - dmacvicar@novell.com
-
-- SLE10SP3 also has vendor_ruby
-
--------------------------------------------------------------------
-Wed Aug 18 14:11:08 UTC 2010 - kkaempf@novell.com
-
-- refactor ruby-satsolver, pure-Ruby extensions added
-- 0.15.1
-
--------------------------------------------------------------------
-Thu May 6 17:39:20 CEST 2010 - mls@suse.de
-
-- fix bug in cleandeps code
-- bump version to 0.15 to make it different from code11_2 branch
-- 0.15.0
-
--------------------------------------------------------------------
-Tue Mar 23 17:24:46 CET 2010 - ma@suse.de
-
-- Parse an installed products <shortsummary> tag [bnc#586303]
-
--------------------------------------------------------------------
-Mon Mar 22 18:45:42 CET 2010 - mls@suse.de
-
-- dataiterator: reset parent when jumping to a solvid [bnc#589640]
-- 0.14.17
-
--------------------------------------------------------------------
-Thu Mar 11 22:13:26 CET 2010 - ma@suse.de
-
-- parse global repository ids. [bnc#377568]
-- fix pattern parsing in repomd format. [bnc#585000]
-- 0.14.16
-
--------------------------------------------------------------------
-Thu Mar 11 12:18:23 CET 2010 - mls@suse.de
-
-- fix language code lookup for fallback languages [bnc#584644]
-- change solvable_lookup_str_lang interface a bit for libzypp
-
--------------------------------------------------------------------
-Fri Feb 19 17:31:51 CET 2010 - mls@suse.de
-
-- make dup rules work when system repo is not first [bnc#581276]
-- parse pattern visibility flag in repomd format
-- 0.14.15
-
--------------------------------------------------------------------
-Fri Jan 29 14:48:34 CET 2010 - mls@suse.de
-
-- speed up createwhatprovides when many solvables provide the same dep
-- fix choice rule creation for real (bnc#551637)
-- 0.14.14
-
--------------------------------------------------------------------
-Mon Jan 18 14:42:27 CET 2010 - mls@suse.de
-
-- set repository:toolversion to 1.0 in common_write
-- 0.14.13
-
--------------------------------------------------------------------
-Mon Dec 21 14:29:24 CET 2009 - mls@suse.de
-
-- disable update rule in noobsoletes case if installed package is to
- be kept [bnc#564239]
-- work around rpm bug when --prefix is used [bnc#565525]
-- add support for sparc architecture [bnc#566291]
-- 0.14.12
-
--------------------------------------------------------------------
-Mon Dec 7 13:59:08 CET 2009 - ma@suse.de
-
-- Support optionally compressed product(s).xml in rpmmd metadata.
-- 0.14.11
-
--------------------------------------------------------------------
-Mon Nov 2 14:10:23 CET 2009 - mls@suse.de
-
-- look at infarch/dup rules when creating choice rules, makes dup
- also install 32bit packages [bnc#551637]
-- 0.14.10
-
--------------------------------------------------------------------
-Wed Oct 14 16:21:32 CEST 2009 - mls@suse.de
-
-- ignore products element so that repo2solv works
-- support MULTI_SEMANTICS
-- add --withsrc option for installcheck
-- add SOLVER_DROP_ORPHANED job type
-- fix dropped package handling in removedisabledconflicts
-- 0.14.9
-
--------------------------------------------------------------------
-Thu Sep 24 10:27:42 CEST 2009 - mls@suse.de
-
-- fix bug in solvable_lookup_str_base
-
--------------------------------------------------------------------
-Wed Sep 23 11:10:08 CEST 2009 - mls@suse.de
-
-- get missing translations from other solvables [bnc#386449]
-- also look at triggers when ordering packages
-- add support for REPOKEY_TYPE_BINARY
-- 0.14.8
-
--------------------------------------------------------------------
-Wed Sep 16 10:56:44 CEST 2009 - mls@suse.de
-
-- use repomdxml to query for the location [bnc#501425]
-- fix assertion failue... again
-- fix fp leak [bnc#535468]
-- 0.14.7
-
--------------------------------------------------------------------
-Fri Sep 4 11:40:47 CEST 2009 - mls@suse.de
-
-- fix multiversion handling for real
-- fix speed regression in repo_susetags
-- close fd leak [bnc#527506]
-- remove db.h workaround
-- 0.14.6
-
--------------------------------------------------------------------
-Mon Aug 31 13:57:29 CEST 2009 - mls@suse.de
-
-- fix to build with rpm-4.7
-
--------------------------------------------------------------------
-Fri Aug 28 11:06:31 CEST 2009 - ma@suse.de
-
-- add support for SOLVER_SOLVABLE_REPO
-- 0.14.5
-
--------------------------------------------------------------------
-Thu Jul 30 12:49:38 CEST 2009 - mls@suse.de
-
-- speed up file list parsing
-- speed up addfileprovides
-- fix bugs in pubkey handling
-- fix bug in filelist handling when there are no abs paths
-- 0.14.4
-
--------------------------------------------------------------------
-Fri Jul 17 14:07:07 CEST 2009 - mls@suse.de
-
-- add satversion.h header file
-- many changes regarding the load callback
-- more dataiterator control functions
-- add transaction ordering code
-- add support for file conflict checking
-- 0.14.3
-
--------------------------------------------------------------------
-Mon Jun 22 16:01:02 CEST 2009 - mls@suse.de
-
-- create libsatsolverext.a
-- 0.14.2
-
--------------------------------------------------------------------
-Wed May 6 19:52:39 CEST 2009 - ma@suse.de
-
-- Fix to support sha256 hashes (bnc#501425)
-
--------------------------------------------------------------------
-Wed Apr 1 10:32:43 CEST 2009 - kkaempf@suse.de
-
-- 0.14.1
- Core changes
- - fix potential NULL deref in srcrpm handling (mls)
- - clean up and fix suggested/recommended list creation code (mls)
- - kill obsolete and broken patchxml support (mls)
- - replace old solver_problemruleinfo with new solver_ruleinfo
- function (mls)
- - add solvable_selfprovidedep() function (mls)
- - add noinfarchcheck solver option (mls)
- - clone job queue to make the code more flexible (mls)
- - rewrite policy rule disabling/re-enabling (bnc#481836) (mls)
- - fix out-of-bounds in solver_printproblem() (mls)
- Bindings changes
- - provide 'Repo.attr(String)' accessor function (kkaempf)
- - provide 'Solver.sizechange' to compute the changes of the
- installed size after a transaction is applied (kkaempf)
- - solver result iterators for Python (jblunck)
- - add %newobject to track newly created objects better (kkaempf)
- - generate rdoc documentation from swig input files (kkaempf)
- - add a no-op Pool destructor (bnc#483252) (kkaempf)
- - provide access to all flags and settings (kkaempf)
-
--------------------------------------------------------------------
-Wed Mar 4 14:39:00 CET 2009 - mls@suse.de
-
-- fix problem_to_solutions segfault
-- bump version to 0.14 to make it different from code11 branch
-- 0.14.0
-
--------------------------------------------------------------------
-Mon Mar 2 18:20:22 CET 2009 - mls@suse.de
-
-- add solver_trivial_installable() to fix multiversion patches [bnc#480303]
-- 0.13.5
-
--------------------------------------------------------------------
-Wed Feb 18 16:48:41 CET 2009 - ma@suse.de
-
-- Use correct namespace (e.g. pattern:) even if solvable has no name [bnc#470011]
-
--------------------------------------------------------------------
-Fri Jan 30 14:26:42 CET 2009 - mls@suse.de
-
-- fix weakmap boundary check
-- 0.13.3
-
--------------------------------------------------------------------
-Tue Jan 27 13:12:30 CET 2009 - ma@suse.de
-
-- Ignore trailing whitespace in content file parser.
-
--------------------------------------------------------------------
-Mon Jan 26 13:55:35 CET 2009 - mls@suse.de
-
-- fix segfault caused by whatprovides reallocation [bnc#468313]
-- 0.13.1
-
--------------------------------------------------------------------
-Tue Jan 20 17:12:17 CET 2009 - ma@suse.de
-
-- repo_rpmdb: Fix conversion to UTF8
-
--------------------------------------------------------------------
-Fri Dec 5 14:43:55 CET 2008 - kkaempf@suse.de
-
-- enhance bindings to provide reasons for solver decisions
-
--------------------------------------------------------------------
-Mon Dec 1 11:50:12 CET 2008 - mls@suse.de
-
-- prefer patterns again until there's a real fix [bnc#450226]
-
--------------------------------------------------------------------
-Mon Dec 1 11:13:55 CET 2008 - mls@suse.de
-
-- susetags: fix file dependency parsing [bnc#450286]
-
--------------------------------------------------------------------
-Fri Nov 28 18:29:08 CET 2008 - mls@suse.de
-
-- correct problem solving algorithm
-- 0.13.0
-
--------------------------------------------------------------------
-Fri Nov 21 16:54:44 CET 2008 - dmacvicar@suse.de
-
-- remove unused updaterepokey, replaced by repo
- product information
-
--------------------------------------------------------------------
-Thu Nov 20 10:32:01 CET 2008 - dmacvicar@suse.de
-
-- support content, distro and updates tag properly
-- remove the unused products tag
-
--------------------------------------------------------------------
-Mon Nov 17 14:30:08 CET 2008 - ma@suse.de
-
-- Parse RELEASE tag from contentfile (bnc #444978)
-- 0.12.3
-
--------------------------------------------------------------------
-Fri Nov 14 18:00:19 CET 2008 - mls@suse.de
-
-- fix bugs in multiversion handling
-- bring perl bindings up to speed
-- 0.12.2
-
--------------------------------------------------------------------
-Fri Nov 7 18:26:07 CET 2008 - ma@suse.de
-
-- fix SOLVID_POS dataiterator handling
-
--------------------------------------------------------------------
-Fri Nov 7 11:56:37 CET 2008 - kkaempf@suse.de
-
-- make repo_zyppdb more robust (bnc#441043)
-
--------------------------------------------------------------------
-Thu Nov 6 09:49:51 CET 2008 - kkaempf@suse.de
-
-- Add 'user_data' argument to all applayer iterator callbacks
- (bnc#418491)
-
--------------------------------------------------------------------
-Wed Oct 29 12:36:57 CET 2008 - ma@suse.de
-
-- Add 'sh4' architectures.
-
--------------------------------------------------------------------
-Tue Oct 28 16:55:03 CET 2008 - ma@suse.de
-
-- Add 'arm' architectures.
-
--------------------------------------------------------------------
-Mon Oct 27 10:54:36 CET 2008 - kkaempf@suse.de
-
-- Improve error detection and reporting when parsing 'content' file
-
--------------------------------------------------------------------
-Fri Oct 24 16:03:13 CEST 2008 - ma@suse.de
-
-- Remember the /etc/products.d enties filename in .solv
- (bnc #432932)
-- 0.12.1
-
--------------------------------------------------------------------
-Thu Oct 23 10:49:40 CEST 2008 - mrueckert@suse.de
-
-- add zlib-devel as buildrequires, cmake is looking for it
- explicitely. rpm-devel used to require it but you dont really
- need it to link against librpm*
-- requires rpm-devel in the devel package, it is required to link
- against libsatsolver.
-
--------------------------------------------------------------------
-Wed Oct 22 13:00:56 CEST 2008 - mls@suse.de
-
-- add pool_set_installed()
-- drop "installed" arguments from some functions
-- 0.12.0
-
--------------------------------------------------------------------
-Wed Oct 22 13:00:25 CEST 2008 - dmacvicar@suse.de
-
-- support the new standarized tags available in
- createrepo > 0.9.6
- saving of the distro tag still missing.
-
--------------------------------------------------------------------
-Thu Oct 16 00:50:47 CEST 2008 - mls@suse.de
-
-- make iterator work with completely empty repos [bnc#435838]
-
--------------------------------------------------------------------
-Tue Oct 14 18:28:57 CEST 2008 - mls@suse.de
-
-- the big solv data change
- * incompatible new file format
- * repodata handles are solvable ids
- * no more extra handles
- * no need to call repodata_extend anymore
-- work around solver dup repo priority bug, real fix follows soon
-- implement releasever
-- repo_products.c is now more robust
-
--------------------------------------------------------------------
-Mon Oct 13 16:50:14 CEST 2008 - kkaempf@suse.de
-
-- make product parsing more robust (bnc#433362)
-
--------------------------------------------------------------------
-Thu Oct 2 18:46:55 CEST 2008 - ma@suse.de
-
-- Product arttributes: removed FLAVOR and REFERENCES, added PRODUCTLINE.
-- revision 11233
-- 0.11.0
-
--------------------------------------------------------------------
-Tue Sep 30 13:03:10 CEST 2008 - ma@suse.de
-
-- repo_content.c: fix broken dependency parsing.
-- revision 11214
-
--------------------------------------------------------------------
-Mon Sep 29 14:53:09 CEST 2008 - ma@suse.de
-
-- rpms2solv failed to write out most solvable data (bnc #422338).
-- revision 11201
-
--------------------------------------------------------------------
-Fri Sep 26 11:54:34 CEST 2008 - kkaempf@suse.de
-
-- new fallback strategy for installed products in rpmdb2solv
- try /etc/products.d (code11 style) first
- if this fails, try /var/lib/zypp/db/products/*
- if this fails, fallback to /etc/*-release
- (bnc#429177)
-- 0.10.16
-
--------------------------------------------------------------------
-Thu Sep 25 17:14:42 CEST 2008 - kkaempf@suse.de
-
-- fully support Dataiterator in Python and Ruby bindings.
-- 0.10.15
-
--------------------------------------------------------------------
-Thu Sep 25 13:53:54 CEST 2008 - dmacvicar@suse.de
-
-- add support for keywords in susedata
-
--------------------------------------------------------------------
-Wed Sep 24 10:55:01 CEST 2008 - kkaempf@suse.de
-
-- parse /etc/<xyz>-release if no /etc/products.d present
- (bnc#429177)
-- 0.10.14
-
--------------------------------------------------------------------
-Mon Sep 22 12:51:51 CEST 2008 - dmacvicar@suse.de
-
-- ability to parse suseinfo.xml for extended repomd.xml attributes
-- fix susedata.xml parsing
-- add CPE attribute to installed product
-- real fix for segfault in multiarch parsing (bnc#427271)
-- 0.10.13
-
--------------------------------------------------------------------
-Thu Sep 18 14:44:31 CEST 2008 - dmacvicar@suse.de
-
-- fix segfault in multiarch parsing (bnc#427271)
-
--------------------------------------------------------------------
-Wed Sep 17 14:10:23 CEST 2008 - mls@suse.de
-
-- fix segfault in provides iterator
-- 0.10.12
-
--------------------------------------------------------------------
-Fri Sep 12 17:32:02 CEST 2008 - dmacvicar@suse.de
-
-- support for susedata.xml
-
--------------------------------------------------------------------
-Fri Sep 12 14:01:11 CEST 2008 - dmacvicar@suse.de
-
-- add repo_add_poolstr_array
-- move updates="key,key.." to repomd.xml
-- make product url ids more extensible
-- 0.10.11
-
--------------------------------------------------------------------
-Thu Sep 11 14:30:16 CEST 2008 - dmacvicar@suse.de
-
-- add REPOSITORY_UPDATES to match product -> repos
-- make updateinfo.xml support id attribute in collection that
- leads to insert that the repository updates that id.
-
--------------------------------------------------------------------
-Wed Sep 10 18:11:10 CEST 2008 - dmacvicar@suse.de
-
-- create one product per BASEARCHS
-
--------------------------------------------------------------------
-Wed Sep 10 14:19:26 CEST 2008 - ma@suse.de
-
-- repo_products: Parse schemeversion, propagate product
- updaterepokey and flavor. Fix segfault on malformed
- xml.
-- 0.10.10
-
--------------------------------------------------------------------
-Wed Sep 10 11:57:27 CEST 2008 - dmacvicar@suse.de
-
-- accept the PATTERNS tag in content file
-
--------------------------------------------------------------------
-Tue Sep 9 21:26:31 CEST 2008 - kkaempf@suse.de
-
-- rpmdb2solv changes:
- - fix bug when parsing multiple products
- - adapt to .prod file as of 9/9/08 7:20pm
-- 0.10.9
-
--------------------------------------------------------------------
-Tue Sep 9 17:52:30 CEST 2008 - ma@suse.de
-
-- Reenable -Werror and fix bindings.
-- 0.10.8
-
--------------------------------------------------------------------
-Tue Sep 9 11:50:39 CEST 2008 - dmacvicar@suse.de
-
-- disable -Werror for swig generated stuff
-
--------------------------------------------------------------------
-Fri Sep 5 18:59:35 CEST 2008 - kkaempf@suse.de
-
-- adapt /etc/product.d parser to generated .prod files.
-
--------------------------------------------------------------------
-Fri Sep 5 13:29:47 CEST 2008 - ma@suse.de
-
-- tools/repo_susetags.c: Parse packages vendor (bnc #422493).
-- 0.10.7
-
--------------------------------------------------------------------
-Thu Sep 4 12:30:06 CEST 2008 - kkaempf@suse.de
-
-- tools/rpmdb2solv: Adapt to xml-based /etc/products.d
-- tools/rpmdb2solv: Add '-a <attribute>' to print
- distribution.target attribute of baseproduct.
-
--------------------------------------------------------------------
-Tue Sep 2 12:17:03 CEST 2008 - mls@suse.de
-
-- make solver includes use "" instead of <>, fixes bnc#415920
-- implement otherproviders()
-- make patches do nevr matching
-- make patch conflicts work with multiversion
-- new job commands, now combinded from job type and select type
-- support for distupgrade mode
-- make SOLVER_ERASE_SOLVABLE work
-- also check obsoletes when disabling update rules
-- 0.10.6
-
--------------------------------------------------------------------
-Fri Aug 22 18:04:12 CEST 2008 - dmacvicar@suse.de
-
-- add support for extensible metadata over primary +
- diskusage
-- 0.10.5
-
--------------------------------------------------------------------
-Fri Aug 15 16:29:00 CEST 2008 - kkaempf@suse.de
-
-- ensure existance of product solvable in repo_content(bnc#417594)
-
--------------------------------------------------------------------
-Fri Aug 15 15:00:32 CEST 2008 - kkaempf@suse.de
-
-- follow /etc/products.d/baseproduct and mark product as 'base'
-
--------------------------------------------------------------------
-Fri Aug 15 14:26:29 CEST 2008 - kkaempf@suse.de
-
-- Implement pre-code11 fallback for products, parse /etc/*-release
- if /etc/products.d is not available.
-
--------------------------------------------------------------------
-Wed Aug 13 18:06:41 CEST 2008 - kkaempf@suse.de
-
-- provide installtime for installed products.
-
--------------------------------------------------------------------
-Wed Aug 13 15:42:36 CEST 2008 - dmacvicar@suse.de
-
-- include new file search capability commited by matz
- (SEARCH_FILES)
-- 0.10.4
-
--------------------------------------------------------------------
-Wed Aug 13 10:31:01 CEST 2008 - kkaempf@suse.de
-
-- Honor rpmdb2solv's '-r <root>' also for the products.d path.
-
--------------------------------------------------------------------
-Tue Aug 12 14:22:29 CEST 2008 - kkaempf@suse.de
-
-- Add .prod parsing for 'installed' products to rpmdb2solv.
-- Improve python-bindings, add iterators.
-
--------------------------------------------------------------------
-Thu Aug 7 22:00:55 CEST 2008 - dmacvicar@suse.de
-
-- implement relogin suggested support (fate#304889)
-
--------------------------------------------------------------------
-Thu Aug 7 13:36:59 CEST 2008 - ma@suse.de
-
-- Susetags: Allow whitespace in file provides generated by
- autobuild (bnc#415115)
-
--------------------------------------------------------------------
-Fri Aug 1 18:59:22 CEST 2008 - dmacvicar@suse.de
-
-- insert the checksum in rpmmd generated solv files
- (bnc#414002)
-- 0.10.3
-
--------------------------------------------------------------------
-Tue Jul 29 10:53:22 CEST 2008 - mls@suse.de
-
-- resolve job rules before installing system packages [bnc#411086]
-- no more freshens. R.I.P.
-- make repo2solv.sh also take repomd.xml in count
-- install repomdxml2solv
-- add Packager, Build Host, Distribution
-- disallow arch/vendor changes even if the package name changes
-
--------------------------------------------------------------------
-Sat Jul 12 02:32:15 CEST 2008 - dmacvicar@suse.de
-
-- infrastructure to save generated and expiration time
- stamp in rpm-md repositories (fate #301904)
-- 0.10.1
-
--------------------------------------------------------------------
-Wed Jul 9 16:25:36 CEST 2008 - ma@suse.de
-
-- Fix repo_content dependency parsing. Parser may lose up to
- two trailing dependencies.
-
--------------------------------------------------------------------
-Tue Jul 1 14:54:38 CEST 2008 - kkaempf@suse.de
-
-- rename language bindings to {perl,python,ruby}-satsolver
- to follow naming conventions.
-
--------------------------------------------------------------------
-Mon Jun 30 23:55:20 CEST 2008 - dmacvicar@suse.de
-
-- forward port
-- add message tag to updateinfo.xml for displaying
- messages in the user interface
-- Fix missing self provides for patches (bnc #397132).
-- do not reorder binary rules if they are not rpm rules [bnc#397456]
-- 0.10.0
-
--------------------------------------------------------------------
-Mon Jun 2 11:47:32 CEST 2008 - coolo@suse.de
-
-- calculate recommendation list also if ignorealreadyrecommended is set,
- as some recommendations would be missing otherwise
-- make dependency output less confusing (bnc#396309)
-- 0.9.0
-
--------------------------------------------------------------------
-Tue May 27 22:34:05 CEST 2008 - coolo@suse.de
-
-- compile with RPM_OPT_FLAGS
-
--------------------------------------------------------------------
-Fri May 23 21:07:01 CEST 2008 - mls@suse.de
-
-- add "zypper" flag
-- add "ignorealreadyrecommended" aka "zypper" solver option
-
--------------------------------------------------------------------
-Thu May 22 20:16:22 CEST 2008 - coolo@suse.de
-
-- fixed language support in patterns (bnc#386524)
-
--------------------------------------------------------------------
-Mon May 19 14:53:01 CEST 2008 - dmacvicar@suse.de
-
-- make solvable_look_bool more robust by allowing both
- the void or the num == 1 strategy.
-
--------------------------------------------------------------------
-Thu May 15 16:05:50 CEST 2008 - coolo@suse.de
-
-- fix susetags parser
-
-------------------------------------------------------------------
-Wed May 14 16:41:26 CEST 2008 - dmacvicar@suse.de
-
-- mls fix of satisfied patterns
-- 0.0.33
-
--------------------------------------------------------------------
-Tue May 13 17:14:26 CEST 2008 - dmacvicar@suse.de
-
-- use repodata_set_void for pattern visible attr
-
--------------------------------------------------------------------
-Tue May 13 14:11:30 CEST 2008 - jreidinger@suse.cz
-
-- read description dir path from content file (bnc #389414)
-
--------------------------------------------------------------------
-Tue May 13 12:20:58 CEST 2008 - dmacvicar@suse.de
-
-- a boolean is not a num attribute set to 1, but just a existing void
- attribute. (bnc#388818)
-
--------------------------------------------------------------------
-Mon May 12 10:16:20 CEST 2008 - coolo@suse.de
-
-- provide libsatsolver to fix requires of debuginfo
-
--------------------------------------------------------------------
-Sat May 10 15:33:15 CEST 2008 - coolo@suse.de
-
-- resubmit clean tar
-
--------------------------------------------------------------------
-Fri May 9 21:56:43 CEST 2008 - ma@suse.de
-
-- Parse the products LABEL in content file to SUMMARY.
-
--------------------------------------------------------------------
-Fri May 9 20:26:52 CEST 2008 - dmacvicar@suse.de
-
-- recognize 1 as true for reboot suggested and
- restart suggested (bnc#388818)
-
--------------------------------------------------------------------
-Fri May 9 16:04:42 CEST 2008 - kkaempf@suse.de
-
-- move 'helix2solv' from satsolver-tools to satsolver-devel package
- (bnc#388595)
-
--------------------------------------------------------------------
-Mon May 5 16:24:14 CEST 2008 - coolo@suse.de
-
-- add obsoleteusesprovides and implicitobsoleteusesprovides solver
- flags
-- speed up solving by not recreating the watch chains every time
- some rule is enabled/disabled. To do this, the "disabled" flag
- had to be moved from w1 to d.
-- fix bug that broke rule disabling when "forceResolve" was true
-- fix bug in update rule calculation
-- speed up solver a bit by creating a queue holding all assertion
- rules, so we do not have to scan all rules for assertions
-- parse also DISTPRODUCT and DISTVERSION (for registration), and the other
- (often unused) attributes of products.
-
--------------------------------------------------------------------
-Mon Apr 28 19:36:54 CEST 2008 - coolo@suse.de
-
-- (De-)Serialize structured types. Dataiterator and repo_search support
- them too, but not yet nested, so that is unsupported for now.
-- skipping kinds in matcher when a flag is specified.
-- make --force behave a bit more like --noforce
-
--------------------------------------------------------------------
-Fri Apr 25 19:23:51 CEST 2008 - coolo@suse.de
-
-- detect and skip empty lines (bnc#381828)
-- fix endless loop
-- move debug functions to solverdebug.c
-- do not delete negative bitfield entries [bnc#381908]
-- add "showinstalledrecommended" option to make the solver
- put installed packages on the suggestions/recommendations
- queues
-- Fix content parsing if PRODUCT isn't the first entry.
-- add more statistics
-- add two assertions
-- add support for susetags filelist
-- plug mem join2 leak
-- fix anchoring of filelist data
-- susetags move files added to provides back into filelist
-- ignore packages.FL for now
-
--------------------------------------------------------------------
-Sun Apr 20 18:25:14 CEST 2008 - coolo@suse.de
-
-- fix build
-
--------------------------------------------------------------------
-Sat Apr 19 08:10:51 CEST 2008 - coolo@suse.de
-
-- fix probleminfo if solvable conflicts with itself and has no requires
-- Fix parsing dep lines of content files (bnc #380396).
-- C++-guard also solver.h
-- add support for feature rules
-- fix a couple of small bugs
-- use new interface
-
--------------------------------------------------------------------
-Wed Apr 16 17:44:20 CEST 2008 - coolo@suse.de
-
-- fix (rare) case of crashing solver
-
--------------------------------------------------------------------
-Tue Apr 15 09:06:36 CEST 2008 - coolo@suse.de
-
-- some fixes around updateinfo parsing
-
--------------------------------------------------------------------
-Wed Apr 9 16:09:36 CEST 2008 - jkupec@suse.cz
-
-- enable regex matching in Dataiterator
-
--------------------------------------------------------------------
-Fri Apr 4 15:45:44 CEST 2008 - coolo@suse.de
-
-- package deptestomatic in -devel
-
--------------------------------------------------------------------
-Mon Mar 31 16:25:03 CEST 2008 - coolo@suse.de
-
-- truly restart when analyze_unsolvable is hit (fixes bnc#368209)
-- make it work with really large directories
-
--------------------------------------------------------------------
-Mon Mar 24 15:31:18 CET 2008 - coolo@suse.de
-
-- install rpms2solv
-
--------------------------------------------------------------------
-Mon Mar 17 16:10:22 CET 2008 - matz@suse.de
-
-- Initialize all allocated array members for blocky arrays (when it
- matters, e.g. when extending also in blocks - bnc#371137)
-
--------------------------------------------------------------------
-Fri Mar 14 08:37:47 CET 2008 - coolo@suse.de
-
-- fix build on other distris
-- fix requires of tools
-
--------------------------------------------------------------------
-Wed Mar 12 11:45:21 CET 2008 - coolo@suse.de
-
-- store datadir for susetags
-- fixing assertion in rules learning
-
--------------------------------------------------------------------
-Fri Mar 7 10:51:09 CET 2008 - coolo@suse.de
-
-- several fixes in whatprovides (possibly root cause of bnc#367210)
-
--------------------------------------------------------------------
-Mon Feb 25 12:59:00 CET 2008 - coolo@suse.de
-
-- fixing rpmdb2solv argument handling
-
--------------------------------------------------------------------
-Fri Feb 22 11:09:05 CET 2008 - coolo@suse.de
-
-- support rpmdb2solv -r for chroots
-
--------------------------------------------------------------------
-Thu Feb 21 18:14:41 CET 2008 - coolo@suse.de
-
-- fix susetags parser for so called full trees
-
--------------------------------------------------------------------
-Wed Feb 20 13:23:31 CET 2008 - coolo@suse.de
-
-- no longer link against db43 but against rpmlib
-
--------------------------------------------------------------------
-Tue Feb 19 15:01:55 CET 2008 - coolo@suse.de
-
-- fix requires/obsoletes
-
--------------------------------------------------------------------
-Tue Feb 19 08:10:01 CET 2008 - coolo@suse.de
-
-- mls is back from vacation - several fixes and enhancements
-
--------------------------------------------------------------------
-Fri Feb 15 11:04:09 CET 2008 - coolo@suse.de
-
-- several fixes for the solv files
-
--------------------------------------------------------------------
-Tue Feb 12 18:29:43 CET 2008 - kkaempf@suse.de
-
-- add libappsatsolver, an application layer to ease coding
- against sat-solver.
-
--------------------------------------------------------------------
-Tue Feb 12 17:14:04 CET 2008 - coolo@suse.de
-
-- let susetags parse vendors
-
--------------------------------------------------------------------
-Thu Feb 7 18:41:05 CET 2008 - coolo@suse.de
-
-- rename libsatsolver in satsolver-tools
-- several updates and fixes
-
--------------------------------------------------------------------
-Fri Feb 1 13:59:53 CET 2008 - coolo@suse.de
-
-- really don't strip
-
--------------------------------------------------------------------
-Mon Jan 14 17:14:03 CET 2008 - coolo@suse.de
-
-- update from SVN:
- - various fixes
- - less logging
-
--------------------------------------------------------------------
-Tue Jan 8 16:02:26 CET 2008 - coolo@suse.de
-
-- update to SVN again and don't strip
-
--------------------------------------------------------------------
-Sat Dec 22 19:02:57 CET 2007 - coolo@suse.de
-
-- update to SVN so libzypp compiles again
-
--------------------------------------------------------------------
-Fri Nov 30 16:01:39 CET 2007 - schubi@suse.de
-
-- update for libzypp integration
-
--------------------------------------------------------------------
-Fri Nov 16 12:17:13 CET 2007 - coolo@suse.de
-
-- update to SVN again to make libzypp compilable again
-
--------------------------------------------------------------------
-Wed Nov 14 16:10:21 CET 2007 - schubi@suse.de
-
-- further develpment. bugfix, logging, docu,...
-
--------------------------------------------------------------------
-Mon Nov 12 13:44:52 CET 2007 - coolo@suse.de
-
-- update to the latest version from SVN
- - compilation fixes
- - policy engine
-
--------------------------------------------------------------------
-Thu Nov 8 13:14:20 CET 2007 - coolo@suse.de
-
-- update to the latest version from SVN
-
--------------------------------------------------------------------
-Tue Oct 2 15:26:36 CEST 2007 - kkaempf@suse.de
-
-- Initial release
-
+++ /dev/null
-#
-# spec file for package libsolv
-#
-# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
-#
-# All modifications and additions to the file contributed by third parties
-# remain the property of their copyright owners, unless otherwise agreed
-# upon. The license for this file, and modifications and additions to the
-# file, is the same license as for the pristine package itself (unless the
-# license for the pristine package is not an Open Source License, in which
-# case the license is the MIT License). An "Open Source License" is a
-# license that conforms to the Open Source Definition (Version 1.9)
-# published by the Open Source Initiative.
-
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
-#
-
-Name: libsolv
-Version: @VERSION@
-Release: 0
-Url: https://github.com/openSUSE/libsolv
-Source: libsolv-%{version}.tar.bz2
-BuildRoot: %{_tmppath}/%{name}-%{version}-build
-
-%bcond_without enable_static
-%bcond_without disable_shared
-%bcond_without perl_binding
-%bcond_without python_binding
-%bcond_without ruby_binding
-%bcond_with zypp
-
-%if 0%{?mandriva_version}
-# force this version on mandriva
-BuildRequires: libneon0.26-devel
-%endif
-%if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
-BuildRequires: db-devel
-%endif
-%if 0%{?suse_version}
-%if 0%{?suse_version} < 1030
-BuildRequires: expat
-%else
-BuildRequires: libexpat-devel
-%endif
-%if 0%{?suse_version} < 1100
-BuildRequires: graphviz
-%endif
-%if 0%{?suse_version} > 1020
-BuildRequires: fdupes
-%endif
-%else
-BuildRequires: expat-devel
-%endif
-BuildRequires: cmake
-BuildRequires: gcc-c++
-BuildRequires: rpm-devel
-BuildRequires: zlib-devel
-
-%if %{with perl_binding}
-BuildRequires: perl
-%if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
-BuildRequires: perl-devel
-%endif
-BuildRequires: swig
-%endif
-%if %{with ruby_binding}
-%global ruby_vendorarch %(ruby -r rbconfig -e "puts RbConfig::CONFIG['vendorarchdir'].nil? ? RbConfig::CONFIG['sitearchdir'] : RbConfig::CONFIG['vendorarchdir']")
-BuildRequires: ruby
-BuildRequires: ruby-devel
-BuildRequires: swig
-%endif
-%if %{with python_binding}
-%global python_sitearch %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib(True);")
-BuildRequires: python-devel
-BuildRequires: swig
-%endif
-
-Summary: A new approach to package dependency solving
-License: BSD-3-Clause
-Group: Development/Libraries/C and C++
-
-%description
-A new approach to package dependency solving
-
-%if !%{with disable_shared}
-%package -n libsolv@LIBSOLV_SOVERSION@
-Summary: A new approach to package dependency solving
-Group: Development/Libraries/C and C++
-
-%description -n libsolv@LIBSOLV_SOVERSION@
-A new approach to package dependency solving
-
-%endif
-%package devel
-Summary: A new approach to package dependency solving
-Group: Development/Libraries/C and C++
-%if !%{with disable_shared}
-Requires: libsolv@LIBSOLV_SOVERSION@ = %version
-%endif
-Requires: rpm-devel
-Conflicts: libsatsolver-devel
-
-%description devel
-Development files for libsolv, a new approach to package dependency solving
-
-%package tools
-Summary: A new approach to package dependency solving
-Group: Development/Libraries/C and C++
-Obsoletes: satsolver-tools < 0.18
-Provides: satsolver-tools = 0.18
-Conflicts: satsolver-tools-obsolete
-Requires: gzip bzip2 coreutils findutils
-
-%description tools
-A new approach to package dependency solving.
-
-%package demo
-Summary: Applications demoing the libsolv library
-Group: System/Management
-Requires: curl
-%if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
-Requires: gnupg2
-%endif
-%if 0%{?suse_version}
-Requires: gpg2
-%endif
-Conflicts: libsatsolver-demo
-
-%description demo
-Applications demoing the libsolv library.
-
-%package -n ruby-solv
-Summary: Ruby bindings for the libsolv library
-Group: Development/Languages/Ruby
-
-%description -n ruby-solv
-Ruby bindings for sat solver.
-
-%package -n python-solv
-%if 0%{?py_requires:1} && %{with python_binding}
-%py_requires
-%endif
-Summary: Python bindings for the libsolv library
-Group: Development/Languages/Python
-
-%description -n python-solv
-Python bindings for sat solver.
-
-%package -n perl-solv
-Requires: perl = %{perl_version}
-Summary: Perl bindings for the libsolv library
-Group: Development/Languages/Perl
-
-%description -n perl-solv
-Perl bindings for sat solver.
-
-%prep
-%setup -n libsolv-%{version}
-
-%build
-export CFLAGS="$RPM_OPT_FLAGS"
-export CXXFLAGS="$CFLAGS"
-
-CMAKE_FLAGS=
-%if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
-CMAKE_FLAGS="-DFEDORA=1"
-%endif
-%if 0%{?suse_version}
-CMAKE_FLAGS="-DSUSE=1 -DENABLE_APPDATA=1 -DENABLE_COMPS=1"
-%endif
-
-cmake $CMAKE_FLAGS \
- -DCMAKE_INSTALL_PREFIX=%{_prefix} \
- -DLIB=%{_lib} \
- -DCMAKE_VERBOSE_MAKEFILE=TRUE \
- -DCMAKE_BUILD_TYPE=RelWithDebInfo \
- %{?with_enable_static:-DENABLE_STATIC=1} \
- %{?with_disable_shared:-DDISABLE_SHARED=1} \
- %{?with_perl_binding:-DENABLE_PERL=1} \
- %{?with_python_binding:-DENABLE_PYTHON=1} \
- %{?with_ruby_binding:-DENABLE_RUBY=1} \
- %{?with_zypp:-DENABLE_SUSEREPO=1 -DENABLE_HELIXREPO=1} \
- -DUSE_VENDORDIRS=1 \
- -DCMAKE_SKIP_RPATH=1
-make %{?jobs:-j %jobs}
-
-%install
-make DESTDIR=$RPM_BUILD_ROOT install
-%if %{with python_binding}
-%if 0%{?suse_version}
-pushd $RPM_BUILD_ROOT/%{python_sitearch}
-python %py_libdir/py_compile.py *.py
-python -O %py_libdir/py_compile.py *.py
-popd
-%endif
-%endif
-%if %{with disable_shared}
-# we want to leave the .a file untouched
-export NO_BRP_STRIP_DEBUG=true
-%endif
-
-%clean
-rm -rf "$RPM_BUILD_ROOT"
-
-%if !%{with disable_shared}
-%post -n libsolv@LIBSOLV_SOVERSION@ -p /sbin/ldconfig
-
-%postun -n libsolv@LIBSOLV_SOVERSION@ -p /sbin/ldconfig
-
-%files -n libsolv@LIBSOLV_SOVERSION@
-%defattr(-,root,root)
-%doc LICENSE*
-%{_libdir}/libsolv.so.*
-%{_libdir}/libsolvext.so.*
-%endif
-
-%files tools
-%defattr(-,root,root)
-%if 0%{?suse_version}
-%exclude %{_bindir}/helix2solv
-%exclude %{_mandir}/man1/helix2solv*
-%endif
-%exclude %{_bindir}/solv
-%{_bindir}/*
-%{_mandir}/man1/*
-
-%files devel
-%defattr(-,root,root)
-%if %{with enable_static}
-%{_libdir}/libsolv.a
-%{_libdir}/libsolvext.a
-%endif
-%if !%{with disable_shared}
-%{_libdir}/libsolv.so
-%{_libdir}/libsolvext.so
-%endif
-%{_includedir}/solv
-%if 0%{?suse_version}
-%{_bindir}/helix2solv
-%{_mandir}/man1/helix2solv*
-%endif
-%{_datadir}/cmake/Modules/*
-%{_libdir}/pkgconfig/libsolv.pc
-%{_mandir}/man3/*
-
-%files demo
-%defattr(-,root,root)
-%{_bindir}/solv
-
-%if %{with perl_binding}
-%files -n perl-solv
-%defattr(-,root,root)
-%{perl_vendorarch}/*
-%endif
-
-%if %{with ruby_binding}
-%files -n ruby-solv
-%defattr(-,root,root)
-%{ruby_vendorarch}/*
-%endif
-
-%if %{with python_binding}
-%files -n python-solv
-%defattr(-,root,root)
-%{python_sitearch}/*
-%endif
-
-%changelog
+++ /dev/null
-package BSSolv;
-
-use strict;
-
-require Exporter;
-
-our @ISA = qw(Exporter);
-
-our $VERSION = '0.08';
-
-require XSLoader;
-
-XSLoader::load('BSSolv', $VERSION);
-
-package BSSolv::repo;
-
-1;
+++ /dev/null
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include "EXTERN.h"
-#include "perl.h"
-#include "XSUB.h"
-
-#define MULTI_SEMANTICS
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "evr.h"
-#include "hash.h"
-#include "chksum.h"
-#include "repo_solv.h"
-#include "repo_write.h"
-#include "repo_rpmdb.h"
-#include "repo_deb.h"
-#if 1
-#include "repo_arch.h"
-#endif
-
-typedef struct _Expander {
- Pool *pool;
-
- Map ignored;
- Map ignoredx;
-
- Queue preferposq;
- Map preferpos;
- Map preferposx;
-
- Map preferneg;
- Map prefernegx;
-
- Queue conflictsq;
- Map conflicts;
-
- int debug;
- int havefileprovides;
- int ignoreconflicts;
- int ignoreignore;
-
- char *debugstr;
- int debugstrl;
- int debugstrf;
-} Expander;
-
-typedef Pool *BSSolv__pool;
-typedef Repo *BSSolv__repo;
-typedef Expander *BSSolv__expander;
-
-static Id buildservice_id;
-static Id buildservice_repocookie;
-static Id buildservice_external;
-static Id buildservice_dodurl;
-static Id expander_directdepsend;
-static Id buildservice_dodcookie;
-
-/* make sure bit n is usable */
-#define MAPEXP(m, n) ((m)->size < (((n) + 8) >> 3) ? map_grow(m, n + 256) : 0)
-
-#define REPOCOOKIE "buildservice repo 1.1"
-
-static int
-myrepowritefilter(Repo *repo, Repokey *key, void *kfdata)
-{
- int i;
- if (key->name == SOLVABLE_URL)
- return KEY_STORAGE_DROPPED;
- if (key->name == SOLVABLE_HEADEREND)
- return KEY_STORAGE_DROPPED;
- if (key->name == SOLVABLE_PACKAGER)
- return KEY_STORAGE_DROPPED;
- if (key->name == SOLVABLE_GROUP)
- return KEY_STORAGE_DROPPED;
- if (key->name == SOLVABLE_LICENSE)
- return KEY_STORAGE_DROPPED;
- if (key->name == SOLVABLE_PKGID)
- return KEY_STORAGE_INCORE;
- if (key->name == SOLVABLE_CHECKSUM)
- return KEY_STORAGE_INCORE;
- i = repo_write_stdkeyfilter(repo, key, kfdata);
- if (i == KEY_STORAGE_VERTICAL_OFFSET)
- return KEY_STORAGE_DROPPED;
- return i;
-}
-
-static inline char *
-hvlookupstr(HV *hv, const char *key, int keyl)
-{
- SV **svp = hv_fetch(hv, key, keyl, 0);
- if (!svp)
- return 0;
- return SvPV_nolen(*svp);
-}
-
-static inline AV *
-hvlookupav(HV *hv, const char *key, int keyl)
-{
- SV *sv, **svp = hv_fetch(hv, key, keyl, 0);
- if (!svp)
- return 0;
- sv = *svp;
- if (!sv || !SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV)
- return 0;
- return (AV *)SvRV(sv);
-}
-
-static Id
-makeevr(Pool *pool, char *e, char *v, char *r)
-{
- char *s;
-
- if (!v)
- return 0;
- if (e && !strcmp(e, "0"))
- e = 0;
- if (e)
- s = pool_tmpjoin(pool, e, ":", v);
- else
- s = v;
- if (r)
- s = pool_tmpjoin(pool, s, "-", r);
- return pool_str2id(pool, s, 1);
-}
-
-static inline char *
-avlookupstr(AV *av, int n)
-{
- SV **svp = av_fetch(av, n, 0);
- if (!svp)
- return 0;
- return SvPV_nolen(*svp);
-}
-
-static inline Id
-id2name(Pool *pool, Id id)
-{
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- id = rd->name;
- }
- return id;
-}
-
-static Id
-dep2id(Pool *pool, char *s)
-{
- char *n;
- Id id;
- int flags;
-
- if ((n = strchr(s, '|')) != 0)
- {
- id = dep2id(pool, n + 1);
- *n = 0;
- id = pool_rel2id(pool, dep2id(pool, s), id, REL_OR, 1);
- *n = '|';
- return id;
- }
- while (*s == ' ' || *s == '\t')
- s++;
- n = s;
- while (*s && *s != ' ' && *s != '\t' && *s != '<' && *s != '=' && *s != '>')
- s++;
-#ifdef REL_MULTIARCH
- if (s - n > 4 && s[-4] == ':' && !strncmp(s - 4, ":any", 4))
- {
- id = pool_strn2id(pool, n, s - n - 4, 1);
- id = pool_rel2id(pool, id, ARCH_ANY, REL_MULTIARCH, 1);
- }
- else
-#endif
- id = pool_strn2id(pool, n, s - n, 1);
- if (!*s)
- return id;
- while (*s == ' ' || *s == '\t')
- s++;
- flags = 0;
- for (;;s++)
- {
- if (*s == '<')
- flags |= REL_LT;
- else if (*s == '=')
- flags |= REL_EQ;
- else if (*s == '>')
- flags |= REL_GT;
- else
- break;
- }
- if (!flags)
- return id;
- while (*s == ' ' || *s == '\t')
- s++;
- n = s;
- while (*s && *s != ' ' && *s != '\t')
- s++;
- return pool_rel2id(pool, id, pool_strn2id(pool, n, s - n, 1), flags, 1);
-}
-
-static inline Offset
-importdeps(HV *hv, const char *key, int keyl, Repo *repo)
-{
- Pool *pool = repo->pool;
- int i;
- AV *av = hvlookupav(hv, key, keyl);
- Offset off = 0;
- if (av)
- {
- for (i = 0; i <= av_len(av); i++)
- {
- char *str = avlookupstr(av, i);
- if (str)
- off = repo_addid_dep(repo, off, dep2id(pool, str), 0);
- }
- }
- return off;
-}
-
-void
-exportdeps(HV *hv, const char *key, int keyl, Repo *repo, Offset off, Id skey)
-{
- Pool *pool = repo->pool;
- AV *av;
- Id id, *pp;
- const char *str;
-
- if (!off || !repo->idarraydata[off])
- return;
- pp = repo->idarraydata + off;
- av = 0;
- while ((id = *pp++))
- {
- if (id == SOLVABLE_FILEMARKER)
- break;
- str = pool_dep2str(pool, id);
- if (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- if (skey == SOLVABLE_CONFLICTS && rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
- {
- if (!strncmp(str, "namespace:", 10))
- str += 10;
- }
- if (skey == SOLVABLE_SUPPLEMENTS)
- {
- if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_FILESYSTEM)
- {
- if (!strncmp(str, "namespace:", 10))
- str += 10;
- }
- else if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_MODALIAS)
- {
- if (!strncmp(str, "namespace:", 10))
- str += 10;
- }
- else if (rd->flags == REL_AND)
- {
- /* either packageand chain or modalias */
- str = 0;
- if (ISRELDEP(rd->evr))
- {
- Reldep *mrd = GETRELDEP(pool, rd->evr);
- if (mrd->flags == REL_NAMESPACE && mrd->name == NAMESPACE_MODALIAS)
- {
- str = pool_tmpjoin(pool, "modalias(", pool_dep2str(pool, rd->name), ":");
- str = pool_tmpappend(pool, str, pool_dep2str(pool, mrd->evr), ")");
- }
- else if (mrd->flags >= 8)
- continue;
- }
- if (!str)
- {
- /* must be and chain */
- str = pool_dep2str(pool, rd->evr);
- for (;;)
- {
- id = rd->name;
- if (!ISRELDEP(id))
- break;
- rd = GETRELDEP(pool, id);
- if (rd->flags != REL_AND)
- break;
- str = pool_tmpjoin(pool, pool_dep2str(pool, rd->evr), ":", str);
- }
- str = pool_tmpjoin(pool, pool_dep2str(pool, id), ":", str);
- str = pool_tmpjoin(pool, "packageand(", str, ")");
- }
- }
- else if (rd->flags >= 8)
- continue;
- }
- }
- if (skey == SOLVABLE_REQUIRES)
- {
- if (id == SOLVABLE_PREREQMARKER)
- continue;
- if (*str == 'r' && !strncmp(str, "rpmlib(", 7))
- continue;
- }
- if (!av)
- av = newAV();
- av_push(av, newSVpv(str, 0));
- }
- if (av)
- (void)hv_store(hv, key, keyl, newRV_noinc((SV*)av), 0);
-}
-
-void
-data2solvables(Repo *repo, Repodata *data, HV *rhv)
-{
- Pool *pool = repo->pool;
- SV *sv;
- HV *hv;
- char *str, *key;
- I32 keyl;
- Id p;
- Solvable *s;
-
- hv_iterinit(rhv);
- while ((sv = hv_iternextsv(rhv, &key, &keyl)) != 0)
- {
- if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV)
- continue;
- hv = (HV *)SvRV(sv);
- str = hvlookupstr(hv, "name", 4);
- if (!str)
- continue; /* need to have a name */
- p = repo_add_solvable(repo);
- s = pool_id2solvable(pool, p);
- s->name = pool_str2id(pool, str, 1);
- str = hvlookupstr(hv, "arch", 4);
- if (!str)
- str = ""; /* dummy, need to have arch */
- s->arch = pool_str2id(pool, str, 1);
- s->evr = makeevr(pool, hvlookupstr(hv, "epoch", 5), hvlookupstr(hv, "version", 7), hvlookupstr(hv, "release", 7));
- str = hvlookupstr(hv, "path", 4);
- if (str)
- {
- char *ss = strrchr(str, '/');
- if (ss)
- {
- *ss = 0;
- repodata_set_str(data, p, SOLVABLE_MEDIADIR, str);
- *ss++ = '/';
- }
- else
- ss = str;
- repodata_set_str(data, p, SOLVABLE_MEDIAFILE, ss);
- }
- str = hvlookupstr(hv, "id", 2);
- if (str)
- repodata_set_str(data, p, buildservice_id, str);
- str = hvlookupstr(hv, "source", 6);
- if (str)
- repodata_set_poolstr(data, p, SOLVABLE_SOURCENAME, str);
- str = hvlookupstr(hv, "hdrmd5", 6);
- if (str && strlen(str) == 32)
- repodata_set_checksum(data, p, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, str);
- s->provides = importdeps(hv, "provides", 8, repo);
- s->obsoletes = importdeps(hv, "obsoletes", 9, repo);
- s->conflicts = importdeps(hv, "conflicts", 9, repo);
- s->requires = importdeps(hv, "requires", 8, repo);
- s->recommends = importdeps(hv, "recommends", 10, repo);
- s->suggests = importdeps(hv, "suggests", 8, repo);
- s->supplements = importdeps(hv, "supplements", 11, repo);
- s->enhances = importdeps(hv, "enhances", 8, repo);
- if (!s->evr && s->provides)
- {
- /* look for self provides */
- Id pro, *prop = s->repo->idarraydata + s->provides;
- while ((pro = *prop++) != 0)
- {
- Reldep *rd;
- if (!ISRELDEP(pro))
- continue;
- rd = GETRELDEP(pool, pro);
- if (rd->name == s->name && rd->flags == REL_EQ)
- s->evr = rd->evr;
- }
- }
- if (s->evr)
- s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
- str = hvlookupstr(hv, "checksum", 8);
- if (str)
- {
- char *cp, typebuf[8];
- Id ctype;
- if (*str != ':' && (cp = strchr(str, ':')) != 0 && cp - str < sizeof(typebuf))
- {
- strncpy(typebuf, str, cp - str);
- typebuf[cp - str] = 0;
- ctype = solv_chksum_str2type(typebuf);
- if (ctype)
- repodata_set_checksum(data, p, SOLVABLE_CHECKSUM, ctype, cp + 1);
- }
- }
- }
-
- repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
- str = hvlookupstr(rhv, "/url", 4);
- if (str)
- repodata_set_str(data, SOLVID_META, buildservice_dodurl, str);
- str = hvlookupstr(rhv, "/dodcookie", 10);
- if (str)
- repodata_set_str(data, SOLVID_META, buildservice_dodcookie, str);
-}
-
-static SV *
-retrieve(unsigned char **srcp, STRLEN *srclp, int depth)
-{
- SV *sv, *rv;
- AV *av;
- HV *hv;
- unsigned char *src = *srcp;
- STRLEN srcl = *srclp;
- int type;
- unsigned int i, len;
- STRLEN size;
-
- if (depth > 10)
- return 0;
- if (srcl-- == 0)
- return 0;
- type = *src++;
- switch (type)
- {
- case 1:
- if (srcl < 4)
- return 0;
- size = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
- srcl -= 4;
- src += 4;
- if (srcl < size)
- return 0;
- sv = NEWSV(10002, size);
- sv_setpvn(sv, (char *)src, size);
- srcl -= size;
- src += size;
- break;
- case 2:
- if (srcl < 4)
- return 0;
- len = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
- srcl -= 4;
- src += 4;
- if (len > srcl)
- return 0;
- av = newAV();
- if (len)
- av_extend(av, len);
- for (i = 0; i < len; i++)
- {
- sv = retrieve(&src, &srcl, depth + 1);
- if (!sv)
- return 0;
- if (av_store(av, i, sv) == 0)
- return 0;
- }
- sv = (SV *)av;
- break;
- case 3:
- if (srcl < 4)
- return 0;
- len = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
- srcl -= 4;
- src += 4;
- if (len > srcl)
- return 0;
- hv = newHV();
- if (len)
- hv_ksplit(hv, len + 1);
- for (i = 0; i < len; i++)
- {
- sv = retrieve(&src, &srcl, depth + 1);
- if (!sv)
- return 0;
- if (srcl < 4)
- return 0;
- size = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
- srcl -= 4;
- src += 4;
- if (srcl < size)
- return 0;
- if (hv_store(hv, (char *)src, (U32)size, sv, 0) == 0)
- return 0;
- srcl -= size;
- src += size;
- }
- sv = (SV *)hv;
- break;
- case 4:
- rv = NEWSV(10002, 0);
- sv = retrieve(&src, &srcl, depth + 1);
- if (!sv)
- return 0;
- sv_upgrade(rv, SVt_RV);
- SvRV_set(rv, sv);
- SvROK_on(rv);
- sv = rv;
- break;
- case 10:
- if (srcl-- == 0)
- return 0;
- size = *src++;
- if (srcl < size)
- return 0;
- sv = NEWSV(10002, size);
- sv_setpvn(sv, (char *)src, size);
- srcl -= size;
- src += size;
- break;
- default:
- /* fprintf(stderr, "unknown tag %d\n", type); */
- return 0;
- }
- *srcp = src;
- *srclp = srcl;
- return sv;
-}
-
-static void
-expander_dbg(Expander *xp, const char *format, ...)
-{
- va_list args;
- char buf[1024];
- int l;
- if (!xp->debug)
- return;
- va_start(args, format);
- vsnprintf(buf, sizeof(buf), format, args);
- va_end(args);
- printf("%s", buf);
- l = strlen(buf);
- if (buf[0] != ' ' || (l && buf[l - 1] == '\n'))
- fflush(stdout);
- if (l >= xp->debugstrf) /* >= because of trailing \0 */
- {
- xp->debugstr = solv_realloc(xp->debugstr, xp->debugstrl + l + 1024);
- xp->debugstrf = l + 1024;
- }
- strcpy(xp->debugstr + xp->debugstrl, buf);
- xp->debugstrl += l;
- xp->debugstrf -= l;
-}
-
-static const char *
-expander_solvid2name(Expander *xp, Id p)
-{
- const char *n = pool_id2str(xp->pool, xp->pool->solvables[p].name);
- Repo *r;
- if (!xp->debug)
- return n;
- r = xp->pool->solvables[p].repo;
- if (!r)
- return n;
- return pool_tmpjoin(xp->pool, n, "@", r->name);
-}
-
-static inline void
-expander_installed(Expander *xp, Id p, Map *installed, Map *conflicts, Queue *conflictsinfo, int *cidone, Queue *out, Queue *todo)
-{
- Pool *pool = xp->pool;
- Solvable *s = pool->solvables + p;
- Id req, id, *reqp, con, *conp;
- const char *n;
-
- MAPSET(installed, p);
- queue_push(out, p);
- if (MAPTST(&xp->conflicts, s->name))
- {
- int i;
- for (i = 0; i < xp->conflictsq.count; i++)
- {
- Id p2, pp2;
- Id id = xp->conflictsq.elements[i];
- if (id != s->name)
- continue;
- id = xp->conflictsq.elements[i ^ 1];
- FOR_PROVIDES(p2, pp2, id)
- {
- if (pool->solvables[p2].name == id)
- {
- MAPEXP(conflicts, pool->nsolvables);
- MAPSET(conflicts, p2);
- }
- }
- }
- }
- if (s->requires)
- {
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- continue;
- id = id2name(pool, req);
- if (!xp->ignoreignore)
- {
- if (MAPTST(&xp->ignored, id))
- continue;
- if (MAPTST(&xp->ignoredx, id))
- {
- Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, s->name), ":", pool_id2str(pool, id)), 0);
- if (xid && MAPTST(&xp->ignored, xid))
- continue;
- }
- }
- n = pool_id2str(pool, id);
- if (!strncmp(n, "rpmlib(", 7))
- {
- MAPEXP(&xp->ignored, id);
- MAPSET(&xp->ignored, id);
- continue;
- }
- if (*n == '/')
- {
- if (!xp->havefileprovides || pool->whatprovides[id] <= 1)
- {
- MAPEXP(&xp->ignored, id);
- MAPSET(&xp->ignored, id);
- continue;
- }
- }
- queue_push2(todo, req, p);
- }
- }
- if (!xp->ignoreconflicts)
- {
- if (s->conflicts)
- {
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- Id p2, pp2;
- FOR_PROVIDES(p2, pp2, con)
- {
- if (p2 == p)
- continue;
- MAPEXP(conflicts, pool->nsolvables);
- MAPSET(conflicts, p2);
- if (xp->debug)
- queue_push2(conflictsinfo, p2, p);
- }
- }
- }
- if (s->obsoletes)
- {
- conp = s->repo->idarraydata + s->obsoletes;
- while ((con = *conp++) != 0)
- {
- Id p2, pp2;
- FOR_PROVIDES(p2, pp2, con)
- {
- if (p2 == p || !pool_match_nevr(pool, pool->solvables + p2, con))
- continue;
- MAPEXP(conflicts, pool->nsolvables);
- MAPSET(conflicts, p2);
- if (xp->debug)
- queue_push2(conflictsinfo, p2, -p);
- }
- }
- }
- if (xp->debug)
- *cidone = out->count;
- }
-}
-
-static inline int
-expander_checkconflicts(Expander *xp, Id p, Map *installed, Id *conflicts, int isobsoletes)
-{
- Pool *pool = xp->pool;
- Id con, p2, pp2;
-
- if (xp->ignoreconflicts)
- return 0;
- while ((con = *conflicts++) != 0)
- {
- FOR_PROVIDES(p2, pp2, con)
- {
- if (p == p2)
- continue;
- if (isobsoletes && !pool_match_nevr(pool, pool->solvables + p2, con))
- continue;
- if (MAPTST(installed, p2))
- return p2;
- }
- }
- return 0;
-}
-
-static void
-expander_updateconflictsinfo(Expander *xp, Queue *conflictsinfo, int *cidone, Queue *out)
-{
- Pool *pool = xp->pool;
- int i;
- if (xp->ignoreconflicts)
- return;
- for (i = *cidone; i < out->count; i++)
- {
- Id p, p2, pp2;
- Id con, *conp;
- Solvable *s;
- p = out->elements[i];
- s = pool->solvables + p;
- /* keep in sync with expander_installed! */
- if (s->conflicts)
- {
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- FOR_PROVIDES(p2, pp2, con)
- {
- if (p2 == p)
- continue;
- queue_push2(conflictsinfo, p2, p);
- }
- }
- }
- if (s->obsoletes)
- {
- conp = s->repo->idarraydata + s->obsoletes;
- while ((con = *conp++) != 0)
- {
- FOR_PROVIDES(p2, pp2, con)
- {
- if (p2 == p || !pool_match_nevr(pool, pool->solvables + p2, con))
- continue;
- queue_push2(conflictsinfo, p2, -p);
- }
- }
- }
- }
- *cidone = out->count;
-}
-
-static inline int
-findconflictsinfo(Queue *conflictsinfo, Id p)
-{
- int i;
-
- for (i = 0; i < conflictsinfo->count; i++)
- if (conflictsinfo->elements[i] == p)
- return conflictsinfo->elements[i + 1];
- return 0;
-}
-
-#define ERROR_NOPROVIDER 1
-#define ERROR_CHOICE 2
-#define ERROR_CONFLICTINGPROVIDER 3
-#define ERROR_CONFLICTINGPROVIDERS 4
-#define ERROR_PROVIDERINFO 5
-#define ERROR_PROVIDERINFO2 6
-
-
-int
-expander_expand(Expander *xp, Queue *in, Queue *out, Queue *inconfl)
-{
- Pool *pool = xp->pool;
- Queue todo, errors, cerrors, qq, posfoundq;
- Map installed;
- Map conflicts;
- Queue conflictsinfo;
- int cidone;
- Solvable *s;
- Id q, p, pp;
- int i, j, nerrors, doamb, ambcnt;
- Id id, who, whon, pn;
- Id conflprov, conflprovpc;
-
- map_init(&installed, pool->nsolvables);
- map_init(&conflicts, 0);
- queue_init(&conflictsinfo);
- queue_init(&todo);
- queue_init(&qq);
- queue_init(&errors);
- queue_init(&cerrors);
- queue_init(&posfoundq);
-
- queue_empty(out);
- cidone = 0;
-
- if (inconfl)
- {
- for (i = 0; i < inconfl->count; i += 2)
- {
- Id con = inconfl->elements[i];
- FOR_PROVIDES(p, pp, con)
- {
- if (inconfl->elements[i + 1] && !pool_match_nevr(pool, pool->solvables + p, con))
- continue;
- MAPEXP(&conflicts, pool->nsolvables);
- MAPSET(&conflicts, p);
- }
- }
- }
- /* do direct expands */
- for (i = 0; i < in->count; i++)
- {
- id = in->elements[i];
- if (id == expander_directdepsend)
- {
- for (i = i + 1; i < in->count; i++)
- if (in->elements[i] != expander_directdepsend)
- queue_push2(&todo, in->elements[i], 0);
- break;
- }
- q = 0;
- FOR_PROVIDES(p, pp, id)
- {
- s = pool->solvables + p;
- if (!pool_match_nevr(pool, s, id))
- continue;
- if (q)
- {
- q = 0;
- break;
- }
- q = p;
- }
- if (!q)
- {
- /* unclear, resolve later */
- queue_push2(&todo, id, 0);
- continue;
- }
- if (MAPTST(&installed, q))
- continue;
- if (conflicts.size && MAPTST(&conflicts, q))
- {
- queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
- queue_push2(&errors, id, 0);
- if (cidone < out->count)
- expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
- queue_push(&errors, ERROR_PROVIDERINFO2);
- queue_push2(&errors, q, findconflictsinfo(&conflictsinfo, q));
- continue;
- }
- if (pool->solvables[q].conflicts && (pp = expander_checkconflicts(xp, q, &installed, pool->solvables[q].repo->idarraydata + pool->solvables[q].conflicts, 0)) != 0)
- {
- queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
- queue_push2(&errors, id, 0);
- queue_push(&errors, ERROR_PROVIDERINFO);
- queue_push2(&errors, q, pp);
- continue;
- }
- if (pool->solvables[q].obsoletes && (pp = expander_checkconflicts(xp, q, &installed, pool->solvables[q].repo->idarraydata + pool->solvables[q].obsoletes, 1)) != 0)
- {
- queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
- queue_push2(&errors, id, 0);
- queue_push(&errors, ERROR_PROVIDERINFO);
- queue_push2(&errors, q, -pp);
- continue;
- }
- if (xp->debug)
- expander_dbg(xp, "added %s because of %s (direct dep)\n", expander_solvid2name(xp, q), pool_dep2str(pool, id));
- expander_installed(xp, q, &installed, &conflicts, &conflictsinfo, &cidone, out, &todo); /* unique match! */
- }
-
- doamb = 0;
- ambcnt = todo.count;
- while (todo.count)
- {
- id = queue_shift(&todo);
- who = queue_shift(&todo);
- if (ambcnt == 0)
- {
- if (doamb)
- break; /* amb pass had no progress, stop */
- if (xp->debug)
- expander_dbg(xp, "now doing undecided dependencies\n");
- doamb = 1; /* start amb pass */
- ambcnt = todo.count;
- }
- else
- ambcnt -= 2;
-// printf("todo %s %s ambcnt %d\n", pool_id2str(pool, pool->solvables[who].name), pool_dep2str(pool, id), ambcnt);
-// fflush(stdout);
- whon = who ? pool->solvables[who].name : 0;
- queue_empty(&qq);
- conflprov = 0;
- conflprovpc = 0;
- FOR_PROVIDES(p, pp, id)
- {
- Id pc;
- if (MAPTST(&installed, p))
- break;
- if (who && !xp->ignoreignore)
- {
- Id pn = pool->solvables[p].name;
- if (MAPTST(&xp->ignored, pn))
- break;
- if (MAPTST(&xp->ignoredx, pn))
- {
- Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
- if (xid && MAPTST(&xp->ignored, xid))
- break;
- }
- }
- if (conflicts.size && MAPTST(&conflicts, p))
- {
- if (xp->debug)
- {
- Id pc = findconflictsinfo(&conflictsinfo, p);
- if (pc)
- expander_dbg(xp, "ignoring provider %s of %s because installed %s %s it\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc > 0 ? pc : -pc), pc > 0 ? "conflicts with" : "obsoletes");
- else
- expander_dbg(xp, "ignoring conflicted provider %s of %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id));
- }
- conflprov = conflprov ? 1 : p;
- conflprovpc = 0;
- continue;
- }
- if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0)) != 0)
- {
- expander_dbg(xp, "ignoring provider %s of %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc));
- conflprov = conflprov ? 1 : p;
- conflprovpc = pc;
- continue;
- }
- if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1)) != 0)
- {
- expander_dbg(xp, "ignoring provider %s of %s because it obsoletes installed %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc));
- conflprov = conflprov ? 1 : p;
- conflprovpc = -pc;
- continue;
- }
- queue_push(&qq, p);
- }
- if (p)
- continue;
- if (qq.count == 0)
- {
- if (!conflprov)
- {
- queue_push(&errors, ERROR_NOPROVIDER);
- queue_push2(&errors, id, who);
- continue;
- }
- /* more work for conflicts */
- if (conflprov != 1)
- {
- /* nice, just one provider */
- queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
- queue_push2(&errors, id, who);
- if (!conflprovpc)
- {
- if (cidone < out->count)
- expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
- conflprovpc = findconflictsinfo(&conflictsinfo, conflprov);
- queue_push(&errors, ERROR_PROVIDERINFO2);
- queue_push2(&errors, conflprov, conflprovpc);
- }
- else
- {
- queue_push(&errors, ERROR_PROVIDERINFO);
- queue_push2(&errors, conflprov, conflprovpc);
- }
- continue;
- }
- /* even more work if all providers conflict */
- queue_push(&errors, ERROR_CONFLICTINGPROVIDERS);
- queue_push2(&errors, id, who);
- if (cidone < out->count)
- expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
- FOR_PROVIDES(p, pp, id)
- {
- Id pc;
- if (conflicts.size && MAPTST(&conflicts, p))
- {
- pc = findconflictsinfo(&conflictsinfo, p);
- queue_push(&errors, ERROR_PROVIDERINFO2);
- queue_push2(&errors, p, pc);
- continue;
- }
- if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0)) != 0)
- {
- queue_push(&errors, ERROR_PROVIDERINFO);
- queue_push2(&errors, p, pc);
- continue;
- }
- if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1)) != 0)
- {
- queue_push(&errors, ERROR_PROVIDERINFO);
- queue_push2(&errors, p, -pc);
- continue;
- }
- }
- continue;
- }
- if (qq.count > 1 && !doamb)
- {
- /* try again later */
- queue_push2(&todo, id, who);
- if (xp->debug)
- {
- expander_dbg(xp, "undecided about %s:%s:", whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
- for (i = 0; i < qq.count; i++)
- expander_dbg(xp, " %s", expander_solvid2name(xp, qq.elements[i]));
- expander_dbg(xp, "\n");
- }
- continue;
- }
-
- /* prune neg prefers */
- if (qq.count > 1)
- {
- for (i = j = 0; i < qq.count; i++)
- {
- p = qq.elements[i];
- pn = pool->solvables[p].name;
- if (MAPTST(&xp->preferneg, pn))
- continue;
- if (who && MAPTST(&xp->prefernegx, pn))
- {
- Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
- if (xid && MAPTST(&xp->preferneg, xid))
- continue;
- }
- qq.elements[j++] = p;
- }
- if (j)
- queue_truncate(&qq, j);
- }
-
- /* prune pos prefers */
- if (qq.count > 1)
- {
- queue_empty(&posfoundq);
- for (i = j = 0; i < qq.count; i++)
- {
- p = qq.elements[i];
- pn = pool->solvables[p].name;
- if (MAPTST(&xp->preferpos, pn))
- {
- queue_push2(&posfoundq, pn, p);
- qq.elements[j++] = p;
- continue;
- }
- if (who && MAPTST(&xp->preferposx, pn))
- {
- Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
- if (xid && MAPTST(&xp->preferpos, xid))
- {
- queue_push2(&posfoundq, xid, p);
- qq.elements[j++] = p;
- continue;
- }
- }
- }
- if (posfoundq.count == 2)
- {
- queue_empty(&qq);
- queue_push(&qq, posfoundq.elements[1]);
- }
- else if (posfoundq.count)
- {
- /* found a pos prefer, now find first hit */
- /* (prefers are ordered) */
- for (i = 0; i < xp->preferposq.count; i++)
- {
- Id xid = xp->preferposq.elements[i];
- for (j = 0; j < posfoundq.count; j += 2)
- if (posfoundq.elements[j] == xid)
- break;
- if (j < posfoundq.count)
- {
- queue_empty(&qq);
- queue_push(&qq, posfoundq.elements[j + 1]);
- break;
- }
- }
- }
- }
-
- /* prune OR deps */
- if (qq.count > 1 && ISRELDEP(id) && GETRELDEP(pool, id)->flags == REL_OR)
- {
- Id rid = id;
- for (;;)
- {
- Reldep *rd = 0;
- if (ISRELDEP(rid))
- {
- rd = GETRELDEP(pool, rid);
- if (rd->flags != REL_OR)
- rd = 0;
- }
- if (rd)
- rid = rd->name;
- queue_empty(&qq);
- FOR_PROVIDES(p, pp, rid)
- queue_push(&qq, p);
- if (qq.count)
- break;
- if (rd)
- rid = rd->evr;
- else
- break;
- }
- }
- if (qq.count > 1)
- {
- queue_push(&cerrors, ERROR_CHOICE);
- queue_push2(&cerrors, id, who);
- for (i = 0; i < qq.count; i++)
- queue_push(&cerrors, qq.elements[i]);
- queue_push(&cerrors, 0);
- /* try again later */
- queue_push2(&todo, id, who);
- continue;
- }
- if (xp->debug)
- expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, qq.elements[0]), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
- expander_installed(xp, qq.elements[0], &installed, &conflicts, &conflictsinfo, &cidone, out, &todo);
- doamb = 0;
- ambcnt = todo.count;
- queue_empty(&cerrors);
- }
- map_free(&installed);
- map_free(&conflicts);
- queue_free(&conflictsinfo);
- nerrors = 0;
- if (errors.count || cerrors.count)
- {
- queue_empty(out);
- for (i = 0; i < errors.count; i += 3)
- {
- queue_push(out, errors.elements[i]);
- queue_push(out, errors.elements[i + 1]);
- queue_push(out, errors.elements[i + 2]);
- nerrors++;
- }
- for (i = 0; i < cerrors.count; )
- {
- queue_push(out, cerrors.elements[i]);
- queue_push(out, cerrors.elements[i + 1]);
- queue_push(out, cerrors.elements[i + 2]);
- i += 3;
- while (cerrors.elements[i])
- {
- queue_push(out, cerrors.elements[i]);
- i++;
- }
- queue_push(out, 0);
- i++;
- nerrors++;
- }
- }
- else
- {
- if (todo.count)
- {
- fprintf(stderr, "Internal expansion error!\n");
- queue_empty(out);
- queue_push(out, ERROR_NOPROVIDER);
- queue_push(out, 0);
- queue_push(out, 0);
- }
- }
- queue_free(&todo);
- queue_free(&qq);
- queue_free(&errors);
- queue_free(&cerrors);
- queue_free(&posfoundq);
- return nerrors;
-}
-
-static void
-set_disttype(Pool *pool, int disttype)
-{
- pool_setdisttype(pool, disttype);
-#ifdef POOL_FLAG_HAVEDISTEPOCH
- /* make newer mandriva work, hopefully there are no ill effects... */
- pool_set_flag(pool, POOL_FLAG_HAVEDISTEPOCH, disttype == DISTTYPE_RPM ? 1 : 0);
-#endif
-}
-
-static void
-set_disttype_from_location(Pool *pool, Solvable *so)
-{
- unsigned int medianr;
- const char *s = solvable_get_location(so, &medianr);
- int disttype = -1;
- int sl;
- if (!s)
- return;
- sl = strlen(s);
- if (disttype < 0 && sl >= 4 && !strcmp(s + sl - 4, ".rpm"))
- disttype = DISTTYPE_RPM;
-#ifdef DISTTYPE_DEB
- if (disttype < 0 && sl >= 4 && !strcmp(s + sl - 4, ".deb"))
- disttype = DISTTYPE_DEB;
-#endif
-#ifdef DISTTYPE_ARCH
- if (disttype < 0 && sl >= 11 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz")))
- disttype = DISTTYPE_ARCH;
-#endif
- if (disttype >= 0 && pool->disttype != disttype)
- set_disttype(pool, disttype);
-}
-
-void
-create_considered(Pool *pool, Repo *repoonly, Map *considered)
-{
- Id p, pb,*best;
- Solvable *s, *sb;
- int ridx;
- Repo *repo;
- int olddisttype = -1;
- int dodrepo;
-
- map_init(considered, pool->nsolvables);
- best = solv_calloc(sizeof(Id), pool->ss.nstrings);
-
- FOR_REPOS(ridx, repo)
- {
- if (repoonly && repo != repoonly)
- continue;
- dodrepo = repo_lookup_str(repo, SOLVID_META, buildservice_dodurl) != 0;
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
- continue;
- pb = best[s->name];
- if (pb)
- {
- sb = pool->solvables + pb;
- if (s->repo != sb->repo)
- continue; /* first repo wins */
- if (s->arch != sb->arch)
- {
- int r;
- if (s->arch == ARCH_NOARCH || s->arch == ARCH_ALL || s->arch == ARCH_ANY)
- continue;
- if (sb->arch != ARCH_NOARCH && sb->arch != ARCH_ALL && sb->arch != ARCH_ANY)
- {
- /* the strcmp is kind of silly, but works for most archs */
- r = strcmp(pool_id2str(pool, sb->arch), pool_id2str(pool, s->arch));
- if (r >= 0)
- continue;
- }
- }
- else if (s->evr != sb->evr)
- {
- /* same repo, check versions */
- int r;
- if (olddisttype < 0)
- {
- olddisttype = pool->disttype;
- set_disttype_from_location(pool, s);
- }
- r = pool_evrcmp(pool, sb->evr, s->evr, EVRCMP_COMPARE);
- if (r > 0)
- continue;
- else if (r == 0)
- {
- r = strcmp(pool_id2str(pool, sb->evr), pool_id2str(pool, s->evr));
- if (r >= 0)
- continue;
- }
- }
- }
- if (dodrepo)
- {
- /* we only consider dod packages */
- const char *bsid = solvable_lookup_str(s, buildservice_id);
- if (!bsid || strcmp(bsid, "dod") != 0)
- continue;
- }
- if (pb)
- MAPCLR(considered, pb);
- best[s->name] = p;
- MAPSET(considered, p);
- }
- /* dodrepos have a second pass: replace dod entries with downloaded ones */
- if (dodrepo)
- {
- const char *bsid;
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
- continue;
- pb = best[s->name];
- if (!pb || pb == p)
- continue;
- sb = pool->solvables + pb;
- if (sb->repo != s->repo || sb->name != s->name || sb->arch != s->arch || sb->evr != s->evr)
- continue;
- bsid = solvable_lookup_str(s, buildservice_id);
- if (bsid && strcmp(bsid, "dod") == 0)
- continue; /* not downloaded */
- MAPCLR(considered, pb);
- best[s->name] = p;
- MAPSET(considered, p);
- }
- }
- }
- solv_free(best);
- if (olddisttype >= 0 && pool->disttype != olddisttype)
- set_disttype(pool, olddisttype);
-}
-
-struct metaline {
- char *l;
- int lastoff;
- int nslash;
- int killed;
-};
-
-static int metacmp(const void *ap, const void *bp)
-{
- const struct metaline *a, *b;
- int r;
-
- a = ap;
- b = bp;
- r = a->nslash - b->nslash;
- if (r)
- return r;
- r = strcmp(a->l + 34, b->l + 34);
- if (r)
- return r;
- r = strcmp(a->l, b->l);
- if (r)
- return r;
- return a - b;
-}
-
-#ifndef REPO_NO_LOCATION
-# define REPO_NO_LOCATION 0
-#endif
-
-Id
-repodata_addbin(Repodata *data, char *prefix, char *s, int sl, char *sid)
-{
- Id p = 0;
- char *path;
-#if REPO_NO_LOCATION == 0
- char *sp;
-#endif
-
- path = solv_dupjoin(prefix, "/", s);
- if (sl >= 4 && !strcmp(s + sl - 4, ".rpm"))
- p = repo_add_rpm(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|RPM_ADD_WITH_PKGID|RPM_ADD_NO_FILELIST|RPM_ADD_NO_RPMLIBREQS);
- else if (sl >= 4 && !strcmp(s + sl - 4, ".deb"))
- p = repo_add_deb(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|DEBS_ADD_WITH_PKGID);
-#ifdef ARCH_ADD_WITH_PKGID
- else if (sl >= 11 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz")))
- p = repo_add_arch_pkg(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|ARCH_ADD_WITH_PKGID);
-#endif
- solv_free(path);
- if (!p)
- return 0;
-#if REPO_NO_LOCATION != 0
- repodata_set_location(data, p, 0, 0, s);
-#else
- if ((sp = strrchr(s, '/')) != 0)
- {
- *sp = 0;
- repodata_set_str(data, p, SOLVABLE_MEDIADIR, s);
- *sp = '/';
- }
- else
- repodata_delete_uninternalized(data, p, SOLVABLE_MEDIADIR);
-#endif
- repodata_set_str(data, p, buildservice_id, sid);
- return p;
-}
-
-static int
-subpack_sort_cmp(const void *ap, const void *bp, void *dp)
-{
- Pool *pool = (Pool *)dp;
- const Id *a = ap;
- const Id *b = bp;
- int r = a[1] - b[1];
- if (r)
- return r;
- r = strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
- return r ? r : a[0] - b[0];
-}
-
-/* This is an OpenSSL-compatible implementation of the RSA Data Security,
- * Inc. MD5 Message-Digest Algorithm.
- *
- * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
- * the public domain. */
-
-#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
-#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define I(x, y, z) ((y) ^ ((x) | ~(z)))
-
-#define STEP(f, a, b, c, d, x, t, s) \
- (a) += f((b), (c), (d)) + (x) + (t); \
- (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
- (a) += (b);
-
-#if defined(__i386__) || defined(__vax__)
-#define SET(n) \
- (*(MD5_u32plus *)&ptr[(n) * 4])
-#define GET(n) \
- SET(n)
-#else
-#define SET(n) \
- (ctx->block[(n)] = \
- (MD5_u32plus)ptr[(n) * 4] | \
- ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
- ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
- ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
-#define GET(n) \
- (ctx->block[(n)])
-#endif
-
-typedef unsigned long MD5_u32plus;
-
-typedef struct {
- MD5_u32plus lo, hi;
- MD5_u32plus a, b, c, d;
- unsigned char buffer[64];
- MD5_u32plus block[16];
-} MD5_CTX;
-
-/*
- * This processes one or more 64-byte data blocks, but does NOT update
- * the bit counters. There're no alignment requirements.
- */
-static void *md5_body(MD5_CTX *ctx, void *data, unsigned long size)
-{
- unsigned char *ptr;
- MD5_u32plus a, b, c, d;
- MD5_u32plus saved_a, saved_b, saved_c, saved_d;
-
- ptr = data;
-
- a = ctx->a;
- b = ctx->b;
- c = ctx->c;
- d = ctx->d;
-
- do {
- saved_a = a;
- saved_b = b;
- saved_c = c;
- saved_d = d;
-
-/* Round 1 */
- STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
- STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
- STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
- STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
- STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
- STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
- STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
- STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
- STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
- STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
- STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
- STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
- STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
- STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
- STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
- STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
-
-/* Round 2 */
- STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
- STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
- STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
- STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
- STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
- STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
- STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
- STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
- STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
- STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
- STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
- STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
- STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
- STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
- STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
- STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
-
-/* Round 3 */
- STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
- STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
- STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
- STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
- STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
- STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
- STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
- STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
- STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
- STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
- STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
- STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
- STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
- STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
- STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
- STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
-
-/* Round 4 */
- STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
- STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
- STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
- STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
- STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
- STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
- STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
- STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
- STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
- STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
- STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
- STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
- STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
- STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
- STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
- STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
-
- a += saved_a;
- b += saved_b;
- c += saved_c;
- d += saved_d;
-
- ptr += 64;
- } while (size -= 64);
-
- ctx->a = a;
- ctx->b = b;
- ctx->c = c;
- ctx->d = d;
-
- return ptr;
-}
-
-static void md5_init(MD5_CTX *ctx)
-{
- ctx->a = 0x67452301;
- ctx->b = 0xefcdab89;
- ctx->c = 0x98badcfe;
- ctx->d = 0x10325476;
- ctx->lo = 0;
- ctx->hi = 0;
-}
-
-static void md5_update(MD5_CTX *ctx, void *data, unsigned long size)
-{
- MD5_u32plus saved_lo;
- unsigned long used, free;
-
- saved_lo = ctx->lo;
- if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
- ctx->hi++;
- ctx->hi += size >> 29;
-
- used = saved_lo & 0x3f;
-
- if (used) {
- free = 64 - used;
-
- if (size < free) {
- memcpy(&ctx->buffer[used], data, size);
- return;
- }
-
- memcpy(&ctx->buffer[used], data, free);
- data = (unsigned char *)data + free;
- size -= free;
- md5_body(ctx, ctx->buffer, 64);
- }
-
- if (size >= 64) {
- data = md5_body(ctx, data, size & ~(unsigned long)0x3f);
- size &= 0x3f;
- }
-
- memcpy(ctx->buffer, data, size);
-}
-
-static void md5_final(MD5_CTX *ctx, unsigned char *result)
-{
- unsigned long used, free;
- used = ctx->lo & 0x3f;
- ctx->buffer[used++] = 0x80;
- free = 64 - used;
- if (free < 8) {
- memset(&ctx->buffer[used], 0, free);
- md5_body(ctx, ctx->buffer, 64);
- used = 0;
- free = 64;
- }
- memset(&ctx->buffer[used], 0, free - 8);
- ctx->lo <<= 3;
- ctx->buffer[56] = ctx->lo;
- ctx->buffer[57] = ctx->lo >> 8;
- ctx->buffer[58] = ctx->lo >> 16;
- ctx->buffer[59] = ctx->lo >> 24;
- ctx->buffer[60] = ctx->hi;
- ctx->buffer[61] = ctx->hi >> 8;
- ctx->buffer[62] = ctx->hi >> 16;
- ctx->buffer[63] = ctx->hi >> 24;
- md5_body(ctx, ctx->buffer, 64);
- result[0] = ctx->a;
- result[1] = ctx->a >> 8;
- result[2] = ctx->a >> 16;
- result[3] = ctx->a >> 24;
- result[4] = ctx->b;
- result[5] = ctx->b >> 8;
- result[6] = ctx->b >> 16;
- result[7] = ctx->b >> 24;
- result[8] = ctx->c;
- result[9] = ctx->c >> 8;
- result[10] = ctx->c >> 16;
- result[11] = ctx->c >> 24;
- result[12] = ctx->d;
- result[13] = ctx->d >> 8;
- result[14] = ctx->d >> 16;
- result[15] = ctx->d >> 24;
- memset(ctx, 0, sizeof(*ctx));
-}
-
-static unsigned int buz_noise[256] =
-{
- 0x9be502a4U, 0xba7180eaU, 0x324e474fU, 0x0aab8451U, 0x0ced3810U,
- 0x2158a968U, 0x6bbd3771U, 0x75a02529U, 0x41f05c14U, 0xc2264b87U,
- 0x1f67b359U, 0xcd2d031dU, 0x49dc0c04U, 0xa04ae45cU, 0x6ade28a7U,
- 0x2d0254ffU, 0xdec60c7cU, 0xdef5c084U, 0x0f77ffc8U, 0x112021f6U,
- 0x5f6d581eU, 0xe35ea3dfU, 0x3216bfb4U, 0xd5a3083dU, 0x7e63e9cdU,
- 0xaa9208f6U, 0xda3f3978U, 0xfe0e2547U, 0x09dfb020U, 0xd97472c5U,
- 0xbbce2edeU, 0x121aebd2U, 0x0e9fdbebU, 0x7b6f5d9cU, 0x84938e43U,
- 0x30694f2dU, 0x86b7a7f8U, 0xefaf5876U, 0x263812e6U, 0xb6e48ddfU,
- 0xce8ed980U, 0x4df591e1U, 0x75257b35U, 0x2f88dcffU, 0xa461fe44U,
- 0xca613b4dU, 0xd9803f73U, 0xea056205U, 0xccca7a89U, 0x0f2dbb07U,
- 0xc53e359eU, 0xe80d0137U, 0x2b2d2a5dU, 0xcfc1391aU, 0x2bb3b6c5U,
- 0xb66aea3cU, 0x00ea419eU, 0xce5ada84U, 0xae1d6712U, 0x12f576baU,
- 0x117fcbc4U, 0xa9d4c775U, 0x25b3d616U, 0xefda65a8U, 0xaff3ef5bU,
- 0x00627e68U, 0x668d1e99U, 0x088d0eefU, 0xf8fac24dU, 0xe77457c7U,
- 0x68d3beb4U, 0x921d2acbU, 0x9410eac9U, 0xd7f24399U, 0xcbdec497U,
- 0x98c99ae1U, 0x65802b2cU, 0x81e1c3c4U, 0xa130bb09U, 0x17a87badU,
- 0xa70367d6U, 0x148658d4U, 0x02f33377U, 0x8620d8b6U, 0xbdac25bdU,
- 0xb0a6de51U, 0xd64c4571U, 0xa4185ba0U, 0xa342d70fU, 0x3f1dc4c1U,
- 0x042dc3ceU, 0x0de89f43U, 0xa69b1867U, 0x3c064e11U, 0xad1e2c3eU,
- 0x9660e8cdU, 0xd36b09caU, 0x4888f228U, 0x61a9ac3cU, 0xd9561118U,
- 0x3532797eU, 0x71a35c22U, 0xecc1376cU, 0xab31e656U, 0x88bd0d35U,
- 0x423b20ddU, 0x38e4651cU, 0x3c6397a4U, 0x4a7b12d9U, 0x08b1cf33U,
- 0xd0604137U, 0xb035fdb8U, 0x4916da23U, 0xa9349493U, 0xd83daa9bU,
- 0x145f7d95U, 0x868531d6U, 0xacb18f17U, 0x9cd33b6fU, 0x193e42b9U,
- 0x26dfdc42U, 0x5069d8faU, 0x5bee24eeU, 0x5475d4c6U, 0x315b2c0cU,
- 0xf764ef45U, 0x01b6f4ebU, 0x60ba3225U, 0x8a16777cU, 0x4c05cd28U,
- 0x53e8c1d2U, 0xc8a76ce5U, 0x8045c1e6U, 0x61328752U, 0x2ebad322U,
- 0x3444f3e2U, 0x91b8af11U, 0xb0cee675U, 0x55dbff5aU, 0xf7061ee0U,
- 0x27d7d639U, 0xa4aef8c9U, 0x42ff0e4fU, 0x62755468U, 0x1c6ca3f3U,
- 0xe4f522d1U, 0x2765fcb3U, 0xe20c8a95U, 0x3a69aea7U, 0x56ab2c4fU,
- 0x8551e688U, 0xe0bc14c2U, 0x278676bfU, 0x893b6102U, 0xb4f0ab3bU,
- 0xb55ddda9U, 0xa04c521fU, 0xc980088eU, 0x912aeac1U, 0x08519badU,
- 0x991302d3U, 0x5b91a25bU, 0x696d9854U, 0x9ad8b4bfU, 0x41cb7e21U,
- 0xa65d1e03U, 0x85791d29U, 0x89478aa7U, 0x4581e337U, 0x59bae0b1U,
- 0xe0fc9df3U, 0x45d9002cU, 0x7837464fU, 0xda22de3aU, 0x1dc544bdU,
- 0x601d8badU, 0x668b0abcU, 0x7a5ebfb1U, 0x3ac0b624U, 0x5ee16d7dU,
- 0x9bfac387U, 0xbe8ef20cU, 0x8d2ae384U, 0x819dc7d5U, 0x7c4951e7U,
- 0xe60da716U, 0x0c5b0073U, 0xb43b3d97U, 0xce9974edU, 0x0f691da9U,
- 0x4b616d60U, 0x8fa9e819U, 0x3f390333U, 0x6f62fad6U, 0x5a32b67cU,
- 0x3be6f1c3U, 0x05851103U, 0xff28828dU, 0xaa43a56aU, 0x075d7dd5U,
- 0x248c4b7eU, 0x52fde3ebU, 0xf72e2edaU, 0x5da6f75fU, 0x2f5148d9U,
- 0xcae2aeaeU, 0xfda6f3e5U, 0xff60d8ffU, 0x2adc02d2U, 0x1dbdbd4cU,
- 0xd410ad7cU, 0x8c284aaeU, 0x392ef8e0U, 0x37d48b3aU, 0x6792fe9dU,
- 0xad32ddfaU, 0x1545f24eU, 0x3a260f73U, 0xb724ca36U, 0xc510d751U,
- 0x4f8df992U, 0x000b8b37U, 0x292e9b3dU, 0xa32f250fU, 0x8263d144U,
- 0xfcae0516U, 0x1eae2183U, 0xd4af2027U, 0xc64afae3U, 0xe7b34fe4U,
- 0xdf864aeaU, 0x80cc71c5U, 0x0e814df3U, 0x66cc5f41U, 0x853a497aU,
- 0xa2886213U, 0x5e34a2eaU, 0x0f53ba47U, 0x718c484aU, 0xfa0f0b12U,
- 0x33cc59ffU, 0x72b48e07U, 0x8b6f57bcU, 0x29cf886dU, 0x1950955bU,
- 0xcd52910cU, 0x4cecef65U, 0x05c2cbfeU, 0x49df4f6aU, 0x1f4c3f34U,
- 0xfadc1a09U, 0xf2d65a24U, 0x117f5594U, 0xde3a84e6U, 0x48db3024U,
- 0xd10ca9b5U
-};
-
-
-/*
- * our delta search blocksize
- *
- * smaller gives more hits, but increases the hash size
- *
- * must be a multiple of 256
- * must be in range [256,32767]
- */
-#define DELTA_BSIZE 1024
-
-/* min store block len, smaller blocks are encoded as direct data */
-#define MIN_BSIZE 32
-
-/* min meta block len, must be >= 10 */
-#define MIN_BSIZE_META 32
-
-/* max meta block len, must be <= DELTA_BSIZE */
-#define MAX_BSIZE_META DELTA_BSIZE
-
-/* number of slots per data area */
-#define SLOTS_PER_AREA 4095
-
-
-/* buzhash by Robert C. Uzgalis */
-/* General hash functions. Technical Report TR-92-01, The University
- of Hong Kong, 1993 */
-
-static unsigned int buzhash(unsigned char *buf)
-{
- unsigned int x = 0x83d31df4U;
- int i;
- for (i = DELTA_BSIZE ; i != 0; i--)
- x = (x << 1) ^ (x & (1 << 31) ? 1 : 0) ^ buz_noise[*buf++];
- return x;
-}
-
-static void md5block(unsigned char *buf, int len, unsigned char *out)
-{
- MD5_CTX ctx;
- md5_init(&ctx);
- md5_update(&ctx, buf, (unsigned long)len);
- md5_final(&ctx, out);
-}
-
-#define HASHCHAIN_START 7
-#define HASHCHAIN_NEXT(h, hh, mask) (((h) + (hh)++) & (mask))
-
-
-struct deltastore {
- int fd; /* file descriptor */
- int rdonly; /* store is read only */
-
- unsigned long long end; /* store file size */
-
- unsigned long long *offsets; /* the data areas we know about */
- int noffsets;
-
- unsigned char *hash; /* our hash */
- unsigned int hm; /* size of hash */
- unsigned int hf; /* hash fill */
- unsigned int hd; /* entries not in hash */
-
- int freecnt; /* free slots in last slot area */
- int usedcnt; /* used slots in last slot area */
- unsigned long long slotsoffset; /* offset of last slot area */
-};
-
-struct deltaout {
- FILE *fp;
- struct deltastore *store;
-
- /* for block coalescence */
- unsigned long long oldoffset;
- unsigned long long oldsize;
-
- /* for offset encoding */
- unsigned long long lastoffset;
-
- /* for meta block creation */
- int outbuf_do_meta; /* create meta blocks */
- unsigned char outbuf[MAX_BSIZE_META + 16]; /* 16 extra bytes for one encoded block */
- int outbuf_len;
- /* offset patching */
- unsigned long long outbuf_startoffset;
- int outbuf_startoffset_set;
- int outbuf_set_len1;
- int outbuf_set_len2;
- unsigned long long outbuf_set_offset; /* offset we need to patch in, already encoded */
-};
-
-static inline unsigned long long getu48(unsigned char *d)
-{
- unsigned long long x = d[0] << 8 | d[1];
- return (x << 32) | (d[2] << 24 | d[3] << 16 | d[4] << 8 | d[5]);
-}
-
-static inline void putu48(unsigned char *d, unsigned long long x)
-{
- d[0] = x >> 40;
- d[1] = x >> 32;
- d[2] = x >> 24;
- d[3] = x >> 16;
- d[4] = x >> 8;
- d[5] = x;
-}
-
-static inline unsigned int getu32(unsigned char *d)
-{
- return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
-}
-
-static inline void putu32(unsigned char *d, unsigned int x)
-{
- d[0] = x >> 24;
- d[1] = x >> 16;
- d[2] = x >> 8;
- d[3] = x;
-}
-
-/**
- ** store handling
- **/
-
-static int
-finddataarea(struct deltastore *store, unsigned long long offset)
-{
- int i;
- for (i = 0; i < store->noffsets; i += 2)
- if (offset >= store->offsets[i] && offset < store->offsets[i + 1])
- return i;
- return -1;
-}
-
-static void
-adddataarea(struct deltastore *store, unsigned long long start, unsigned long long end)
-{
- unsigned long long *newoffsets;
- if (store->noffsets && store->offsets[store->noffsets - 1] == start)
- {
- store->offsets[store->noffsets - 1] = end;
- return;
- }
- if (store->offsets)
- newoffsets = realloc(store->offsets, (store->noffsets + 2) * sizeof(unsigned long long));
- else
- newoffsets = malloc((store->noffsets + 2) * sizeof(unsigned long long));
- if (!newoffsets)
- return;
- newoffsets[store->noffsets++] = start;
- newoffsets[store->noffsets++] = end;
- store->offsets = newoffsets;
-}
-
-static int
-addslotarea(struct deltastore *store, int cnt)
-{
- unsigned char *slots;
- if (!cnt || cnt > 65535)
- return 0;
- if (store->rdonly)
- return 0;
- if ((store->end & 4095) != 0) /* pad to multiple of 4096 */
- {
- char pad[4096];
- int l = 4096 - (store->end & 4095);
- memset(pad, 0, l);
- if (pwrite(store->fd, pad, l, store->end) != l)
- {
- perror("pwrite pad next slotsarea");
- return 0;
- }
- store->end += l;
- }
- if (store->end + (cnt + 1) * 16 >= (1LL << 48))
- return 0; /* store too big! */
- slots = calloc(cnt + 1, 16);
- if (!slots)
- return 0;
- memcpy(slots, "OBSDELT", 8);
- slots[8] = cnt >> 8;
- slots[9] = cnt;
- /* write pointer to next slot area */
- if (store->end)
- {
- putu48(slots + 10, store->end);
- if (pwrite(store->fd, slots, 16, store->slotsoffset) != 16)
- {
- perror("pwrite update next slotsarea");
- free(slots);
- return 0;
- }
- memset(slots + 10, 0, 6);
- }
- if (pwrite(store->fd, slots, (cnt + 1) * 16, store->end) != (cnt + 1) * 16)
- {
- perror("pwrite new slotarea");
- free(slots);
- return 0;
- }
- free(slots);
- store->slotsoffset = store->end;
- store->end += (cnt + 1) * 16;
- store->freecnt = cnt;
- store->usedcnt = 0;
- return 1;
-}
-
-/*
- * add a new block to the store.
- * returns the store offset, 0 on error
- */
-static unsigned long long
-putinstore(struct deltastore *store, unsigned char *buf, int size, unsigned char *md5, unsigned int hx)
-{
- unsigned char md5buf[16];
- unsigned char hp[16];
- unsigned long long offset;
-
- unsigned char *hash;
- unsigned int h, hh, hm;
-
- if (!size || size > DELTA_BSIZE)
- return 0;
-
- if (store->rdonly)
- return 0;
- if (store->freecnt == 0 && !addslotarea(store, SLOTS_PER_AREA))
- return 0;
-
- /* write data */
- offset = store->end;
- if (offset + size >= (1LL << 48))
- return 0; /* store too big! */
- if (pwrite(store->fd, buf, size, store->end) != size)
- {
- perror("pwrite data");
- return 0;
- }
- adddataarea(store, store->end, store->end + size);
- store->end += size;
-
- /* write slot */
- if (!md5)
- {
- md5block(buf, size, md5buf);
- md5 = md5buf;
- }
- hp[0] = size >> 8;
- hp[1] = size;
- putu48(hp + 2, offset);
- if (size == DELTA_BSIZE)
- {
- if (!hx)
- hx = buzhash(buf);
- putu32(hp + 8, hx);
- memcpy(hp + 12, md5, 4);
- }
- else
- {
- hp[0] |= 0x80; /* small block marker */
- memcpy(hp + 8, md5, 8);
- hx = getu32(hp + 8); /* needed later */
- }
-#if 0
-{
- int j;
- printf("NEW SLOT");
- for (j = 0; j < 16; j++)
- printf(" %02x", hp[j]);
- printf("\n");
-}
-#endif
- if (pwrite(store->fd, hp, 16, store->slotsoffset + (store->usedcnt + 1) * 16) != 16)
- {
- perror("pwrite slot");
- return 0;
- }
- store->freecnt--;
- store->usedcnt++;
-
- /* update hash */
- hm = store->hm;
- hash = store->hash;
- h = hx & hm;
- hh = HASHCHAIN_START;
- while (hash[16 * h] != 0)
- h = HASHCHAIN_NEXT(h, hh, hm);
- memcpy(hash + 16 * h, hp, 16);
- store->hf++;
- return offset;
-}
-
-/* make sure that we found the correct block */
-static int
-checkstore(struct deltastore *store, unsigned long long offset, unsigned char *buf, int size)
-{
- unsigned char buf2[4096];
- while (size)
- {
- int l = size > 4096 ? 4096 : size;
- if (pread(store->fd, buf2, l, offset) != l)
- return 0;
- if (memcmp(buf2, buf, l) != 0)
- return 0;
- size -= l;
- buf += l;
- offset += l;
- }
- return 1;
-}
-
-/*
- * try to find a (non-rolling) block in the store. If not found, add it.
- * returns the store offset, 0 on error
- */
-static unsigned long long
-reuse_or_add_block(struct deltastore *store, unsigned char *buf, int size)
-{
- unsigned char md5[16];
- unsigned int h, hh, hm;
- unsigned char *hash;
- unsigned long long offset;
-
- if (!size || size >= DELTA_BSIZE)
- return 0; /* only small non-rolling blocks for now */
- md5block(buf, size, md5);
- hm = store->hm;
- hash = store->hash;
- h = (md5[0] << 24 | md5[1] << 16 | md5[2] << 8 | md5[3]) & hm;
- hh = HASHCHAIN_START;
- while (hash[16 * h] != 0)
- {
- unsigned char *hp = hash + 16 * h;
- if (((hp[0] & 0x7f) << 8 | hp[1]) == size && !memcmp(hp + 8, md5, 8))
- {
- offset = getu48(hp + 2);
- if (checkstore(store, offset, buf, size))
- return offset;
- }
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- return putinstore(store, buf, size, md5, 0);
-}
-
-/**
- ** block encoding
- **/
-
-static int
-encodelonglong(FILE *ofp, unsigned long long x)
-{
- unsigned long long z = 1;
- int c;
- do
- {
- z = z << 7 | (x & 127);
- x >>= 7;
- }
- while (x);
- for (;;)
- {
- c = (z & 127) | 128;
- z >>= 7;
- if (z == 1)
- break;
- if (putc(c, ofp) == EOF)
- return 0;
- }
- if (putc(c ^ 128, ofp) == EOF)
- return 0;
- return 1;
-}
-
-static int
-encodelonglong_mem(unsigned char *bp, unsigned long long x)
-{
- unsigned long long z = 1;
- int c;
- int l = 0;
- do
- {
- z = z << 7 | (x & 127);
- x >>= 7;
- }
- while (x);
- for (;;)
- {
- c = (z & 127) | 128;
- z >>= 7;
- if (z == 1)
- break;
- *bp++ = c;
- l++;
- }
- *bp = c ^ 128;;
- return l + 1;
-}
-
-
-#if 1
-/* fancy delta conversion, seems to work better than the simple xor */
-static inline unsigned long long
-encodeoffset(unsigned long long oldoffset, unsigned long long offset)
-{
- if (oldoffset & (1LL << 47))
- {
- offset ^= ((1LL << 48) - 1);
- oldoffset ^= ((1LL << 48) - 1);
- }
- if (offset < oldoffset * 2)
- {
- if (offset < oldoffset)
- offset = (oldoffset - offset - 1) << 1 | 1;
- else
- offset = (offset - oldoffset) << 1;
- }
- return offset;
-}
-
-static inline unsigned long long
-decodeoffset(unsigned long long oldoffset, unsigned long long offset)
-{
- int neg = oldoffset & (1LL << 47) ? ((1LL << 48) - 1) : 0;
- oldoffset ^= neg;
- if (offset < oldoffset * 2)
- {
- if (offset & 1)
- offset = oldoffset - ((offset >> 1) + 1);
- else
- offset = oldoffset + (offset >> 1);
- }
- return offset ^ neg;
-}
-
-#else
-static inline unsigned long long
-encodeoffset(unsigned long long oldoffset, unsigned long long offset)
-{
- return oldoffset ^ offset;
-}
-
-static inline unsigned long long
-decodeoffset(unsigned long long oldoffset, unsigned long long offset)
-{
- return oldoffset ^ offset;
-}
-#endif
-
-static int
-flushoutbuf(struct deltaout *out)
-{
- if (!out->outbuf_len)
- return 1;
- if (out->outbuf_len >= MAX_BSIZE_META)
- return 0;
-
- if (out->outbuf_len >= MIN_BSIZE_META)
- {
- /* put as meta block into store! */
- int size = out->outbuf_len;
- unsigned long long offset;
-#if 0
- printf("META size %d\n", out->outbuf_len);
-#endif
- offset = reuse_or_add_block(out->store, out->outbuf, size);
- if (!offset)
- return 0;
- /* encode meta block into outbuf */
- if (out->outbuf_startoffset_set)
- out->lastoffset = out->outbuf_startoffset;
- out->outbuf[0] = 15; /* meta */
- out->outbuf_len = 1;
- out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, size);
- out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, encodeoffset(out->lastoffset, offset));
- out->lastoffset = offset + size;
- out->outbuf_startoffset_set = 0;
- }
-
- if (out->outbuf_startoffset_set)
- {
- /* tricky: fix up first offset! */
- unsigned char buf[9];
- int l = encodelonglong_mem(buf, out->outbuf_set_offset);
- if (fwrite(out->outbuf, out->outbuf_set_len1, 1, out->fp) != 1)
- return 0;
- if (fwrite(buf, l, 1, out->fp) != 1)
- return 0;
- if (out->outbuf_set_len2 < out->outbuf_len && fwrite(out->outbuf + out->outbuf_set_len2, out->outbuf_len - out->outbuf_set_len2, 1, out->fp) != 1)
- return 0;
- }
- else if (fwrite(out->outbuf, out->outbuf_len, 1, out->fp) != 1)
- return 0;
- out->outbuf_len = 0;
- out->outbuf_startoffset_set = 0;
- return 1;
-}
-
-static int
-encodestoreblock_real(struct deltaout *out, unsigned long long offset, unsigned long long size)
-{
-#if 0
- printf("BLOCK %#llx %llu\n", offset, size);
-#endif
- if (out->outbuf_do_meta)
- {
- int lastlen = out->outbuf_len;
- /* the following code is needed as we want to use a lastoffset of
- * zero if this ends up in a meta instruction. So we encode with
- * lastoffset zero but also store the real lastoffset and byte offsets,
- * in order to fix up the offset in flushbuf */
- int set = out->outbuf_startoffset_set;
- if (!set)
- {
- out->outbuf_startoffset_set = 1;
- out->outbuf_startoffset = out->lastoffset;
- out->outbuf_set_offset = encodeoffset(out->lastoffset, offset);
- out->lastoffset = 0;
- }
- out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, out->oldsize + 256);
- if (!set)
- out->outbuf_set_len1 = out->outbuf_len;
- out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, encodeoffset(out->lastoffset, offset));
- if (!set)
- out->outbuf_set_len2 = out->outbuf_len;
-
- if (out->outbuf_len >= DELTA_BSIZE)
- {
- /* buffer too full. revert changes. flush outbuf. retry */
- out->outbuf_len = lastlen;
- if (!set)
- {
- out->outbuf_startoffset_set = 0;
- out->lastoffset = out->outbuf_startoffset;
- }
- if (!flushoutbuf(out))
- return 0;
- return encodestoreblock_real(out, offset, size);
- }
- }
- else
- {
- if (!encodelonglong(out->fp, size + 256))
- return 0;
- if (!encodelonglong(out->fp, encodeoffset(out->lastoffset, offset)))
- return 0;
- }
- out->lastoffset = offset + size;
- return 1;
-}
-
-/* encode a store block instruction
- * we delay the real encoding so that we can join adjacent blocks
- */
-static int
-encodestoreblock(struct deltaout *out, unsigned long long offset, unsigned long long size)
-{
- if (out->oldoffset)
- {
- if (out->oldoffset + out->oldsize == offset)
- {
- out->oldsize += size;
- return 1;
- }
- if (!encodestoreblock_real(out, out->oldoffset, out->oldsize))
- return 0;
- }
- out->oldoffset = offset; /* block not yet written */
- out->oldsize = size;
- return 1;
-}
-
-/* encode a direct data instruction
- */
-static int
-encodedirect(struct deltaout *out, unsigned char *buf, int size)
-{
- if (!size)
- return 1;
- if (size >= 256 - 16)
- return 0;
- if (out->oldoffset)
- {
- if (!encodestoreblock(out, 0, 0)) /* flush */
- return 0;
- }
-#if 0
- printf("DIRECT %u\n", size);
-#endif
- if (out->outbuf_do_meta)
- {
- if (out->outbuf_len + 1 + size >= DELTA_BSIZE)
- {
- /* buffer too full. flush outbuf */
- if (!flushoutbuf(out))
- return 0;
- }
- out->outbuf[out->outbuf_len++] = 16 + size;
- memcpy(out->outbuf + out->outbuf_len, buf, size);
- out->outbuf_len += size;
- }
- else
- {
- if (putc(16 + size, out->fp) == EOF)
- return 0;
- if (fwrite(buf, size, 1, out->fp) != 1)
- return 0;
- }
- return 1;
-}
-
-/**
- ** the delta algorithm
- **/
-
-static unsigned long long
-extendblock(struct deltastore *store, FILE *fp, unsigned long long offset, unsigned long long areaend, unsigned long long maxextend)
-{
- unsigned char buf[1024];
- int c, i, bufl;
- unsigned long long extend = 0;
-
- if (offset >= areaend)
- return 0;
- if (areaend - offset < maxextend)
- maxextend = areaend - offset;
- if (!maxextend)
- return 0;
- i = bufl = 0;
- for (;;)
- {
- if (i == bufl)
- {
- bufl = maxextend > 1024 ? 1024 : maxextend;
- if (bufl == 0)
- return extend;
- if (pread(store->fd, buf, bufl, (off_t)offset) != bufl)
- return extend;
- offset += bufl;
- maxextend -= bufl;
- i = 0;
- }
- c = getc(fp);
- if (c == EOF)
- return extend;
- if (buf[i++] != c)
- {
- ungetc(c, fp);
- return extend;
- }
- extend++;
- }
-}
-
-static unsigned long long
-extendblock_back(struct deltastore *store, unsigned char *data, unsigned long long offset, unsigned long long areastart, unsigned long long maxextend)
-{
- unsigned char buf[1024];
- unsigned long long extend = 0;
- int bufl;
-
- if (offset <= areastart)
- return 0;
- if (offset - areastart < maxextend)
- maxextend = offset - areastart;
- if (!maxextend)
- return 0;
- bufl = 0;
- for (;;)
- {
- if (bufl == 0)
- {
- bufl = maxextend > 1024 ? 1024 : maxextend;
- if (bufl == 0)
- return extend;
- offset -= bufl;
- if (pread(store->fd, buf, bufl, (off_t)offset) != bufl)
- return extend;
- maxextend -= bufl;
- }
- if (*--data != buf[--bufl])
- return extend;
- extend++;
- }
-}
-
-static int
-dosimple(struct deltastore *store, struct deltaout *out, unsigned char *buf, int size)
-{
- unsigned long long offset;
-
- while (size >= DELTA_BSIZE)
- {
- offset = putinstore(store, buf, DELTA_BSIZE, 0, 0);
- if (!offset || !encodestoreblock(out, offset, DELTA_BSIZE))
- return 0;
- size -= DELTA_BSIZE;
- buf += DELTA_BSIZE;
- }
- if (size < MIN_BSIZE)
- return encodedirect(out, buf, size);
- offset = reuse_or_add_block(store, buf, size);
- if (!offset)
- return 0;
- return encodestoreblock(out, offset, size);
-}
-
-static int
-dodelta(struct deltastore *store, FILE *fp, struct deltaout *out, unsigned long long size)
-{
- unsigned char buf[DELTA_BSIZE * 16];
- unsigned char md5[16];
- unsigned long long offset, extendf, extendb;
- unsigned int h, hh, hm, hx;
- unsigned char *hash;
- int c, foundit, bi;
-
-#if 0
- printf("DODELTA\n");
-#endif
- hm = store->hm;
- hash = store->hash;
- while (size)
- {
- if (size < DELTA_BSIZE)
- {
- if (fread(buf, (int)size, 1, fp) != 1)
- return 0;
- if (!dosimple(store, out, buf, (int)size))
- return 0;
- break;
- }
- /* read a block */
- bi = 0;
- if (fread(buf, DELTA_BSIZE, 1, fp) != 1)
- return 0;
- size -= DELTA_BSIZE;
-
- hx = buzhash(buf);
- foundit = 0;
-
- /* start rolling */
- for (;;)
- {
- int md5set = 0;
- /* check if the block at (buf + bi) is in the hash */
-#if 0
- if (hx != buzhash(buf + bi))
- abort();
-#endif
- hh = HASHCHAIN_START;
- for (h = hx & hm; hash[16 * h] != 0; h = HASHCHAIN_NEXT(h, hh, hm))
- {
- unsigned char *hp = hash + 16 * h;
- /* first check block len */
- if (hp[0] != (DELTA_BSIZE >> 8) || hp[1] != (DELTA_BSIZE & 0xff))
- continue;
- /* then check complete hash value */
- if (hp[8] != (hx >> 24))
- continue;
- if ((hp[8] << 24 | hp[9] << 16 | hp[10] << 8 | hp[11]) != hx)
- continue;
- /* then check strong hash */
- if (!md5set)
- {
- md5block(buf + bi, DELTA_BSIZE, md5);
- md5set = 1;
- }
- if (memcmp(hp + 12, md5, 4) != 0)
- continue;
- /* looks good. check data */
- offset = getu48(hp + 2);
- if (!checkstore(store, offset, buf + bi, DELTA_BSIZE))
- continue;
- /* yes! found block in store! */
- /* try to extend found block */
- c = finddataarea(store, offset);
- extendf = extendb = 0;
- if (c >= 0)
- {
- extendf = extendblock(store, fp, offset + DELTA_BSIZE, store->offsets[c + 1], size);
- size -= extendf; /* extended bytes */
- extendb = extendblock_back(store, buf + bi, offset, store->offsets[c], bi);
- offset -= extendb;
- bi -= extendb;
- }
- /* encode data before block */
- if (bi)
- {
- if (!dosimple(store, out, buf, bi))
- return 0;
- bi = 0;
- }
- /* encode */
- if (!encodestoreblock(out, offset, DELTA_BSIZE + extendf + extendb))
- return 0;
- foundit = 1;
- break;
- }
- if (foundit)
- break;
-
- /* not found. move block one byte */
- if (!size)
- {
- if (!dosimple(store, out, buf, bi + DELTA_BSIZE))
- return 0;
- break;
- }
- c = fgetc(fp);
- if (c == EOF)
- return 0;
- size--;
- buf[DELTA_BSIZE + bi] = c;
- hx = (hx << 1) ^ (hx & (1 << 31) ? 1 : 0) ^ buz_noise[c];
- c = buf[bi++];
- hx ^= buz_noise[c] ^ (0x83d31df4U ^ 0x07a63be9U);
- if (bi == sizeof(buf) - DELTA_BSIZE)
- {
- /* trim down, but leave one block for backward extension */
- if (!dosimple(store, out, buf, bi - DELTA_BSIZE))
- return 0;
- /* no overlap as the buffer is >= 4 * DELTA_BSIZE */
- memcpy(buf, buf + bi - DELTA_BSIZE, 2 * DELTA_BSIZE);
- bi = DELTA_BSIZE;
- }
- }
- }
- if (!encodestoreblock(out, 0, 0)) /* flush */
- return 0;
- if (!flushoutbuf(out))
- return 0;
- return 1;
-}
-
-static int
-readdeltastore(struct deltastore *store, int fd, int rdonly, unsigned long long xsize)
-{
- unsigned char *slots;
- unsigned char oneslot[16];
- unsigned long long offset, nextoffset, lastgoodoffset;
- struct stat st;
- unsigned long long fsize;
- unsigned int nslots = 0, hslots;
- unsigned char *hash;
- unsigned int hm, h, hh, hf, hd;
- int isbad = 0;
- int i, lasti, cnt, maxcnt = 0;
- unsigned int drop = 0;
-
- memset(store, 0, sizeof(*store));
- store->fd = fd;
- store->rdonly = rdonly;
- if (fstat(fd, &st))
- {
- perror("fstat");
- return 0;
- }
- fsize = (unsigned long long)st.st_size;
- store->end = fsize;
-
- /* first pass: find number of used entries */
- offset = 0;
- lastgoodoffset = -1;
- for (;;)
- {
- if (offset == fsize)
- break;
- if (offset + 16 > fsize)
- {
- fprintf(stderr, "WARNING: slot area exceeds file size!\n");
- isbad = 1;
- break;
- }
- if (pread(fd, oneslot, 16, offset) != 16)
- return 0;
- if (memcmp(oneslot, "OBSDELT", 8) != 0)
- {
- fprintf(stderr, "WARNING: slot magic error!\n");
- isbad = 1;
- break;
- }
- cnt = oneslot[8] << 8 | oneslot[9];
- nextoffset = getu48(oneslot + 10);
- if (!nextoffset)
- nextoffset = fsize;
- offset += (cnt + 1) * 16;
- if (offset > fsize)
- {
- fprintf(stderr, "WARNING: slot area exceeds file size!\n");
- isbad = 1;
- break;
- }
- nslots += cnt;
- lastgoodoffset = offset - (cnt + 1) * 16;
- if (cnt > maxcnt)
- maxcnt = cnt;
- if (offset > nextoffset)
- {
- fprintf(stderr, "WARNING: end of slots bigger than nextoffset!\n");
- isbad = 1;
- break;
- }
- offset = nextoffset;
- }
-
- if (isbad && !store->rdonly)
- {
- fprintf(stderr, "WARNING: fixing up bad slots!\n");
- if (lastgoodoffset == -1)
- {
- /* worst case: first slots area is damaged */
- memset(oneslot, 0, 16);
- memcpy(oneslot, "OBSDELT", 8);
- putu48(oneslot + 10, fsize);
- if (pwrite(store->fd, oneslot, 16, 0) != 16)
- {
- perror("pwrite repair first slots area");
- return 0;
- }
- }
- else
- {
- putu48(oneslot + 10, fsize);
- if (pwrite(store->fd, oneslot + 10, 6, lastgoodoffset + 10) != 6)
- {
- perror("pwrite repair bad slots area nextoffset");
- return 0;
- }
- }
- isbad = 0;
- }
-
- slots = calloc(maxcnt + 1, 16);
- if (!slots)
- return 0;
-
- /* find good hash size and allocate hash */
- hslots = nslots + xsize / 512;
- while (hslots & (hslots - 1))
- hslots = hslots & (hslots - 1);
- if (hslots < 16384)
- hslots = 16384; /* 1 MByte min mem */
- while (hslots > 128 * 1024 * 1024) /* 8 GByte max mem */
- {
- /* oh no. max size reached. drop half of slots */
- hslots >>= 1;
- drop += (drop ? drop : nslots) / 2;
- }
- hslots *= 4;
- store->hm = hm = hslots - 1;
- store->hash = hash = calloc(hm + 1, 16);
- if (!hash)
- {
- fprintf(stderr, "could not allocate hash (%u MB)\n", (hm + 1) / (1024 * 1024 / 16));
- free(slots);
- return 0;
- }
-
- /* second pass: fill the hash */
- offset = 0;
- hf = hd = 0;
- for (;;)
- {
- int toread = 16 * (maxcnt + 1);
- if (isbad && lastgoodoffset == -1)
- break;
- if (offset >= fsize)
- break;
- if (offset + toread > fsize)
- toread = fsize - offset;
- if (pread(fd, slots, toread, offset) != toread)
- {
- free(slots);
- return 0;
- }
- if (memcmp(slots, "OBSDELT", 8) != 0)
- break;
- cnt = oneslot[8] << 8 | oneslot[9];
- offset += 16 * (cnt + 1);
- nextoffset = getu48(slots + 10);
- if (!nextoffset)
- nextoffset = fsize;
- if (offset > nextoffset)
- break;
- if (offset != nextoffset)
- adddataarea(store, offset, nextoffset);
- lasti = 0;
- for (i = 1; i < cnt + 1; i++)
- if (slots[16 * i])
- {
- unsigned char *hp = slots + 16 * i;
- int len = (hp[0] & 127) << 8 | hp[1];
- unsigned long long o = getu48(hp + 2);
- lasti = i;
- if (drop)
- {
- drop--;
- hd++;
- }
- else if (o >= offset && o + len <= nextoffset)
- {
- /* a good one. add to hash. */
- h = getu32(hp + 8) & hm;
- hh = HASHCHAIN_START;
- while (hash[16 * h] != 0)
- h = HASHCHAIN_NEXT(h, hh, hm);
- memcpy(hash + 16 * h, hp, 16);
- hf++;
- }
- }
- store->slotsoffset = offset - 16 * (cnt + 1);
- store->freecnt = cnt - lasti;
- store->usedcnt = lasti;
- if (isbad && lastgoodoffset == store->slotsoffset)
- break;
- offset = nextoffset;
- }
- store->hf = hf;
- store->hd = hd;
-#if 0
- printf("readdeltastore: have %d entries, %d dropped, hash size %d\n", hf, hd, hm + 1);
-#endif
- free(slots);
- return 1;
-}
-
-static void
-printdeltastorestats(struct deltastore *store)
-{
- unsigned int buckets[2048];
- unsigned int hm, hf, hd;
- unsigned char *hp;
- int i, j, bc = 16;
-
- memset(buckets, 0, sizeof(buckets));
- hm = store->hm;
- hf = store->hf;
- hd = store->hd;
-
- printf("store size: %llu (%u MB)\n", store->end, (unsigned int)(store->end / (1024 * 1024)));
- printf("hash mask: 0x%x (%u MB hash mem)\n", hm, (unsigned int)(hm / 65536) + 1);
- printf("hash entries set: %u (%.2f %%)\n", hf, ((double)hf * 100) / ((double)hm + 1));
- printf("hash entries dropped: %u (%.2f %%)\n", hd, hd ? ((double)hd * 100) / ((double)hf + (double)hd) : 0.);
- for (hp = store->hash;; hp += 16)
- {
- if (hp[0])
- buckets[((hp[0] & 0x7f) << 8 | hp[1]) / 16]++;
- if (!hm--)
- break;
- }
- for (i = 2047; i >= 1; i--)
- if (buckets[i])
- break;
- i++;
- while (i > 16)
- {
- for (j = 0; j < i; j += 2)
- buckets[j / 2] = buckets[j] + buckets[j + 1];
- i = (i + 1) / 2;
- bc *= 2;
- }
- printf("block stats:\n");
- for (j = 0; j < i; j++)
- printf(" size %#6x - %#6x: %10u\n", j * bc, j * bc + bc - 1, buckets[j]);
- printf("data areas: %d\n", store->noffsets / 2);
-}
-
-static void
-freedeltastore(struct deltastore *store)
-{
- if (store->hash)
- free(store->hash);
- if (store->offsets)
- free(store->offsets);
-}
-
-static void
-settimes(int fd, struct stat *st)
-{
- struct timeval tv[2];
-
- tv[0].tv_sec = st->st_atime;
- tv[0].tv_usec = 0;
- tv[1].tv_sec = st->st_mtime;
- tv[1].tv_usec = 0;
- futimes(fd, tv);
-}
-
-static int
-checkhexcomp(unsigned char *buf)
-{
- int i, hexcomp = 0;
- for (i = 0; i < 110; i++)
- {
- int c = *buf++;
- if (c >= '0' && c <= '9')
- continue;
- else if (c >= 'A' && c <= 'F')
- {
- if (!hexcomp)
- hexcomp = 1;
- if (hexcomp != 1)
- break;
- }
- else if (c >= 'a' && c <= 'f')
- {
- if (!hexcomp)
- hexcomp = 2;
- if (hexcomp != 2)
- break;
- }
- else
- break;
- }
- if (i < 110)
- return 0;
- return hexcomp ? hexcomp : 1;
-}
-
-static unsigned int fromhex(unsigned char *hex)
-{
- int i;
- unsigned int x = 0;
- for (i = 0; i < 8; i++, hex++)
- {
- if (*hex >= '0' && *hex <= '9')
- x = x << 4 | (*hex - '0');
- else if (*hex >= 'a' && *hex <= 'f')
- x = x << 4 | (*hex - ('a' - 10));
- else if (*hex >= 'A' && *hex <= 'F')
- x = x << 4 | (*hex - ('A' - 10));
- }
- return x;
-}
-
-static void
-magic_inode_increment(unsigned char *cpio)
-{
- unsigned int inode = getu32(cpio + 3);
- if (inode)
- putu32(cpio + 3, inode + 1);
-}
-
-static int
-makedelta(struct deltastore *store, FILE *fp, FILE *ofp, unsigned long long fpsize)
-{
- unsigned char cpiohead[1024 + 16384];
- unsigned char oldcpio[1024];
- int trailerseen = 0;
- int i, j;
- struct deltaout out;
-
- if (fpsize >= (1LL << 40))
- return 0;
-
- /* init deltaout struct */
- memset(&out, 0, sizeof(out));
- out.fp = ofp;
- out.store = store;
- out.outbuf_do_meta = 1; /* create meta blocks */
-
- /* write our header */
- memset(cpiohead, 0, 32);
- memcpy(cpiohead, "OBScpio", 8);
- putu48(cpiohead + 10, fpsize);
- if (fwrite(cpiohead, 16, 1, ofp) != 1)
- return 0;
-
- memset(oldcpio, 0, 1024);
- while (!trailerseen)
- {
- unsigned long long fsize;
- unsigned int hsize, nsize;
- int run;
- int hexcomp;
- int noff = 110;
- int zero;
-
- /* read the header */
- if (fread(cpiohead, 110, 1, fp) != 1)
- {
- fprintf(stderr, "cpio header read error\n");
- return 0;
- }
- if (memcmp(cpiohead, "070701", 6) != 0)
- {
- fprintf(stderr, "not a newc cpio archive\n");
- return 0;
- }
- fsize = fromhex(cpiohead + 54);
- nsize = fromhex(cpiohead + 94);
- if (nsize > 16384)
- {
- fprintf(stderr, "filename size too big\n");
- return 0;
- }
- hsize = noff + nsize;
- if (hsize & 3)
- hsize += 4 - (hsize & 3);
- /* check if we can do hex compression */
- hexcomp = checkhexcomp(cpiohead);
- if (hexcomp)
- {
- /* do fancy hex compression */
- cpiohead[0] = 0x07;
- cpiohead[1] = 0x07;
- cpiohead[2] = 0x01;
- for (i = 3; i < 55; i += 4)
- {
- unsigned int x = fromhex(cpiohead + i * 2);
- putu32(cpiohead + i, x);
- }
- noff -= 55;
- hsize -= 55;
- }
-
-#if 0
- printf("fsize = %d, nsize = %d, hsize = %d\n", fsize, nsize, hsize);
-#endif
- if (fread(cpiohead + noff, hsize - noff, 1, fp) != 1)
- {
- fprintf(stderr, "cpio header read error\n");
- return 0;
- }
- if (fsize == 0 && nsize == 11 && !memcmp(cpiohead + noff, "TRAILER!!!", 11))
- {
- trailerseen = 1;
- while (hsize < sizeof(cpiohead))
- {
- int c = getc(fp);
- if (c == EOF)
- break;
- cpiohead[hsize++] = c;
- }
- if (hsize == sizeof(cpiohead))
- {
- fprintf(stderr, "excess trailer data\n");
- return 0;
- }
- }
- /* check trailing zero */
- for (zero = 0; zero < 4; zero++)
- if (cpiohead[hsize - 1 - zero] != 0)
- break;
- /* write the header */
- if (putc(2 + hexcomp, ofp) == EOF)
- return 0;
- for (i = 0; i < 1024 && i < hsize; i++)
- {
- cpiohead[i] ^= oldcpio[i];
- oldcpio[i] ^= cpiohead[i];
- }
- if (hexcomp)
- magic_inode_increment(oldcpio);
- run = 0;
- hsize -= zero;
- for (i = 0; i < hsize; i++)
- {
- if (cpiohead[i] == 0)
- {
- run++;
- if (i + 1 < hsize)
- continue;
- }
- while (run)
- {
- int z = run > 127 ? 127 : run;
- if (putc(z, ofp) == EOF)
- return 0;
- run -= z;
- }
- if (cpiohead[i] == 0)
- break; /* ended in zero */
- for (j = i; j < hsize - 1; j++)
- if (cpiohead[j] == 0 && cpiohead[j + 1] == 0)
- break;
- if (j == hsize - 1)
- j = hsize;
- j -= i;
- if (j > 123)
- j = 123;
- if (putc(j + 128, ofp) == EOF)
- return 0;
- while (j-- > 0)
- {
- int z = cpiohead[i++];
- if (putc(z, ofp) == EOF)
- return 0;
- }
- i--;
- }
- if (putc(zero ? zero + 251 : 0, ofp) == EOF)
- return 0;
- if (fsize)
- {
- if (!dodelta(store, fp, &out, fsize))
- return 0;
- if ((fsize & 3) != 0)
- {
- i = 4 - (fsize & 3);
- if (putc(4 + i, ofp) == EOF)
- return 0;
- while (i--)
- {
- if (getc(fp) != 0)
- {
- fprintf(stderr, "non-zero padding\n");
- return 0;
- }
- }
- }
- }
- }
- if (putc(1, ofp) == EOF)
- return 0;
- if (fflush(ofp) != 0)
- return 0;
- return 1;
-}
-
-static unsigned long long
-expandobscpio_next(FILE *fp)
-{
- unsigned long long x = 0;
- int i;
- for (;;)
- {
- i = getc(fp);
- if (i == EOF)
- return (unsigned long long)(-1);
- if ((i & 128) == 0)
- return x << 7 | i;
- x = x << 7 | (i ^ 128);
- }
-}
-
-static unsigned long long
-expandobscpio_next_mem(unsigned char **bp, unsigned int *lp)
-{
- unsigned long long x = 0;
- unsigned char *b = *bp;
- unsigned int l = *lp;
- int i;
- for (;;)
- {
- if (l == 0)
- return (unsigned long long)(-1);
- i = *b++;
- l--;
- if ((i & 128) == 0)
- {
- *bp = b;
- *lp = l;
- return x << 7 | i;
- }
- x = x << 7 | (i ^ 128);
- }
-}
-
-static int
-expandobscpio_direct_mem(unsigned char **bp, unsigned int *lp, void *out, unsigned int outlen)
-{
- if (*lp < outlen)
- return 0;
- if (outlen)
- memcpy(out, *bp, outlen);
- *bp += outlen;
- *lp -= outlen;
- return 1;
-}
-
-static int
-expandcpiohead(FILE *fp, FILE *ofp, unsigned char *cpio, int hexcomp)
-{
- int l = 0;
- int zero;
- for (;;)
- {
- int c = getc(fp);
- if (c == EOF)
- return 0;
- if (c == 0)
- break;
- if (c < 128)
- zero = 1;
- else if (c >= 252)
- {
- zero = -1;
- c -= 251;
- }
- else
- {
- zero = 0;
- c -= 128;
- }
- while (c-- > 0)
- {
- int x = zero ? 0 : getc(fp);
- if (x == EOF)
- return 0;
- if (l < 1024)
- {
- if (zero >= 0)
- x ^= cpio[l];
- cpio[l++] = x;
- }
- if (hexcomp && l <= 55)
- {
- int lettershift = (hexcomp == 1 ? 'A' : 'a') - ('0' + 10);
- int x1 = (x >> 4) + '0';
- int x2 = (x & 15) + '0';
- if (x1 > '9')
- x1 += lettershift;
- if (x2 > '9')
- x2 += lettershift;
- if (putc(x1, ofp) == EOF || putc(x2, ofp) == EOF)
- return 0;
- }
- else if (putc(x, ofp) == EOF)
- return 0;
- }
- if (zero < 0)
- break;
- }
- if (hexcomp)
- magic_inode_increment(cpio);
- return 1;
-}
-
-static int
-expandobscpio(FILE *fp, int fdstore, FILE *ofp)
-{
- unsigned char magic[16];
- unsigned char metabuf[16384];
- unsigned char cpio[1024];
- unsigned long long o, l;
- struct stat st;
- unsigned long long oldoffset = 0;
- unsigned char *meta = 0;
- unsigned int metal = 0;
- unsigned long long oldoffset_meta = 0;
-
- if (!fp || !ofp || fdstore == -1)
- return 0;
- if (fstat(fileno(fp), &st))
- return 0;
- if (fread(magic, 16, 1, fp) != 1 || memcmp(magic, "OBScpio", 7) != 0)
- return 0;
- memset(cpio, 0, sizeof(cpio));
- for (;;)
- {
- if (meta && !metal)
- {
- meta = 0;
- oldoffset = oldoffset_meta;
- }
- if (meta)
- l = expandobscpio_next_mem(&meta, &metal);
- else
- l = expandobscpio_next(fp);
- if (l == (unsigned long long)(-1))
- return 0;
-#if 0
-printf("NEXT %d\n", l);
-#endif
- if (l < 16)
- {
- /* first 16 reserved as instructions */
- if (meta)
- return 0;
- if (l == 1) /* EOF */
- break;
- if (l == 2 || l == 3 || l == 4) /* CPIO */
- {
- if (!expandcpiohead(fp, ofp, cpio, l - 2))
- return 0;
- }
- else if (l == 5 || l == 6 || l == 7) /* ZERO */
- {
- l -= 4;
- while (l--)
- if (putc(0, ofp) == EOF)
- return 0;
- }
- else if (l == 15) /* META */
- {
- l = expandobscpio_next(fp);
- if (l == (unsigned long long)(-1))
- return 0;
- if (l < 16 || l > sizeof(metabuf))
- return 0;
- o = expandobscpio_next(fp);
- if (o == (unsigned long long)(-1))
- return 0;
- o = decodeoffset(oldoffset, o);
- oldoffset_meta = o + l;
- oldoffset = 0;
- if (pread(fdstore, metabuf, (size_t)l, (off_t)o) != (size_t)l)
- return 0;
- metal = (unsigned int)l;
- meta = metabuf;
- }
- else
- return 0;
- }
- else if (l < 256)
- {
- /* direct bytes */
- l -= 16;
- if (l)
- {
- char buf[256];
- if (meta)
- {
- if (expandobscpio_direct_mem(&meta, &metal, buf, l) != 1)
- return 0;
- }
- else if (fread(buf, (int)l, 1, fp) != 1)
- return 0;
- if (fwrite(buf, (int)l, 1, ofp) != 1)
- return 0;
- }
- }
- else
- {
- /* bytes from the store */
- l -= 256;
- if (meta)
- o = expandobscpio_next_mem(&meta, &metal);
- else
- o = expandobscpio_next(fp);
- if (o == (unsigned long long)(-1))
- return 0;
- o = decodeoffset(oldoffset, o);
- oldoffset = o + l;
- while (l)
- {
- char buf[8192];
- size_t count = l > 8192 ? 8192 : l;
- if (pread(fdstore, buf, count, (off_t)o) != count)
- return 0;
- if (fwrite(buf, count, 1, ofp) != 1)
- return 0;
- o += count;
- l -= count;
- }
- }
- }
- if (fflush(ofp) != 0)
- return 0;
- settimes(fileno(ofp), &st);
- return 1;
-}
-
-static void
-printobscpioinstr(FILE *fp, int fdstore, int withmeta)
-{
- unsigned char magic[16];
- unsigned long long oldoffset = 0, o, l;
- unsigned char metabuf[16384];
- unsigned char *meta = 0;
- unsigned int metal = 0;
- unsigned long long oldoffset_meta = 0;
-
- unsigned int stats_cpio = 0;
- unsigned long long stats_cpio_len = 0;
- unsigned int stats_direct = 0;
- unsigned long long stats_direct_len = 0;
- unsigned int stats_store = 0;
- unsigned long long stats_store_len = 0;
- unsigned int stats_zero = 0;
- unsigned long long stats_zero_len = 0;
- unsigned int stats_meta = 0;
- unsigned long long stats_meta_len = 0;
- unsigned int stats_meta_store = 0;
- unsigned long long stats_meta_store_len = 0;
- unsigned int stats_meta_direct = 0;
- unsigned long long stats_meta_direct_len = 0;
-
- if (fread(magic, 16, 1, fp) != 1 || memcmp(magic, "OBScpio", 7) != 0)
- return;
- for (;;)
- {
- if (meta && !metal)
- {
- meta = 0;
- oldoffset = oldoffset_meta;
- }
- if (meta)
- l = expandobscpio_next_mem(&meta, &metal);
- else
- l = expandobscpio_next(fp);
- if (l == (unsigned long long)(-1))
- return;
- if (l < 16)
- {
- if (meta)
- return;
- if (l == 1)
- {
- printf("end\n");
- break;
- }
- if (l == 2 || l == 3 || l == 4) /* CPIO HEADER */
- {
- printf("cpio%d", (int)l);
- stats_cpio++;
- for (;;)
- {
- int c = getc(fp);
- if (c == EOF)
- return;
- stats_cpio_len++;
- if (c == 0)
- {
- printf(" (0)");
- break;
- }
- if (c < 128)
- printf(" [%d]", c);
- else if (c >= 252)
- {
- printf(" (%d)", c - 251);
- break;
- }
- else
- {
- c -= 128;
- printf(" %d", c);
- stats_cpio_len += c;
- while (c--)
- if (getc(fp) == EOF)
- return;
- }
- }
- printf("\n");
- }
- else if (l == 5 || l == 6 || l == 7) /* ZERO */
- {
- printf("zero %d\n", (int)l - 4);
- stats_zero++;
- stats_zero_len += (int)l - 4;
- }
- else if (l == 15) /* META */
- {
- l = expandobscpio_next(fp);
- if (l == (unsigned long long)(-1))
- return;
- if (l < 16 || l > sizeof(metabuf))
- return;
- o = expandobscpio_next(fp);
- if (o == (unsigned long long)(-1))
- return;
- o = decodeoffset(oldoffset, o);
- oldoffset = o + l;
- printf("meta %#llx %llu\n", o, l);
- stats_meta++;
- stats_meta_len += l;
- if (withmeta)
- {
- oldoffset_meta = o + l;
- oldoffset = 0;
- if (pread(fdstore, metabuf, (size_t)l, (off_t)o) != (size_t)l)
- return;
- metal = (unsigned int)l;
- meta = metabuf;
- }
- }
- else
- return;
- continue;
- }
- if (meta)
- printf(" ");
- if (l < 256)
- {
- l -= 16;
- printf("direct %d\n", (int)l);
- if (meta)
- {
- stats_meta_direct++;
- stats_meta_direct_len += l;
- }
- else
- {
- stats_direct++;
- stats_direct_len += l;
- }
- if (meta)
- {
- if (l > metal)
- return;
- metal -= l;
- meta += l;
- }
- else
- {
- while (l--)
- if (getc(fp) == EOF)
- return;
- }
- continue;
- }
- l -= 256;
- if (meta)
- o = expandobscpio_next_mem(&meta, &metal);
- else
- o = expandobscpio_next(fp);
- if (o == (unsigned long long)(-1))
- return;
- o = decodeoffset(oldoffset, o);
- oldoffset = o + l;
- printf("store %#llx %llu\n", o, l);
- if (meta)
- {
- stats_meta_store++;
- stats_meta_store_len += l;
- }
- else
- {
- stats_store++;
- stats_store_len += l;
- }
- }
- printf("stats cpio %u len %llu\n", stats_cpio, stats_cpio_len);
- printf("stats direct %u len %llu\n", stats_direct, stats_direct_len);
- if (withmeta)
- printf("stats meta_direct %u len %llu\n", stats_meta_direct, stats_meta_direct_len);
- printf("stats store %u len %llu\n", stats_store, stats_store_len);
- if (withmeta)
- printf("stats meta_store %u len %llu\n", stats_meta_store, stats_meta_store_len);
- printf("stats zero %u len %llu\n", stats_zero, stats_zero_len);
- printf("stats meta %u len %llu\n", stats_meta, stats_meta_len);
- if (withmeta)
- printf("stats instr %u\n", stats_cpio + stats_direct + stats_store + stats_zero + stats_meta + 1 + stats_meta_direct + stats_meta_store);
- printf("stats file_instr %u\n", stats_cpio + stats_direct + stats_store + stats_zero + stats_meta + 1);
- printf("stats file_data %lld\n", stats_cpio_len + stats_direct_len);
- printf("stats file_size %lld\n", (unsigned long long)ftell(fp));
-}
-
-MODULE = BSSolv PACKAGE = BSSolv
-
-void
-depsort(HV *deps, SV *mapp, SV *cycp, ...)
- PPCODE:
- {
- int i, j, k, cy, cycstart, nv;
- SV *sv, **svp;
- Id id, *e;
- Id *mark;
- char **names;
- Hashtable ht;
- Hashval h, hh, hm;
- HV *mhv = 0;
-
- Queue edata;
- Queue vedge;
- Queue todo;
- Queue cycles;
-
- if (items == 3)
- XSRETURN_EMPTY; /* nothing to sort */
- if (items == 4)
- {
- /* only one item */
- char *s = SvPV_nolen(ST(3));
- EXTEND(SP, 1);
- sv = newSVpv(s, 0);
- PUSHs(sv_2mortal(sv));
- XSRETURN(1); /* nothing to sort */
- }
-
- if (mapp && SvROK(mapp) && SvTYPE(SvRV(mapp)) == SVt_PVHV)
- mhv = (HV *)SvRV(mapp);
-
- queue_init(&edata);
- queue_init(&vedge);
- queue_init(&todo);
- queue_init(&cycles);
-
- hm = mkmask(items);
- ht = solv_calloc(hm + 1, sizeof(*ht));
- names = solv_calloc(items, sizeof(char *));
- nv = 1;
- for (i = 3; i < items; i++)
- {
- char *s = SvPV_nolen(ST(i));
- h = strhash(s) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- if (!strcmp(names[id], s))
- break;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- if (id)
- continue; /* had that one before, ignore */
- id = nv++;
- ht[h] = id;
- names[id] = s;
- }
-
- /* we now know all vertices, create edges */
- queue_push(&vedge, 0);
- queue_push(&edata, 0);
- for (i = 1; i < nv; i++)
- {
- svp = hv_fetch(deps, names[i], strlen(names[i]), 0);
- sv = svp ? *svp : 0;
- queue_push(&vedge, edata.count);
- if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
- {
- AV *av = (AV *)SvRV(sv);
- for (j = 0; j <= av_len(av); j++)
- {
- char *s;
- STRLEN slen;
-
- svp = av_fetch(av, j, 0);
- if (!svp)
- continue;
- sv = *svp;
- s = SvPV(sv, slen);
- if (!s)
- continue;
- if (mhv)
- {
- /* look up in dep map */
- svp = hv_fetch(mhv, s, slen, 0);
- if (svp)
- {
- s = SvPV(*svp, slen);
- if (!s)
- continue;
- }
- }
- /* look up in hash */
- h = strhash(s) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- if (!strcmp(names[id], s))
- break;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- if (!id)
- continue; /* not known, ignore */
- if (id == i)
- continue; /* no self edge */
- queue_push(&edata, id);
- }
- }
- queue_push(&edata, 0);
- }
- solv_free(ht);
-
- if (0)
- {
- printf("vertexes: %d\n", vedge.count - 1);
- for (i = 1; i < vedge.count; i++)
- {
- printf("%d %s:", i, names[i]);
- Id *e = edata.elements + vedge.elements[i];
- for (; *e; e++)
- printf(" %d", *e);
- printf("\n");
- }
- }
-
-
- /* now everything is set up, sort em! */
- mark = solv_calloc(vedge.count, sizeof(Id));
- for (i = vedge.count - 1; i; i--)
- queue_push(&todo, i);
- EXTEND(SP, vedge.count - 1);
- while (todo.count)
- {
- i = queue_pop(&todo);
- // printf("check %d\n", i);
- if (i < 0)
- {
- i = -i;
- mark[i] = 2;
- sv = newSVpv(names[i], 0);
- PUSHs(sv_2mortal(sv));
- continue;
- }
- if (mark[i] == 2)
- continue;
- if (mark[i] == 0)
- {
- int edgestovisit = 0;
- Id *e = edata.elements + vedge.elements[i];
- for (; *e; e++)
- {
- if (*e == -1)
- continue; /* broken */
- if (mark[*e] == 2)
- continue;
- if (!edgestovisit++)
- queue_push(&todo, -i);
- queue_push(&todo, *e);
- }
- if (!edgestovisit)
- {
- mark[i] = 2;
- sv = newSVpv(names[i], 0);
- PUSHs(sv_2mortal(sv));
- }
- else
- mark[i] = 1;
- continue;
- }
- /* oh no, we found a cycle, record and break it */
- cy = cycles.count;
- for (j = todo.count - 1; j >= 0; j--)
- if (todo.elements[j] == -i)
- break;
- cycstart = j;
- // printf("cycle:\n");
- for (j = cycstart; j < todo.count; j++)
- if (todo.elements[j] < 0)
- {
- k = -todo.elements[j];
- mark[k] = 0;
- queue_push(&cycles, k);
- // printf(" %d\n", k);
- }
- queue_push(&cycles, 0);
- todo.elements[cycstart] = i;
- /* break it */
- for (k = cy; cycles.elements[k]; k++)
- ;
- if (!cycles.elements[k])
- k = cy;
- j = cycles.elements[k + 1] ? cycles.elements[k + 1] : cycles.elements[cy];
- k = cycles.elements[k];
- /* breaking edge from k -> j */
- // printf("break %d -> %d\n", k, j);
- e = edata.elements + vedge.elements[k];
- for (; *e; e++)
- if (*e == j)
- break;
- if (!*e)
- abort();
- *e = -1;
- todo.count = cycstart + 1;
- }
-
- /* recored cycles */
- if (cycles.count && cycp && SvROK(cycp) && SvTYPE(SvRV(cycp)) == SVt_PVAV)
- {
- AV *av = (AV *)SvRV(cycp);
- for (i = 0; i < cycles.count;)
- {
- AV *av2 = newAV();
- for (; cycles.elements[i]; i++)
- {
- SV *sv = newSVpv(names[cycles.elements[i]], 0);
- av_push(av2, sv);
- }
- av_push(av, newRV_noinc((SV*)av2));
- i++;
- }
- }
- queue_free(&cycles);
-
- queue_free(&edata);
- queue_free(&vedge);
- queue_free(&todo);
- solv_free(mark);
- solv_free(names);
- }
-
-void
-gen_meta(AV *subp, ...)
- PPCODE:
- {
- Hashtable ht;
- Hashval h, hh, hm;
- char **subpacks;
- struct metaline *lines, *lp;
- int nlines;
- int i, j, cycle, ns;
- char *s, *s2, *lo;
- Id id;
- Queue cycles;
- Id cycles_buf[64];
-
- if (items == 1)
- XSRETURN_EMPTY; /* nothing to generate */
-
- queue_init_buffer(&cycles, cycles_buf, sizeof(cycles_buf)/sizeof(*cycles_buf));
- hm = mkmask(av_len(subp) + 2);
- ht = solv_calloc(hm + 1, sizeof(*ht));
- subpacks = solv_calloc(av_len(subp) + 2, sizeof(char *));
- for (j = 0; j <= av_len(subp); j++)
- {
- SV **svp = av_fetch(subp, j, 0);
- if (!svp)
- continue;
- s = SvPV_nolen(*svp);
- h = strhash(s) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- h = HASHCHAIN_NEXT(h, hh, hm);
- ht[h] = j + 1;
- subpacks[j + 1] = s;
- }
-
- lines = solv_calloc(items - 1, sizeof(*lines));
- nlines = items - 1;
- /* lines are of the form "md5sum pkg/pkg..." */
- for (i = 0, lp = lines; i < nlines; i++, lp++)
- {
- s = SvPV_nolen(ST(i + 1));
- if (strlen(s) < 35 || s[32] != ' ' || s[33] != ' ')
- croak("gen_meta: bad line %s\n", s);
- /* count '/' */
- lp->l = s;
- ns = 0;
- cycle = 0;
- lo = s + 34;
- for (s2 = lo; *s2; s2++)
- if (*s2 == '/')
- {
- if (!cycle)
- {
- *s2 = 0;
- h = strhash(lo) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- if (!strcmp(lo, subpacks[id]))
- break;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- *s2 = '/';
- if (id)
- cycle = 1 + ns;
- }
- ns++;
- lo = s2 + 1;
- }
- if (!cycle)
- {
- h = strhash(lo) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- if (!strcmp(lo, subpacks[id]))
- break;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- if (id)
- cycle = 1 + ns;
- }
- if (cycle)
- {
- lp->killed = 1;
- if (cycle > 1) /* ignore self cycles */
- queue_push(&cycles, i);
- }
- lp->nslash = ns;
- lp->lastoff = lo - s;
- }
- solv_free(ht);
- solv_free(subpacks);
-
- /* if we found cycles, prune em */
- if (cycles.count)
- {
- char *cycledata = 0;
- int cycledatalen = 0;
-
- cycledata = solv_extend(cycledata, cycledatalen, 1, 1, 255);
- cycledata[0] = 0;
- cycledatalen += 1;
- hm = mkmask(cycles.count);
- ht = solv_calloc(hm + 1, sizeof(*ht));
- for (i = 0; i < cycles.count; i++)
- {
- char *se;
- s = lines[cycles.elements[i]].l + 34;
- se = strchr(s, '/');
- if (se)
- *se = 0;
- h = strhash(s) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- if (!strcmp(s, cycledata + id))
- break;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- if (id)
- continue;
- cycledata = solv_extend(cycledata, cycledatalen, strlen(s) + 1, 1, 255);
- ht[h] = cycledatalen;
- strcpy(cycledata + cycledatalen, s);
- cycledatalen += strlen(s) + 1;
- if (se)
- *se = '/';
- }
- for (i = 0, lp = lines; i < nlines; i++, lp++)
- {
- if (lp->killed || !lp->nslash)
- continue;
- lo = strchr(lp->l + 34, '/') + 1;
- for (s2 = lo; *s2; s2++)
- if (*s2 == '/')
- {
- *s2 = 0;
- h = strhash(lo) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- if (!strcmp(lo, cycledata + id))
- break;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- *s2 = '/';
- if (id)
- {
- lp->killed = 1;
- break;
- }
- lo = s2 + 1;
- }
- if (lp->killed)
- continue;
- h = strhash(lo) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- if (!strcmp(lo, cycledata + id))
- break;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- if (id)
- {
- lp->killed = 1;
- }
- }
- solv_free(ht);
- cycledata = solv_free(cycledata);
- queue_free(&cycles);
- }
-
- /* cycles are pruned, now sort array */
- if (nlines > 1)
- qsort(lines, nlines, sizeof(*lines), metacmp);
-
- hm = mkmask(nlines);
- ht = solv_calloc(hm + 1, sizeof(*ht));
- for (i = 0, lp = lines; i < nlines; i++, lp++)
- {
- if (lp->killed)
- continue;
- s = lp->l;
- h = strnhash(s, 10);
- h = strhash_cont(s + lp->lastoff, h) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- struct metaline *lp2 = lines + (id - 1);
- if (!strncmp(lp->l, lp2->l, 32) && !strcmp(lp->l + lp->lastoff, lp2->l + lp2->lastoff))
- break;
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- if (id)
- lp->killed = 1;
- else
- ht[h] = i + 1;
- }
- solv_free(ht);
- j = 0;
- for (i = 0, lp = lines; i < nlines; i++, lp++)
- if (!lp->killed)
- j++;
- EXTEND(SP, j);
- for (i = 0, lp = lines; i < nlines; i++, lp++)
- {
- SV *sv;
- if (lp->killed)
- continue;
- sv = newSVpv(lp->l, 0);
- PUSHs(sv_2mortal(sv));
- }
- solv_free(lines);
- }
-
-SV *
-thawcache(SV *sv)
- CODE:
- unsigned char *src;
- STRLEN srcl;
- if (!SvPOK(sv)) {
- croak("thaw: argument is not a string\n");
- XSRETURN_UNDEF;
- }
- src = (unsigned char *)SvPV(sv, srcl);
- if (srcl < 7 || src[0] != 'p' || src[1] != 's' || src[2] != 't' || src[3] != '0') {
- croak("thaw: argument is not a perl storable\n");
- XSRETURN_UNDEF;
- }
- if ((src[4] & 1) != 1) {
- croak("thaw: argument is not a perl storable in network order\n");
- XSRETURN_UNDEF;
- }
- if (src[4] < 5) {
- croak("thaw: argument is a perl storable with a too old version\n");
- XSRETURN_UNDEF;
- }
- src += 6;
- srcl -= 6;
- sv = retrieve(&src, &srcl, 0);
- if (sv == 0 || srcl) {
- croak("thaw: corrupt storable\n");
- XSRETURN_UNDEF;
- }
- RETVAL = newRV_noinc(sv);
- OUTPUT:
- RETVAL
-
-int
-isobscpio(const char *file)
- CODE:
- int fd;
- RETVAL = 0;
- if ((fd = open(file, O_RDONLY)) != -1) {
- unsigned char magic[16];
- if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7))
- RETVAL = 1;
- close(fd);
- }
- OUTPUT:
- RETVAL
-
-
-void
-obscpiostat(const char *file)
- PPCODE:
- {
- int fd;
- struct stat st;
- if ((fd = open(file, O_RDONLY)) != -1) {
- if (!fstat(fd, &st)) {
- unsigned char magic[16];
- if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
- st.st_size = getu48(magic + 10);
- }
- EXTEND(SP, 10);
- PUSHs(&PL_sv_undef);
- PUSHs(&PL_sv_undef);
- PUSHs(sv_2mortal(newSVuv((UV)st.st_mode)));
- PUSHs(sv_2mortal(newSVuv((UV)st.st_nlink)));
- PUSHs(&PL_sv_undef);
- PUSHs(&PL_sv_undef);
- PUSHs(&PL_sv_undef);
-#if IVSIZE > 4
- PUSHs(sv_2mortal(newSVuv((UV)st.st_size)));
-#else
- PUSHs(sv_2mortal(newSVnv((double)st.st_size)));
-#endif
- PUSHs(sv_2mortal(newSVuv((UV)st.st_atime)));
- PUSHs(sv_2mortal(newSVuv((UV)st.st_mtime)));
- PUSHs(sv_2mortal(newSVuv((UV)st.st_ctime)));
- }
- close(fd);
- }
- }
-
-int
-obscpioopen(const char *file, const char *store, SV *gvrv, const char *tmpdir = 0)
- CODE:
- int fd;
- GV *gv;
- if (!SvROK(gvrv) || SvTYPE(SvRV(gvrv)) != SVt_PVGV) {
- croak("obscpioopen needs a GV reference\n");
- }
- if (tmpdir && strlen(tmpdir) > 200) {
- croak("tmpdir too long\n");
- }
- gv = (GV *)SvRV(gvrv);
- RETVAL = 0;
- if ((fd = open(file, O_RDONLY)) != -1) {
- unsigned char magic[16];
- if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
- char template[256];
- int nfd = -1;
- int sfd;
- if ((sfd = open(store, O_RDONLY)) != -1) {
- if (tmpdir) {
- strcpy(template, tmpdir);
- strcat(template, "/obscpioopen-XXXXXX");
- } else {
- strcpy(template, "/var/tmp/obscpioopen-XXXXXX");
- }
- nfd = mkstemp(template);
- if (nfd != -1) {
- FILE *fp = 0, *nfp = 0;
- unlink(template);
- lseek(fd, 0, SEEK_SET);
- if ((fp = fdopen(fd, "r")) == 0)
- close(fd);
- if ((nfp = fdopen(nfd, "w+")) == 0)
- close(nfd);
- if (fp && nfp && expandobscpio(fp, sfd, nfp)) {
- nfd = dup(nfd);
- if (fclose(nfp)) {
- close(nfd);
- nfd = -1;
- }
- nfp = 0;
- } else {
- nfd = -1;
- }
- if (fp)
- fclose(fp);
- if (nfp)
- fclose(nfp);
- fd = -1;
- }
- close(sfd);
- }
- if (fd != -1)
- close(fd);
- fd = nfd;
- }
- if (fd != -1) {
- IO * io = GvIOn(gv);
- PerlIO *fp;
-
- lseek(fd, 0, SEEK_SET);
- fp = PerlIO_fdopen(fd, "rb");
- if (fp) {
- IoIFP(io) = fp;
- RETVAL = 1;
- }
- }
- }
-
- OUTPUT:
- RETVAL
-
-int
-expandobscpio(const char *file, const char *store, const char *tmpfile)
- CODE:
- {
- int fd, nfd, sfd;
- RETVAL = 0;
-
- unlink(tmpfile);
- if ((fd = open(file, O_RDONLY)) != -1) {
- unsigned char magic[16];
- if (!(read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7))) {
- close(fd);
- fd = -1;
- if (link(file, tmpfile) == 0 && (fd = open(tmpfile, O_RDONLY)) != -1) {
- if (read(fd, magic, 16) == 16 && !memcmp(magic, "OBScpio", 7)) {
- unlink(tmpfile);
- } else {
- close(fd);
- fd = -1;
- RETVAL = 1;
- }
- }
- }
- if (fd != -1) {
- if ((sfd = open(store, O_RDONLY)) != -1) {
- FILE *fp;
- lseek(fd, 0, SEEK_SET);
- if ((fp = fdopen(fd, "r")) == 0)
- close(fd);
- if (fp && (nfd = open(tmpfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0666)) != -1) {
- FILE *nfp;
- if ((nfp = fdopen(nfd, "w")) == 0)
- close(nfd);
- if (nfp && expandobscpio(fp, sfd, nfp)) {
- if (!fclose(nfp))
- RETVAL = 1;
- else
- unlink(tmpfile);
- nfp = 0;
- } else
- unlink(tmpfile);
- if (nfp)
- fclose(nfp);
- }
- if (fp)
- fclose(fp);
- close(sfd);
- } else {
- close(fd);
- }
- }
- }
- }
- OUTPUT:
- RETVAL
-
-
-int
-makeobscpio(const char *in, const char *store, const char *out)
- CODE:
- {
- FILE *fpin, *fpout;
- struct stat st;
- int fdstore;
- RETVAL = 0;
- if ((fpin = fopen(in, "r")) == 0) {
- perror(in);
- } else if (fstat(fileno(fpin), &st) != 0) {
- perror(in);
- fclose(fpin);
- } else if ((fpout = fopen(out, "w")) == 0) {
- perror(out);
- fclose(fpin);
- } else if ((fdstore = open(store, O_RDWR|O_CREAT, 0666)) == -1) {
- perror(store);
- fclose(fpin);
- fclose(fpout);
- } else {
- int gotlock = 0;
- while (!gotlock) {
- if (flock(fdstore, LOCK_EX) == 0)
- gotlock = 1;
- else if (errno != EINTR)
- break;
- }
- if (gotlock) {
- struct deltastore store;
- if (readdeltastore(&store, fdstore, 0, (unsigned long long)st.st_size)) {
- int r = makedelta(&store, fpin, fpout, (unsigned long long)st.st_size);
-#if 0
- printf("after makedelta: have %d entries, hash size %d\n", store.hf, store.hm + 1);
-#endif
- if (fsync(store.fd))
- r = 0;
- freedeltastore(&store);
- if (r) {
- settimes(fileno(fpout), &st);
- RETVAL = 1;
- }
- }
- }
- close(fdstore);
- fclose(fpin);
- fclose(fpout);
- }
- }
- OUTPUT:
- RETVAL
-
-void
-obscpiostorestats(const char *store)
- CODE:
- {
- int fdstore;
-
- if ((fdstore = open(store, O_RDONLY)) == -1)
- perror(store);
- else {
- int gotlock = 0;
- while (!gotlock) {
- if (flock(fdstore, LOCK_EX) == 0)
- gotlock = 1;
- else if (errno != EINTR)
- break;
- }
- if (gotlock) {
- struct deltastore store;
- if (readdeltastore(&store, fdstore, 1, (unsigned long long)0)) {
- printdeltastorestats(&store);
- fsync(store.fd);
- freedeltastore(&store);
- }
- }
- close(fdstore);
- }
- }
-
-void
-obscpioinstr(const char *file, const char *store = 0)
- CODE:
- {
- FILE *fp;
- int fdstore = -1;
- if ((fp = fopen(file, "r")) == 0)
- perror(file);
- else {
- if (store) {
- fdstore = open(store, O_RDONLY);
- if (fdstore == -1)
- perror(store);
- }
- printobscpioinstr(fp, fdstore, fdstore == -1 ? 0 : 1);
- fclose(fp);
- if (fdstore != -1)
- close(fdstore);
- }
- }
-
-
-MODULE = BSSolv PACKAGE = BSSolv::pool PREFIX = pool
-
-PROTOTYPES: ENABLE
-
-BSSolv::pool
-new(char *packname = "BSSolv::pool")
- CODE:
- {
- Pool *pool = pool_create();
- set_disttype(pool, DISTTYPE_RPM);
- buildservice_id = pool_str2id(pool, "buildservice:id", 1);
- buildservice_repocookie= pool_str2id(pool, "buildservice:repocookie", 1);
- buildservice_external = pool_str2id(pool, "buildservice:external", 1);
- buildservice_dodurl = pool_str2id(pool, "buildservice:dodurl", 1);
- expander_directdepsend = pool_str2id(pool, "-directdepsend--", 1);
- buildservice_dodcookie = pool_str2id(pool, "buildservice:dodcookie", 1);
- pool_freeidhashes(pool);
- RETVAL = pool;
- }
- OUTPUT:
- RETVAL
-
-void
-settype(BSSolv::pool pool, char *type)
- CODE:
- if (!strcmp(type, "rpm"))
- set_disttype(pool, DISTTYPE_RPM);
-#ifdef DISTTYPE_DEB
- else if (!strcmp(type, "deb"))
- set_disttype(pool, DISTTYPE_DEB);
-#endif
-#ifdef DISTTYPE_ARCH
- else if (!strcmp(type, "arch"))
- set_disttype(pool, DISTTYPE_ARCH);
-#endif
- else
- croak("settype: unknown type '%s'\n", type);
-
-
-BSSolv::repo
-repofromfile(BSSolv::pool pool, char *name, char *filename)
- CODE:
- FILE *fp;
- fp = fopen(filename, "r");
- if (!fp) {
- croak("%s: %s\n", filename, Strerror(errno));
- XSRETURN_UNDEF;
- }
- RETVAL = repo_create(pool, name);
- repo_add_solv(RETVAL, fp, 0);
- fclose(fp);
- OUTPUT:
- RETVAL
-
-BSSolv::repo
-repofromstr(BSSolv::pool pool, char *name, SV *sv)
- CODE:
- FILE *fp;
- STRLEN len;
- char *buf;
- buf = SvPV(sv, len);
- if (!buf)
- croak("repofromstr: undef string\n");
- fp = fmemopen(buf, len, "r");
- if (!fp) {
- croak("fmemopen failed\n");
- XSRETURN_UNDEF;
- }
- RETVAL = repo_create(pool, name);
- repo_add_solv(RETVAL, fp, 0);
- fclose(fp);
- OUTPUT:
- RETVAL
-
-BSSolv::repo
-repofrombins(BSSolv::pool pool, char *name, char *dir, ...)
- CODE:
- {
- int i;
- Repo *repo;
- Repodata *data;
- repo = repo_create(pool, name);
- data = repo_add_repodata(repo, 0);
- for (i = 3; i + 1 < items; i += 2)
- {
- STRLEN sl;
- char *s = SvPV(ST(i), sl);
- char *sid = SvPV_nolen(ST(i + 1));
- if (sl < 4)
- continue;
- if (strcmp(s + sl - 4, ".rpm")
- && strcmp(s + sl - 4, ".deb")
-#ifdef ARCH_ADD_WITH_PKGID
- && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
- && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
-#endif
- )
- continue;
- if (sl >= 10 && !strcmp(s + sl - 10, ".patch.rpm"))
- continue;
- if (sl >= 10 && !strcmp(s + sl - 10, ".nosrc.rpm"))
- continue;
- if (sl >= 8 && !strcmp(s + sl - 8, ".src.rpm"))
- continue;
- repodata_addbin(data, dir, s, (int)sl, sid);
- }
- repo_set_str(repo, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
- repo_internalize(repo);
- RETVAL = repo;
- }
- OUTPUT:
- RETVAL
-
-BSSolv::repo
-repofromdata(BSSolv::pool pool, char *name, HV *rhv)
- CODE:
- {
- Repo *repo;
- Repodata *data;
- repo = repo_create(pool, name);
- data = repo_add_repodata(repo, 0);
- data2solvables(repo, data, rhv);
- if (name && !strcmp(name, "/external/"))
- repodata_set_void(data, SOLVID_META, buildservice_external);
- repo_internalize(repo);
- RETVAL = repo;
- }
- OUTPUT:
- RETVAL
-
-void
-createwhatprovides(BSSolv::pool pool)
- CODE:
- if (pool->considered)
- {
- map_free(pool->considered);
- solv_free(pool->considered);
- }
- pool->considered = solv_calloc(sizeof(Map), 1);
- create_considered(pool, 0, pool->considered);
- pool_createwhatprovides(pool);
-
-void
-setdebuglevel(BSSolv::pool pool, int level)
- CODE:
- pool_setdebuglevel(pool, level);
-
-void
-whatprovides(BSSolv::pool pool, char *str)
- PPCODE:
- {
- Id p, pp, id;
- id = dep2id(pool, str);
- if (id)
- FOR_PROVIDES(p, pp, id)
- XPUSHs(sv_2mortal(newSViv((IV)p)));
- }
-
-void
-whatrequires(BSSolv::pool pool, char *str)
- PPCODE:
- {
- Id p, id;
- Id *pp;
- Solvable *s;
- id = dep2id(pool, str);
- if (id)
- {
- for (p = 2; p < pool->nsolvables; p++)
- {
- if (!MAPTST(pool->considered, p))
- continue;
- s = pool->solvables + p;
- if (!s->requires)
- continue;
- for (pp = s->repo->idarraydata + s->requires; *pp; pp++)
- if (pool_match_dep(pool, id, *pp))
- break;
- if (*pp)
- XPUSHs(sv_2mortal(newSViv((IV)p)));
- }
- }
- }
-
-void
-consideredpackages(BSSolv::pool pool)
- PPCODE:
- {
- int p, nsolv = 0;
- for (p = 2; p < pool->nsolvables; p++)
- if (MAPTST(pool->considered, p))
- nsolv++;
- EXTEND(SP, nsolv);
- for (p = 2; p < pool->nsolvables; p++)
- if (MAPTST(pool->considered, p))
- PUSHs(sv_2mortal(newSViv((IV)p)));
- }
-
-void
-allpackages(BSSolv::pool pool)
- PPCODE:
- {
- int p, nsolv = 0;
- for (p = 2; p < pool->nsolvables; p++)
- if (pool->solvables[p].repo)
- nsolv++;
- EXTEND(SP, nsolv);
- for (p = 2; p < pool->nsolvables; p++)
- if (pool->solvables[p].repo)
- PUSHs(sv_2mortal(newSViv((IV)p)));
- }
-
-const char *
-pkg2name(BSSolv::pool pool, int p)
- CODE:
- RETVAL = pool_id2str(pool, pool->solvables[p].name);
- OUTPUT:
- RETVAL
-
-const char *
-pkg2srcname(BSSolv::pool pool, int p)
- CODE:
- if (solvable_lookup_void(pool->solvables + p, SOLVABLE_SOURCENAME))
- RETVAL = pool_id2str(pool, pool->solvables[p].name);
- else
- RETVAL = solvable_lookup_str(pool->solvables + p, SOLVABLE_SOURCENAME);
- OUTPUT:
- RETVAL
-
-const char *
-pkg2pkgid(BSSolv::pool pool, int p)
- CODE:
- {
- Id type;
- const char *s = solvable_lookup_checksum(pool->solvables + p, SOLVABLE_PKGID, &type);
- RETVAL = s;
- }
- OUTPUT:
- RETVAL
-
-const char *
-pkg2bsid(BSSolv::pool pool, int p)
- CODE:
- RETVAL = solvable_lookup_str(pool->solvables + p, buildservice_id);
- OUTPUT:
- RETVAL
-
-const char *
-pkg2reponame(BSSolv::pool pool, int p)
- CODE:
- {
- Repo *repo = pool->solvables[p].repo;
- RETVAL = repo ? repo->name : 0;
- }
- OUTPUT:
- RETVAL
-
-const char *
-pkg2path(BSSolv::pool pool, int p)
- CODE:
- {
- unsigned int medianr;
- RETVAL = solvable_get_location(pool->solvables + p, &medianr);
- }
- OUTPUT:
- RETVAL
-
-const char *
-pkg2fullpath(BSSolv::pool pool, int p, char *myarch)
- CODE:
- {
- unsigned int medianr;
- const char *s = solvable_get_location(pool->solvables + p, &medianr);
- Repo *repo = pool->solvables[p].repo;
- s = pool_tmpjoin(pool, myarch, "/:full/", s);
- RETVAL = pool_tmpjoin(pool, repo->name, "/", s);
- }
- OUTPUT:
- RETVAL
-
-int
-pkg2sizek(BSSolv::pool pool, int p)
- CODE:
-#ifdef SOLV_KV_NUM64
- RETVAL = solvable_lookup_sizek(pool->solvables + p, SOLVABLE_DOWNLOADSIZE, 0);
-#else
- RETVAL = solvable_lookup_num(pool->solvables + p, SOLVABLE_DOWNLOADSIZE, 0);
-#endif
- OUTPUT:
- RETVAL
-
-const char *
-pkg2checksum(BSSolv::pool pool, int p)
- CODE:
- {
- Id type;
- const char *s = solvable_lookup_checksum(pool->solvables + p, SOLVABLE_CHECKSUM, &type);
- if (s)
- s = pool_tmpjoin(pool, solv_chksum_type2str(type), ":", s);
- RETVAL = s;
- }
- OUTPUT:
- RETVAL
-
-int
-verifypkgchecksum(BSSolv::pool pool, int p, char *path)
- CODE:
- {
- Id type;
- const unsigned char *cin, *cout;
- FILE *fp;
- void *cs;
- int cslen;
- char buf[4096];
- size_t len;
- int res = 0;
-
- if ((cin = solvable_lookup_bin_checksum(pool->solvables + p, SOLVABLE_CHECKSUM, &type)) != 0) {
- if ((fp = fopen(path, "r")) != 0) {
- if ((cs = solv_chksum_create(type)) != 0) {
- while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
- solv_chksum_add(cs, buf, len);
- if ((cout = solv_chksum_get(cs, &cslen)) != 0 && cslen && !memcmp(cin, cout, cslen))
- res = 1;
- solv_chksum_free(cs, 0);
- }
- fclose(fp);
- }
- }
- RETVAL = res;
- }
- OUTPUT:
- RETVAL
-
-HV *
-pkg2data(BSSolv::pool pool, int p)
- CODE:
- {
- Solvable *s = pool->solvables + p;
- Id id;
- const char *ss, *se;
- unsigned int medianr;
-
- if (!s->repo)
- XSRETURN_EMPTY;
- RETVAL = newHV();
- sv_2mortal((SV*)RETVAL);
- (void)hv_store(RETVAL, "name", 4, newSVpv(pool_id2str(pool, s->name), 0), 0);
- ss = pool_id2str(pool, s->evr);
- se = ss;
- while (*se >= '0' && *se <= '9')
- se++;
- if (se != ss && *se == ':' && se[1])
- {
- (void)hv_store(RETVAL, "epoch", 5, newSVpvn(ss, se - ss), 0);
- ss = se + 1;
- }
- se = strrchr(ss, '-');
- if (se)
- {
- (void)hv_store(RETVAL, "version", 7, newSVpvn(ss, se - ss), 0);
- (void)hv_store(RETVAL, "release", 7, newSVpv(se + 1, 0), 0);
- }
- else
- (void)hv_store(RETVAL, "version", 7, newSVpv(ss, 0), 0);
- (void)hv_store(RETVAL, "arch", 4, newSVpv(pool_id2str(pool, s->arch), 0), 0);
- exportdeps(RETVAL, "provides", 8, s->repo, s->provides, SOLVABLE_PROVIDES);
- exportdeps(RETVAL, "obsoletes", 9, s->repo, s->obsoletes, SOLVABLE_OBSOLETES);
- exportdeps(RETVAL, "conflicts", 9, s->repo, s->conflicts, SOLVABLE_CONFLICTS);
- exportdeps(RETVAL, "requires", 8, s->repo, s->requires, SOLVABLE_REQUIRES);
- exportdeps(RETVAL, "recommends", 10, s->repo, s->recommends, SOLVABLE_RECOMMENDS);
- exportdeps(RETVAL, "suggests", 8, s->repo, s->suggests, SOLVABLE_SUGGESTS);
- exportdeps(RETVAL, "supplements", 11, s->repo, s->supplements, SOLVABLE_SUPPLEMENTS);
- exportdeps(RETVAL, "enhances", 8, s->repo, s->enhances, SOLVABLE_ENHANCES);
- if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
- ss = pool_id2str(pool, s->name);
- else
- ss = solvable_lookup_str(s, SOLVABLE_SOURCENAME);
- if (ss)
- (void)hv_store(RETVAL, "source", 6, newSVpv(ss, 0), 0);
- ss = solvable_get_location(s, &medianr);
- if (ss)
- (void)hv_store(RETVAL, "path", 4, newSVpv(ss, 0), 0);
- ss = solvable_lookup_checksum(s, SOLVABLE_PKGID, &id);
- if (ss && id == REPOKEY_TYPE_MD5)
- (void)hv_store(RETVAL, "hdrmd5", 6, newSVpv(ss, 0), 0);
- ss = solvable_lookup_str(s, buildservice_id);
- if (ss)
- (void)hv_store(RETVAL, "id", 2, newSVpv(ss, 0), 0);
- }
- OUTPUT:
- RETVAL
-
-void
-repos(BSSolv::pool pool)
- PPCODE:
- {
- int ridx;
- Repo *repo;
-
- EXTEND(SP, pool->nrepos);
- FOR_REPOS(ridx, repo)
- {
- SV *sv = sv_newmortal();
- sv_setref_pv(sv, "BSSolv::repo", (void *)repo);
- PUSHs(sv);
- }
- }
-
-void
-preparehashes(BSSolv::pool pool, char *prp, SV *gctxprpnotreadysv = 0)
- PPCODE:
- {
- HV *gctxprpnotready = 0;
- int ridx;
- Repo *repo;
- /* generated: */
- HV *depislocal = newHV();
- HV *dep2pkg = newHV();
- HV *dep2src = newHV();
- HV *notready = newHV();
- HV *subpacks = newHV();
- const char *srcstr;
- const char *str;
- Queue subq;
- Id lastsrc, srcname, srctype;
- int i, j;
- Id p;
- Solvable *s;
- SV *sv, **svp;
-
- if (gctxprpnotreadysv && SvROK(gctxprpnotreadysv) && SvTYPE(SvRV(gctxprpnotreadysv)) == SVt_PVHV)
- gctxprpnotready = (HV *)SvRV(gctxprpnotreadysv);
- queue_init(&subq);
- FOR_REPOS(ridx, repo)
- {
- HV *prpnotready = 0;
- int islocal = repo->name && !strcmp(repo->name, prp);
- svp = 0;
- if (repo->name && !islocal && gctxprpnotready)
- svp = hv_fetch(gctxprpnotready, repo->name, strlen(repo->name), 0);
- if (svp && *svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV)
- prpnotready = (HV *)SvRV(*svp);
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- if (!MAPTST(pool->considered, p))
- continue;
- srctype = solvable_lookup_type(pool->solvables + p, SOLVABLE_SOURCENAME);
- if (srctype == REPOKEY_TYPE_VOID)
- srcname = s->name;
- else if (srctype == REPOKEY_TYPE_ID)
- srcname = solvable_lookup_id(pool->solvables + p, SOLVABLE_SOURCENAME);
- else
- {
- srcstr = solvable_lookup_str(pool->solvables + p, SOLVABLE_SOURCENAME);
- srcname = srcstr ? pool_str2id(pool, srcstr, 1) : 0;
- }
- if (!srcname || srcname == 1)
- srcname = s->name;
- queue_push2(&subq, s->name, srcname);
-
- str = pool_id2str(pool, s->name);
- (void)hv_store(dep2pkg, str, strlen(str), newSViv((IV)p), 0);
- if (islocal)
- (void)hv_store(depislocal, str, strlen(str), newSViv((IV)1), 0);
- srcstr = pool_id2str(pool, srcname);
- (void)hv_store(dep2src, str, strlen(str), newSVpv(srcstr, 0), 0);
- if (!islocal && prpnotready)
- {
- svp = hv_fetch(prpnotready, srcstr, strlen(srcstr), 0);
- if (svp && *svp && SvTRUE(*svp))
- (void)hv_store(notready, srcstr, strlen((char *)srcstr), newSViv((IV)2), 0);
- }
- }
- }
- solv_sort(subq.elements, subq.count / 2, sizeof(Id) * 2, subpack_sort_cmp, pool);
- queue_push2(&subq, 0, 0);
- lastsrc = 0;
- for (i = j = 0; i < subq.count; i += 2)
- {
- if (subq.elements[i + 1] != lastsrc)
- {
- if (j < i)
- {
- AV *subs = newAV();
- for (; j < i; j += 2)
- {
- str = pool_id2str(pool, subq.elements[j]);
- av_push(subs, newSVpv(str, 0));
- }
- str = pool_id2str(pool, lastsrc);
- (void)hv_store(subpacks, str, strlen(str), newRV_noinc((SV *)subs), 0);
- }
- lastsrc = subq.elements[i + 1];
- }
- }
- queue_free(&subq);
- EXTEND(SP, 5);
- sv = newRV_noinc((SV *)dep2pkg);
- PUSHs(sv_2mortal(sv));
- sv = newRV_noinc((SV *)dep2src);
- PUSHs(sv_2mortal(sv));
- sv = newRV_noinc((SV *)depislocal);
- PUSHs(sv_2mortal(sv));
- sv = newRV_noinc((SV *)notready);
- PUSHs(sv_2mortal(sv));
- sv = newRV_noinc((SV *)subpacks);
- PUSHs(sv_2mortal(sv));
- }
-
-void
-DESTROY(BSSolv::pool pool)
- CODE:
- if (pool->considered)
- {
- map_free(pool->considered);
- pool->considered = solv_free(pool->considered);
- }
- pool_free(pool);
-
-
-
-
-MODULE = BSSolv PACKAGE = BSSolv::repo PREFIX = repo
-
-void
-pkgnames(BSSolv::repo repo)
- PPCODE:
- {
- Pool *pool = repo->pool;
- Id p;
- Solvable *s;
- Map c;
-
- create_considered(pool, repo, &c);
- EXTEND(SP, 2 * repo->nsolvables);
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- if (!MAPTST(&c, p))
- continue;
- PUSHs(sv_2mortal(newSVpv(pool_id2str(pool, s->name), 0)));
- PUSHs(sv_2mortal(newSViv(p)));
- }
- map_free(&c);
- }
-
-void
-pkgpaths(BSSolv::repo repo)
- PPCODE:
- {
- Pool *pool = repo->pool;
- Id p;
- Solvable *s;
- Map c;
- const char *str;
- unsigned int medianr;
-
- create_considered(pool, repo, &c);
- EXTEND(SP, 2 * repo->nsolvables);
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- if (!MAPTST(&c, p))
- continue;
- /* ignore dod packages */
- str = solvable_lookup_str(s, buildservice_id);
- if (str && !strcmp(str, "dod"))
- continue;
- str = solvable_get_location(pool->solvables + p, &medianr);
- if (!str)
- continue;
- PUSHs(sv_2mortal(newSVpv(str, 0)));
- PUSHs(sv_2mortal(newSViv(p)));
- }
- map_free(&c);
- }
-
-void
-tofile(BSSolv::repo repo, char *filename)
- CODE:
- {
- FILE *fp;
- fp = fopen(filename, "w");
- if (fp == 0)
- croak("%s: %s\n", filename, Strerror(errno));
- repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
- if (fclose(fp))
- croak("fclose: %s\n", Strerror(errno));
- }
-
-void
-tofile_fd(BSSolv::repo repo, int fd)
- CODE:
- {
- FILE *fp;
- int fd2;
- fd2 = dup(fd);
- if (fd2 == -1)
- croak("dup: %s\n", Strerror(errno));
- fp = fdopen(fd2, "w");
- if (fp == 0)
- {
- int e = errno;
- close(fd2);
- croak("fdopen: %s\n", Strerror(e));
- }
- repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
- if (fclose(fp))
- {
- int e = errno;
- close(fd2);
- croak("fclose: %s\n", Strerror(e));
- }
- }
-
-SV *
-tostr(BSSolv::repo repo)
- CODE:
- {
- FILE *fp;
- char *buf;
- size_t len;
- fp = open_memstream(&buf, &len);
- if (fp == 0)
- croak("open_memstream: %s\n", Strerror(errno));
- repo_write_filtered(repo, fp, myrepowritefilter, 0, 0);
- if (fclose(fp))
- croak("fclose: %s\n", Strerror(errno));
- RETVAL = newSVpvn(buf, len);
- free(buf);
- }
- OUTPUT:
- RETVAL
-
-int
-updatefrombins(BSSolv::repo repo, char *dir, ...)
- CODE:
- {
- Pool *pool = repo->pool;
- int i;
- Repodata *data = 0;
- Hashtable ht;
- Hashval h, hh, hm;
- int dirty = 0;
- Map reused;
- int oldend = 0;
- Id p, id;
- Solvable *s;
- STRLEN sl;
- const char *oldcookie;
-
- map_init(&reused, repo->end - repo->start);
- if (repo_lookup_str(repo, SOLVID_META, buildservice_dodurl))
- {
- /* this is a dod repo. keep all dod packages. */
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- const char *str = solvable_lookup_str(s, buildservice_id);
- if (str && !strcmp(str, "dod"))
- MAPSET(&reused, p - repo->start);
- }
- }
- hm = mkmask(2 * repo->nsolvables + 1);
- ht = solv_calloc(hm + 1, sizeof(*ht));
- oldcookie = repo_lookup_str(repo, SOLVID_META, buildservice_repocookie);
- if (oldcookie && !strcmp(oldcookie, REPOCOOKIE))
- {
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- const char *str = solvable_lookup_str(s, buildservice_id);
- if (!str || !strcmp(str, "dod"))
- continue;
- h = strhash(str) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- h = HASHCHAIN_NEXT(h, hh, hm);
- ht[h] = p;
- }
- }
- if (repo->end != repo->start)
- oldend = repo->end;
-
- for (i = 2; i + 1 < items; i += 2)
- {
- char *s = SvPV(ST(i), sl);
- char *sid = SvPV_nolen(ST(i + 1));
- if (sl < 4)
- continue;
- if (strcmp(s + sl - 4, ".rpm")
- && strcmp(s + sl - 4, ".deb")
-#ifdef ARCH_ADD_WITH_PKGID
- && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
- && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
-#endif
- )
- if (sl > 10 && !strcmp(s + sl - 10, ".patch.rpm"))
- continue;
- if (sl > 10 && !strcmp(s + sl - 10, ".nosrc.rpm"))
- continue;
- if (sl > 8 && !strcmp(s + sl - 8, ".src.rpm"))
- continue;
- h = strhash(sid) & hm;
- hh = HASHCHAIN_START;
- while ((id = ht[h]) != 0)
- {
- const char *str = solvable_lookup_str(pool->solvables + id, buildservice_id);
- if (!strcmp(str, sid))
- {
- /* check location */
- unsigned int medianr;
- str = solvable_get_location(pool->solvables + id, &medianr);
- if (str[0] == '.' && str[1] == '/')
- str += 2;
- if (!strcmp(str, s))
- break;
- }
- h = HASHCHAIN_NEXT(h, hh, hm);
- }
- if (id)
- {
- /* same id and location, reuse old entry */
- MAPSET(&reused, id - repo->start);
- }
- else
- {
- /* add new entry */
- dirty++;
- if (!data)
- data = repo_add_repodata(repo, 0);
- repodata_addbin(data, dir, s, (int)sl, sid);
- }
- }
- solv_free(ht);
- if (oldcookie)
- {
- if (strcmp(oldcookie, REPOCOOKIE) != 0)
- {
- Repodata *firstrepodata = repo_id2repodata(repo, 1);
- if (data && data != firstrepodata)
- repodata_internalize(data);
- data = firstrepodata;
- repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
- }
- }
- else
- {
- if (!data)
- data = repo_add_repodata(repo, 0);
- repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
- }
- if (data)
- repodata_internalize(data);
- if (oldend)
- {
- for (i = repo->start; i < oldend; i++)
- {
- if (pool->solvables[i].repo != repo)
- continue;
- if (MAPTST(&reused, i - repo->start))
- continue;
- if (dirty <= 0)
- dirty--;
- repo_free_solvable_block(repo, i, 1, 0);
- }
- }
- map_free(&reused);
- RETVAL = dirty;
- }
- OUTPUT:
- RETVAL
-
-
-void
-getpathid(BSSolv::repo repo)
- PPCODE:
- {
- Id p;
- Solvable *s;
- EXTEND(SP, repo->nsolvables * 2);
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- unsigned int medianr;
- const char *str;
- str = solvable_get_location(s, &medianr);
- PUSHs(sv_2mortal(newSVpv(str, 0)));
- str = solvable_lookup_str(s, buildservice_id);
- PUSHs(sv_2mortal(newSVpv(str, 0)));
- }
- }
-
-const char *
-name(BSSolv::repo repo)
- CODE:
- RETVAL = repo->name;
- OUTPUT:
- RETVAL
-
-int
-isexternal(BSSolv::repo repo)
- CODE:
- RETVAL = repo_lookup_void(repo, SOLVID_META, buildservice_external) ? 1 : 0;
- OUTPUT:
- RETVAL
-
-const char *
-dodurl(BSSolv::repo repo)
- CODE:
- RETVAL = repo_lookup_str(repo, SOLVID_META, buildservice_dodurl);
- OUTPUT:
- RETVAL
-
-const char *
-dodcookie(BSSolv::repo repo)
- CODE:
- RETVAL = repo_lookup_str(repo, SOLVID_META, buildservice_dodcookie);
- OUTPUT:
- RETVAL
-
-void
-updatedoddata(BSSolv::repo repo, HV *rhv = 0)
- CODE:
- {
- Id p;
- Solvable *s;
- Repodata *data;
- /* delete old dod data */
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- const char *str = solvable_lookup_str(s, buildservice_id);
- if (!str || !strcmp(str, "dod"))
- repo_free_solvable(repo, p, 1);
- }
- data = repo_add_repodata(repo, REPO_REUSE_REPODATA);
- repodata_unset(data, SOLVID_META, buildservice_dodurl);
- repodata_unset(data, SOLVID_META, buildservice_dodcookie);
- /* add new data */
- if (rhv)
- data2solvables(repo, data, rhv);
- repo_internalize(repo);
- }
-
-
-MODULE = BSSolv PACKAGE = BSSolv::expander PREFIX = expander
-
-
-BSSolv::expander
-new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
- CODE:
- {
- SV *sv, **svp;
- char *str;
- int i, neg;
- Id id, id2;
- Expander *xp;
-
- xp = calloc(sizeof(Expander), 1);
- xp->pool = pool;
- svp = hv_fetch(config, "prefer", 6, 0);
- sv = svp ? *svp : 0;
- if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
- {
- AV *av = (AV *)SvRV(sv);
- for (i = 0; i <= av_len(av); i++)
- {
- svp = av_fetch(av, i, 0);
- if (!svp)
- continue;
- sv = *svp;
- str = SvPV_nolen(sv);
- if (!str)
- continue;
- neg = 0;
- if (*str == '-')
- {
- neg = 1;
- str++;
- }
- id = pool_str2id(pool, str, 1);
- id2 = 0;
- if ((str = strchr(str, ':')) != 0)
- id2 = pool_str2id(pool, str + 1, 1);
- if (neg)
- {
- MAPEXP(&xp->preferneg, id);
- MAPSET(&xp->preferneg, id);
- if (id2)
- {
- MAPEXP(&xp->prefernegx, id2);
- MAPSET(&xp->prefernegx, id2);
- }
- }
- else
- {
- queue_push(&xp->preferposq, id);
- MAPEXP(&xp->preferpos, id);
- MAPSET(&xp->preferpos, id);
- if (id2)
- {
- MAPEXP(&xp->preferposx, id2);
- MAPSET(&xp->preferposx, id2);
- }
- }
- }
- }
- svp = hv_fetch(config, "ignoreh", 7, 0);
- sv = svp ? *svp : 0;
- if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)
- {
- HV *hv = (HV *)SvRV(sv);
- HE *he;
-
- hv_iterinit(hv);
- while ((he = hv_iternext(hv)) != 0)
- {
- I32 strl;
- str = hv_iterkey(he, &strl);
- if (!str)
- continue;
-
- id = pool_str2id(pool, str, 1);
- id2 = 0;
- if ((str = strchr(str, ':')) != 0)
- id2 = pool_str2id(pool, str + 1, 1);
- MAPEXP(&xp->ignored, id);
- MAPSET(&xp->ignored, id);
- if (id2)
- {
- MAPEXP(&xp->ignoredx, id2);
- MAPSET(&xp->ignoredx, id2);
- }
- }
- }
- svp = hv_fetch(config, "conflict", 8, 0);
- sv = svp ? *svp : 0;
- if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
- {
- AV *av = (AV *)SvRV(sv);
- for (i = 0; i <= av_len(av); i++)
- {
- char *p;
- Id id2;
-
- svp = av_fetch(av, i, 0);
- if (!svp)
- continue;
- sv = *svp;
- str = SvPV_nolen(sv);
- if (!str)
- continue;
- p = strchr(str, ':');
- if (!p)
- continue;
- id = pool_strn2id(pool, str, p - str, 1);
- str = p + 1;
- while ((p = strchr(str, ',')) != 0)
- {
- id2 = pool_strn2id(pool, str, p - str, 1);
- queue_push2(&xp->conflictsq, id, id2);
- MAPEXP(&xp->conflicts, id);
- MAPSET(&xp->conflicts, id);
- MAPEXP(&xp->conflicts, id2);
- MAPSET(&xp->conflicts, id2);
- str = p + 1;
- }
- id2 = pool_str2id(pool, str, 1);
- queue_push2(&xp->conflictsq, id, id2);
- MAPEXP(&xp->conflicts, id);
- MAPSET(&xp->conflicts, id);
- MAPEXP(&xp->conflicts, id2);
- MAPSET(&xp->conflicts, id2);
- }
- }
- /* XXX: this modifies the pool, which is a bit unclean! */
- svp = hv_fetch(config, "fileprovides", 12, 0);
- sv = svp ? *svp : 0;
- if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)
- {
- HV *hv = (HV *)SvRV(sv);
- I32 strl;
- Queue q;
-
- xp->havefileprovides = 1;
- hv_iterinit(hv);
- queue_init(&q);
- while ((sv = hv_iternextsv(hv, &str, &strl)) != 0)
- {
- AV *av;
- Id p, pp;
- int havenew = 0;
-
- if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV)
- continue;
- id = pool_str2id(pool, str, 1);
- queue_empty(&q);
- FOR_PROVIDES(p, pp, id)
- queue_push(&q, p);
- av = (AV *)SvRV(sv);
- for (i = 0; i <= av_len(av); i++)
- {
- svp = av_fetch(av, i, 0);
- if (!svp)
- continue;
- sv = *svp;
- str = SvPV_nolen(sv);
- if (!str)
- continue;
- id2 = pool_str2id(pool, str, 0);
- FOR_PROVIDES(p, pp, id2)
- {
- int j;
- for (j = 0; j < q.count; j++)
- {
- if (q.elements[j] == p)
- break;
- if (q.elements[j] > p)
- {
- queue_insert(&q, j, p);
- havenew = 1;
- break;
- }
- }
- if (j == q.count)
- {
- queue_push(&q, p);
- havenew = 1;
- }
- }
- }
- if (havenew)
- pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
- }
- queue_free(&q);
- }
- svp = hv_fetch(config, "expandflags:ignoreconflicts", 27, 0);
- sv = svp ? *svp : 0;
- if (sv && SvTRUE(sv))
- xp->ignoreconflicts = 1;
- svp = hv_fetch(config, "expand_dbg", 10, 0);
- sv = svp ? *svp : 0;
- if (sv && SvTRUE(sv))
- xp->debug = 1;
- sv = get_sv("Build::expand_dbg", FALSE);
- if (sv && SvTRUE(sv))
- xp->debug = 1;
- RETVAL = xp;
- }
- OUTPUT:
- RETVAL
-
-
-void
-expand(BSSolv::expander xp, ...)
- PPCODE:
- {
- Pool *pool;
- int i, nerrors;
- Id id, who, conflbuf[16];
- Queue revertignore, in, out, confl;
- int oldignoreignore = xp->ignoreignore;
- int ignoreignore = 0;
- Map oldignored, oldignoredx;
- int ignoremapssaved = 0;
-
- queue_init(&revertignore);
- queue_init(&in);
- queue_init(&out);
- queue_init_buffer(&confl, conflbuf, sizeof(conflbuf)/sizeof(*conflbuf));
- pool = xp->pool;
- if (xp->debug)
- expander_dbg(xp, "expand args:");
- for (i = 1; i < items; i++)
- {
- char *s = SvPV_nolen(ST(i));
- if (xp->debug)
- expander_dbg(xp, " %s", s);
- if (*s == '-')
- {
- Id id;
- if (s[1] == '-' && !strcmp(s, "--ignoreignore--"))
- {
- ignoreignore = 1;
- continue;
- }
- id = pool_str2id(pool, s + 1, 1);
- if (id == expander_directdepsend)
- {
- queue_push(&in, id);
- continue;
- }
- queue_push(&revertignore, id);
- }
- else if (*s == '!')
- {
- Id id = dep2id(pool, s + (s[1] == '!' ? 2 : 1));
- queue_push2(&confl, id, s[1] == '!' ? 1 : 0);
- }
- else
- {
- Id id = dep2id(pool, s);
- queue_push(&in, id);
- }
- }
- if (xp->debug)
- expander_dbg(xp, "\n");
-
- if (ignoreignore && revertignore.count)
- {
- /* bad: have direct ignores and project config ignores */
- oldignored = xp->ignored;
- oldignoredx = xp->ignoredx;
- ignoremapssaved = 1;
- /* clear project config maps */
- memset(&xp->ignored, 0, sizeof(xp->ignored));
- memset(&xp->ignoredx, 0, sizeof(xp->ignoredx));
- }
-
- if (revertignore.count)
- {
- /* mix direct ignores with ignores from project config */
- int revertcnt = revertignore.count;
- for (i = 0; i < revertcnt; i++)
- {
- const char *ss;
- id = revertignore.elements[i];
- MAPEXP(&xp->ignored, id);
- if (MAPTST(&xp->ignored, id))
- continue;
- MAPSET(&xp->ignored, id);
- queue_push(&revertignore, id);
- if ((ss = strchr(pool_id2str(pool, id), ':')) != 0)
- {
- id = pool_str2id(pool, ss + 1, 1);
- MAPEXP(&xp->ignoredx, id);
- if (MAPTST(&xp->ignoredx, id))
- continue;
- MAPSET(&xp->ignoredx, id);
- queue_push(&revertignore, -id);
- }
- }
- queue_deleten(&revertignore, 0, revertcnt);
- }
- else if (ignoreignore)
- {
- /* no direct ignores, disable ignore processing */
- xp->ignoreignore = 1;
- }
-
- MAPEXP(&xp->ignored, pool->ss.nstrings);
- MAPEXP(&xp->ignoredx, pool->ss.nstrings);
- MAPEXP(&xp->preferpos, pool->ss.nstrings);
- MAPEXP(&xp->preferposx, pool->ss.nstrings);
- MAPEXP(&xp->preferneg, pool->ss.nstrings);
- MAPEXP(&xp->prefernegx, pool->ss.nstrings);
- MAPEXP(&xp->conflicts, pool->ss.nstrings);
-
- nerrors = expander_expand(xp, &in, &out, &confl);
-
- /* revert ignores */
- xp->ignoreignore = oldignoreignore;
- if (ignoremapssaved)
- {
- map_free(&xp->ignored);
- map_free(&xp->ignoredx);
- xp->ignored = oldignored;
- xp->ignoredx = oldignoredx;
- }
- else
- {
- for (i = 0; i < revertignore.count; i++)
- {
- id = revertignore.elements[i];
- if (id > 0)
- MAPCLR(&xp->ignored, id);
- else
- MAPCLR(&xp->ignoredx, -id);
- }
- }
- queue_free(&revertignore);
-
- queue_free(&in);
- queue_free(&confl);
-
- if (nerrors)
- {
- EXTEND(SP, nerrors + 1);
- PUSHs(sv_2mortal(newSV(0)));
- for (i = 0; i < out.count; )
- {
- SV *sv;
- Id type = out.elements[i];
- if (type == ERROR_NOPROVIDER)
- {
- id = out.elements[i + 1];
- who = out.elements[i + 2];
- if (who)
- sv = newSVpvf("nothing provides %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
- else
- sv = newSVpvf("nothing provides %s", pool_dep2str(pool, id));
- i += 3;
- }
- else if (type == ERROR_CONFLICTINGPROVIDER)
- {
- id = out.elements[i + 1];
- who = out.elements[i + 2];
- if (who)
- sv = newSVpvf("conflict for provider of %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
- else
- sv = newSVpvf("conflict for provider of %s", pool_dep2str(pool, id));
- i += 3;
- }
- else if (type == ERROR_CONFLICTINGPROVIDERS)
- {
- id = out.elements[i + 1];
- who = out.elements[i + 2];
- if (who)
- sv = newSVpvf("conflict for all providers of %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
- else
- sv = newSVpvf("conflict for all providers of %s", pool_dep2str(pool, id));
- i += 3;
- }
- else if (type == ERROR_PROVIDERINFO)
- {
- Id who2 = out.elements[i + 2];
- who = out.elements[i + 1];
- if (who2 < 0)
- sv = newSVpvf("(provider %s obsoletes installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[-who2].name));
- else
- sv = newSVpvf("(provider %s conflicts with installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[who2].name));
- i += 3;
- }
- else if (type == ERROR_PROVIDERINFO2)
- {
- Id who2 = out.elements[i + 2];
- who = out.elements[i + 1];
- if (who2 < 0)
- sv = newSVpvf("(provider %s is obsoleted by installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[-who2].name));
- else if (who2 > 0)
- sv = newSVpvf("(provider %s is conflicted by installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[who2].name));
- else
- sv = newSVpvf("(provider %s is conflicted by the build config)", pool_id2str(pool, pool->solvables[who].name));
- i += 3;
- }
- else if (type == ERROR_CHOICE)
- {
- int j;
- char *str = "";
- for (j = i + 3; out.elements[j]; j++)
- {
- Solvable *s = pool->solvables + out.elements[j];
- str = pool_tmpjoin(pool, str, " ", pool_id2str(pool, s->name));
- }
- if (*str)
- str++; /* skip starting ' ' */
- id = out.elements[i + 1];
- who = out.elements[i + 2];
- if (who)
- sv = newSVpvf("have choice for %s needed by %s: %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name), str);
- else
- sv = newSVpvf("have choice for %s: %s", pool_dep2str(pool, id), str);
- i = j + 1;
- }
- else
- croak("expander: bad error type\n");
- PUSHs(sv_2mortal(sv));
- }
- }
- else
- {
- EXTEND(SP, out.count + 1);
- PUSHs(sv_2mortal(newSViv((IV)1)));
- for (i = 0; i < out.count; i++)
- {
- Solvable *s = pool->solvables + out.elements[i];
- PUSHs(sv_2mortal(newSVpv(pool_id2str(pool, s->name), 0)));
- }
- }
- queue_free(&out);
- }
-
-const char *
-debugstr(BSSolv::expander xp)
- CODE:
- if (!xp->debugstr)
- xp->debugstr = calloc(1, 1);
- RETVAL = xp->debugstr;
- OUTPUT:
- RETVAL
-
-
-void
-DESTROY(BSSolv::expander xp)
- CODE:
- map_free(&xp->ignored);
- map_free(&xp->ignoredx);
- queue_free(&xp->preferposq);
- map_free(&xp->preferpos);
- map_free(&xp->preferposx);
- map_free(&xp->preferneg);
- map_free(&xp->prefernegx);
- queue_free(&xp->conflictsq);
- map_free(&xp->conflicts);
- solv_free(xp->debugstr);
- solv_free(xp);
+++ /dev/null
-use ExtUtils::MakeMaker;
-
-my $solvprefix = '/usr';
-
-my $inc = "-I$solvprefix/include/solv";
-my $lib = '';
-
-if (grep {$_ eq '--bundled-libsolv'} @ARGV) {
- my $builddir = 'libsolv';
- $inc = "-I$builddir/src -I$builddir/ext";
- $lib = "-L$builddir/src -L$builddir/ext";
-}
-
-$lib = ($lib ? "$lib " : '') . '-lsolvext -lsolv -lz -llzma';
-
-WriteMakefile(
- NAME => 'BSSolv',
- VERSION_FROM => 'BSSolv.pm',
- INC => $inc,
- LIBS => [ $lib ],
-)
Name: perl-BSSolv
Version: 0.28.0
-Release: 2.1
+Release: 1.1
Url: https://github.com/openSUSE/perl-BSSolv
-Source: libsolv-0.6.15.tar.gz
-Source1: Makefile.PL
-Source2: BSSolv.pm
-Source3: BSSolv.xs
-Source4: typemap
+Source: %{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-build
%if 0%{?mandriva_version}
%prep
%setup -c
ln -s libsolv-* libsolv
-cp %{SOURCE1} %{SOURCE2} %{SOURCE3} %{SOURCE4} .
pushd libsolv
popd
+++ /dev/null
-BSSolv::pool T_PTROBJ
-BSSolv::repo T_PTROBJ
-BSSolv::expander T_PTROBJ
+++ /dev/null
-
-INCLUDE (CheckFunctionExists)
-CHECK_FUNCTION_EXISTS (qsort_r HAVE_QSORT_R)
-CHECK_FUNCTION_EXISTS (__qsort_r HAVE___QSORT_R)
-
-IF (HAVE_QSORT_R)
- ADD_DEFINITIONS (-DHAVE_QSORT_R=1)
-ENDIF (HAVE_QSORT_R)
-
-IF (HAVE___QSORT_R)
- ADD_DEFINITIONS (-DHAVE___QSORT_R=1)
-ENDIF (HAVE___QSORT_R)
-
-ADD_DEFINITIONS (-DLIBSOLV_INTERNAL=1)
-
-SET (libsolv_SRCS
- bitmap.c poolarch.c poolvendor.c poolid.c strpool.c dirpool.c
- solver.c solverdebug.c repo_solv.c repo_write.c evr.c pool.c
- queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c
- transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c
- chksum.c md5.c sha1.c sha2.c solvversion.c selection.c)
-
-SET (libsolv_HEADERS
- bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h
- poolid.h pooltypes.h queue.h solvable.h solver.h solverdebug.h
- repo.h repodata.h repo_solv.h repo_write.h util.h selection.h
- strpool.h dirpool.h knownid.h transaction.h rules.h problems.h
- chksum.h dataiterator.h ${CMAKE_BINARY_DIR}/src/solvversion.h)
-
-SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
-IF (HAVE_LINKER_VERSION_SCRIPT)
-SET (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_FLAGS} -Wl,--version-script=${CMAKE_SOURCE_DIR}/src/libsolv.ver")
-ENDIF (HAVE_LINKER_VERSION_SCRIPT)
-
-IF (DISABLE_SHARED)
-ADD_LIBRARY (libsolv STATIC ${libsolv_SRCS})
-ELSE (DISABLE_SHARED)
-ADD_LIBRARY (libsolv SHARED ${libsolv_SRCS})
-ENDIF (DISABLE_SHARED)
-
-SET_TARGET_PROPERTIES(libsolv PROPERTIES OUTPUT_NAME "solv")
-SET_TARGET_PROPERTIES(libsolv PROPERTIES SOVERSION ${LIBSOLV_SOVERSION})
-SET_TARGET_PROPERTIES(libsolv PROPERTIES INSTALL_NAME_DIR ${LIB_INSTALL_DIR})
-
-INSTALL (FILES ${libsolv_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/solv")
-INSTALL (TARGETS libsolv LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
-
-IF (ENABLE_STATIC AND NOT DISABLE_SHARED)
-ADD_LIBRARY (libsolv_static STATIC ${libsolv_SRCS})
-SET_TARGET_PROPERTIES(libsolv_static PROPERTIES OUTPUT_NAME "solv")
-SET_TARGET_PROPERTIES(libsolv_static PROPERTIES SOVERSION ${LIBSOLV_SOVERSION})
-INSTALL (TARGETS libsolv_static LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
-ENDIF (ENABLE_STATIC AND NOT DISABLE_SHARED)
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * bitmap.c
- *
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "bitmap.h"
-#include "util.h"
-
-/* constructor */
-void
-map_init(Map *m, int n)
-{
- m->size = (n + 7) >> 3;
- m->map = m->size ? solv_calloc(m->size, 1) : 0;
-}
-
-/* destructor */
-void
-map_free(Map *m)
-{
- m->map = solv_free(m->map);
- m->size = 0;
-}
-
-/* copy constructor t <- s */
-void
-map_init_clone(Map *t, Map *s)
-{
- t->size = s->size;
- if (s->size)
- {
- t->map = solv_malloc(s->size);
- memcpy(t->map, s->map, s->size);
- }
- else
- t->map = 0;
-}
-
-/* grow a map */
-void
-map_grow(Map *m, int n)
-{
- n = (n + 7) >> 3;
- if (m->size < n)
- {
- m->map = solv_realloc(m->map, n);
- memset(m->map + m->size, 0, n - m->size);
- m->size = n;
- }
-}
-
-/* bitwise-ands maps t and s, stores the result in t. */
-void
-map_and(Map *t, Map *s)
-{
- unsigned char *ti, *si, *end;
- ti = t->map;
- si = s->map;
- end = ti + (t->size < s->size ? t->size : s->size);
- while (ti < end)
- *ti++ &= *si++;
-}
-
-/* bitwise-ors maps t and s, stores the result in t. */
-void
-map_or(Map *t, Map *s)
-{
- unsigned char *ti, *si, *end;
- if (t->size < s->size)
- map_grow(t, s->size << 3);
- ti = t->map;
- si = s->map;
- end = ti + (t->size < s->size ? t->size : s->size);
- while (ti < end)
- *ti++ |= *si++;
-}
-
-/* remove all set bits in s from t. */
-void
-map_subtract(Map *t, Map *s)
-{
- unsigned char *ti, *si, *end;
- ti = t->map;
- si = s->map;
- end = ti + (t->size < s->size ? t->size : s->size);
- while (ti < end)
- *ti++ &= ~*si++;
-}
-
-/* EOF */
+++ /dev/null
-/*
- * Copyright (c) 2007-2011, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * bitmap.h
- *
- */
-
-#ifndef LIBSOLV_BITMAP_H
-#define LIBSOLV_BITMAP_H
-
-#include <string.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct _Map {
- unsigned char *map;
- int size;
-} Map;
-
-#define MAPZERO(m) (memset((m)->map, 0, (m)->size))
-/* set all bits */
-#define MAPSETALL(m) (memset((m)->map, 0xff, (m)->size))
-/* set bit */
-#define MAPSET(m, n) ((m)->map[(n) >> 3] |= 1 << ((n) & 7))
-/* clear bit */
-#define MAPCLR(m, n) ((m)->map[(n) >> 3] &= ~(1 << ((n) & 7)))
-/* test bit */
-#define MAPTST(m, n) ((m)->map[(n) >> 3] & (1 << ((n) & 7)))
-
-extern void map_init(Map *m, int n);
-extern void map_init_clone(Map *t, Map *s);
-extern void map_grow(Map *m, int n);
-extern void map_free(Map *m);
-extern void map_and(Map *t, Map *s);
-extern void map_or(Map *t, Map *s);
-extern void map_subtract(Map *t, Map *s);
-
-static inline void map_empty(Map *m)
-{
- MAPZERO(m);
-}
-static inline void map_set(Map *m, int n)
-{
- MAPSET(m, n);
-}
-static inline void map_setall(Map *m)
-{
- MAPSETALL(m);
-}
-static inline void map_clr(Map *m, int n)
-{
- MAPCLR(m, n);
-}
-static inline int map_tst(Map *m, int n)
-{
- return MAPTST(m, n);
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_BITMAP_H */
+++ /dev/null
-/*
- * Copyright (c) 2008-2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "util.h"
-#include "chksum.h"
-
-#include "md5.h"
-#include "sha1.h"
-#include "sha2.h"
-
-struct _Chksum {
- Id type;
- int done;
- unsigned char result[64];
- union {
- MD5_CTX md5;
- SHA1_CTX sha1;
- SHA224_CTX sha224;
- SHA256_CTX sha256;
- SHA384_CTX sha384;
- SHA512_CTX sha512;
- } c;
-};
-
-Chksum *
-solv_chksum_create(Id type)
-{
- Chksum *chk;
- chk = solv_calloc(1, sizeof(*chk));
- chk->type = type;
- switch(type)
- {
- case REPOKEY_TYPE_MD5:
- solv_MD5_Init(&chk->c.md5);
- return chk;
- case REPOKEY_TYPE_SHA1:
- solv_SHA1_Init(&chk->c.sha1);
- return chk;
- case REPOKEY_TYPE_SHA224:
- solv_SHA224_Init(&chk->c.sha224);
- return chk;
- case REPOKEY_TYPE_SHA256:
- solv_SHA256_Init(&chk->c.sha256);
- return chk;
- case REPOKEY_TYPE_SHA384:
- solv_SHA384_Init(&chk->c.sha384);
- return chk;
- case REPOKEY_TYPE_SHA512:
- solv_SHA512_Init(&chk->c.sha512);
- return chk;
- default:
- break;
- }
- free(chk);
- return 0;
-}
-
-Chksum *
-solv_chksum_create_clone(Chksum *chk)
-{
- return solv_memdup(chk, sizeof(*chk));
-}
-
-int
-solv_chksum_len(Id type)
-{
- switch (type)
- {
- case REPOKEY_TYPE_MD5:
- return 16;
- case REPOKEY_TYPE_SHA1:
- return 20;
- case REPOKEY_TYPE_SHA224:
- return 28;
- case REPOKEY_TYPE_SHA256:
- return 32;
- case REPOKEY_TYPE_SHA384:
- return 48;
- case REPOKEY_TYPE_SHA512:
- return 64;
- default:
- return 0;
- }
-}
-
-Chksum *
-solv_chksum_create_from_bin(Id type, const unsigned char *buf)
-{
- Chksum *chk;
- int l = solv_chksum_len(type);
- if (buf == 0 || l == 0)
- return 0;
- chk = solv_calloc(1, sizeof(*chk));
- chk->type = type;
- chk->done = 1;
- memcpy(chk->result, buf, l);
- return chk;
-}
-
-void
-solv_chksum_add(Chksum *chk, const void *data, int len)
-{
- if (chk->done)
- return;
- switch(chk->type)
- {
- case REPOKEY_TYPE_MD5:
- solv_MD5_Update(&chk->c.md5, (void *)data, len);
- return;
- case REPOKEY_TYPE_SHA1:
- solv_SHA1_Update(&chk->c.sha1, data, len);
- return;
- case REPOKEY_TYPE_SHA224:
- solv_SHA224_Update(&chk->c.sha224, data, len);
- return;
- case REPOKEY_TYPE_SHA256:
- solv_SHA256_Update(&chk->c.sha256, data, len);
- return;
- case REPOKEY_TYPE_SHA384:
- solv_SHA384_Update(&chk->c.sha384, data, len);
- return;
- case REPOKEY_TYPE_SHA512:
- solv_SHA512_Update(&chk->c.sha512, data, len);
- return;
- default:
- return;
- }
-}
-
-const unsigned char *
-solv_chksum_get(Chksum *chk, int *lenp)
-{
- if (chk->done)
- {
- if (lenp)
- *lenp = solv_chksum_len(chk->type);
- return chk->result;
- }
- switch(chk->type)
- {
- case REPOKEY_TYPE_MD5:
- solv_MD5_Final(chk->result, &chk->c.md5);
- chk->done = 1;
- if (lenp)
- *lenp = 16;
- return chk->result;
- case REPOKEY_TYPE_SHA1:
- solv_SHA1_Final(&chk->c.sha1, chk->result);
- chk->done = 1;
- if (lenp)
- *lenp = 20;
- return chk->result;
- case REPOKEY_TYPE_SHA224:
- solv_SHA224_Final(chk->result, &chk->c.sha224);
- chk->done = 1;
- if (lenp)
- *lenp = 28;
- return chk->result;
- case REPOKEY_TYPE_SHA256:
- solv_SHA256_Final(chk->result, &chk->c.sha256);
- chk->done = 1;
- if (lenp)
- *lenp = 32;
- return chk->result;
- case REPOKEY_TYPE_SHA384:
- solv_SHA384_Final(chk->result, &chk->c.sha384);
- chk->done = 1;
- if (lenp)
- *lenp = 48;
- return chk->result;
- case REPOKEY_TYPE_SHA512:
- solv_SHA512_Final(chk->result, &chk->c.sha512);
- chk->done = 1;
- if (lenp)
- *lenp = 64;
- return chk->result;
- default:
- if (lenp)
- *lenp = 0;
- return 0;
- }
-}
-
-Id
-solv_chksum_get_type(Chksum *chk)
-{
- return chk->type;
-}
-
-int
-solv_chksum_isfinished(Chksum *chk)
-{
- return chk->done != 0;
-}
-
-const char *
-solv_chksum_type2str(Id type)
-{
- switch(type)
- {
- case REPOKEY_TYPE_MD5:
- return "md5";
- case REPOKEY_TYPE_SHA1:
- return "sha1";
- case REPOKEY_TYPE_SHA224:
- return "sha224";
- case REPOKEY_TYPE_SHA256:
- return "sha256";
- case REPOKEY_TYPE_SHA384:
- return "sha384";
- case REPOKEY_TYPE_SHA512:
- return "sha512";
- default:
- return 0;
- }
-}
-
-Id
-solv_chksum_str2type(const char *str)
-{
- if (!strcasecmp(str, "md5"))
- return REPOKEY_TYPE_MD5;
- if (!strcasecmp(str, "sha") || !strcasecmp(str, "sha1"))
- return REPOKEY_TYPE_SHA1;
- if (!strcasecmp(str, "sha224"))
- return REPOKEY_TYPE_SHA224;
- if (!strcasecmp(str, "sha256"))
- return REPOKEY_TYPE_SHA256;
- if (!strcasecmp(str, "sha384"))
- return REPOKEY_TYPE_SHA384;
- if (!strcasecmp(str, "sha512"))
- return REPOKEY_TYPE_SHA512;
- return 0;
-}
-
-void *
-solv_chksum_free(Chksum *chk, unsigned char *cp)
-{
- if (cp)
- {
- const unsigned char *res;
- int l;
- res = solv_chksum_get(chk, &l);
- if (l && res)
- memcpy(cp, res, l);
- }
- solv_free(chk);
- return 0;
-}
-
-int
-solv_chksum_cmp(Chksum *chk, Chksum *chk2)
-{
- int len;
- const unsigned char *res1, *res2;
- if (chk == chk2)
- return 1;
- if (!chk || !chk2 || chk->type != chk2->type)
- return 0;
- res1 = solv_chksum_get(chk, &len);
- res2 = solv_chksum_get(chk2, 0);
- return memcmp(res1, res2, len) == 0 ? 1 : 0;
-}
+++ /dev/null
-/*
- * Copyright (c) 2007-2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef LIBSOLV_CHKSUM_H
-#define LIBSOLV_CHKSUM_H
-
-#include "pool.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct _Chksum;
-typedef struct _Chksum Chksum;
-
-Chksum *solv_chksum_create(Id type);
-Chksum *solv_chksum_create_clone(Chksum *chk);
-Chksum *solv_chksum_create_from_bin(Id type, const unsigned char *buf);
-void solv_chksum_add(Chksum *chk, const void *data, int len);
-Id solv_chksum_get_type(Chksum *chk);
-int solv_chksum_isfinished(Chksum *chk);
-const unsigned char *solv_chksum_get(Chksum *chk, int *lenp);
-void *solv_chksum_free(Chksum *chk, unsigned char *cp);
-const char *solv_chksum_type2str(Id type);
-Id solv_chksum_str2type(const char *str);
-int solv_chksum_len(Id type);
-int solv_chksum_cmp(Chksum *chk, Chksum *chk2);
-
-#ifdef LIBSOLV_INTERNAL
-
-#define case_CHKSUM_TYPES \
- case REPOKEY_TYPE_MD5: \
- case REPOKEY_TYPE_SHA1: \
- case REPOKEY_TYPE_SHA224: \
- case REPOKEY_TYPE_SHA256: \
- case REPOKEY_TYPE_SHA384: \
- case REPOKEY_TYPE_SHA512
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_CHKSUM_H */
+++ /dev/null
-/*
- * Copyright (c) 2014, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * cplxdeps.c
- *
- * normalize complex dependencies into CNF/DNF form
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-
-#include "pool.h"
-#include "cplxdeps.h"
-
-#ifdef ENABLE_COMPLEX_DEPS
-
-#undef CPLXDEBUG
-
-int
-pool_is_complex_dep_rd(Pool *pool, Reldep *rd)
-{
- for (;;)
- {
- if (rd->flags == REL_AND || rd->flags == REL_COND) /* those two are the complex ones */
- return 1;
- if (rd->flags != REL_OR)
- return 0;
- if (ISRELDEP(rd->name) && pool_is_complex_dep_rd(pool, GETRELDEP(pool, rd->name)))
- return 1;
- if (!ISRELDEP(rd->evr))
- return 0;
- rd = GETRELDEP(pool, rd->evr);
- }
-}
-
-/* expand simple dependencies into package lists */
-static int
-expand_simpledeps(Pool *pool, Queue *bq, int start, int split)
-{
- int end = bq->count;
- int i, x;
- int newsplit = 0;
- for (i = start; i < end; i++)
- {
- if (i == split)
- newsplit = bq->count - (end - start);
- x = bq->elements[i];
- if (x == pool->nsolvables)
- {
- Id *dp = pool->whatprovidesdata + bq->elements[++i];
- for (; *dp; dp++)
- queue_push(bq, *dp);
- }
- else
- queue_push(bq, x);
- }
- if (i == split)
- newsplit = bq->count - (end - start);
- queue_deleten(bq, start, end - start);
- return newsplit;
-}
-
-#ifdef CPLXDEBUG
-static void
-print_depblocks(Pool *pool, Queue *bq, int start)
-{
- int i;
-
- for (i = start; i < bq->count; i++)
- {
- if (bq->elements[i] == pool->nsolvables)
- {
- Id *dp = pool->whatprovidesdata + bq->elements[++i];
- printf(" (");
- while (*dp)
- printf(" %s", pool_solvid2str(pool, *dp++));
- printf(" )");
- }
- else if (bq->elements[i] > 0)
- printf(" %s", pool_solvid2str(pool, bq->elements[i]));
- else if (bq->elements[i] < 0)
- printf(" -%s", pool_solvid2str(pool, -bq->elements[i]));
- else
- printf(" ||");
- }
- printf("\n");
-}
-#endif
-
-/* invert all literals in the blocks. note that this also turns DNF into CNF and vice versa */
-static int
-invert_depblocks(Pool *pool, Queue *bq, int start, int r)
-{
- int i, j, end;
- if (r == 0 || r == 1)
- return r ? 0 : 1;
- expand_simpledeps(pool, bq, start, 0);
- end = bq->count;
- for (i = j = start; i < end; i++)
- {
- if (bq->elements[i])
- {
- bq->elements[i] = -bq->elements[i];
- continue;
- }
- /* end of block reached, reverse */
- if (i - 1 > j)
- {
- int k;
- for (k = i - 1; j < k; j++, k--)
- {
- Id t = bq->elements[j];
- bq->elements[j] = bq->elements[k];
- bq->elements[k] = t;
- }
- }
- j = i + 1;
- }
- return -1;
-}
-
-/*
- * returns:
- * 0: no blocks
- * 1: matches all
- * -1: at least one block
- */
-static int
-normalize_dep(Pool *pool, Id dep, Queue *bq, int flags)
-{
- int bqcnt = bq->count;
- int bqcnt2;
- int todnf = flags & CPLXDEPS_TODNF ? 1 : 0;
- Id p, dp;
-
-#ifdef CPLXDEBUG
- printf("normalize_dep %s todnf:%d\n", pool_dep2str(pool, dep), todnf);
-#endif
- if (pool_is_complex_dep(pool, dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_COND)
- {
- int rdflags = rd->flags;
- Id name = rd->name;
- Id evr = rd->evr;
- int r, mode;
-
- if (rdflags == REL_COND)
- {
- /* check for relly complex ELSE case */
- if (ISRELDEP(evr))
- {
- Reldep *rd2 = GETRELDEP(pool, evr);
- if (rd2->flags == REL_ELSE)
- {
- int r2;
- /* really complex case */
- if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_AND_1)
- {
- /* A OR ~B */
- rdflags = REL_COND;
- evr = rd2->name;
- }
- else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_AND_2)
- {
- /* C OR B */
- rdflags = REL_OR;
- name = rd2->evr;
- evr = rd2->name;
- }
- else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_1)
- {
- /* A AND B */
- rdflags = REL_AND;
- evr = rd2->name;
- }
- else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_2)
- {
- /* A AND C */
- rdflags = REL_AND;
- evr = rd2->evr;
- }
- else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_3)
- {
- /* C AND ~B */
- rdflags = REL_ELSE;
- name = rd2->evr;
- evr = rd2->name;
- }
- else if (!todnf)
- {
- /* we want AND: A IF (B ELSE C) -> (A OR ~B) AND (C OR B) */
- r = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_AND_1);
- if (r == 0 && (flags & CPLXDEPS_DONTFIX) == 0)
- return 0;
- r2 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_AND_2);
- if (r2 == 0 && (flags & CPLXDEPS_DONTFIX) == 0)
- {
- queue_truncate(bq, bqcnt);
- return 0;
- }
- if (r == -1 || r2 == -1)
- return -1;
- return r == 1 || r2 == 1 ? 1 : 0;
- }
- else
- {
- int r2, r3;
- /* we want OR: A IF (B ELSE C) -> (A AND B) OR (A AND C) OR (~B AND C) */
- r = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_1);
- if (r == 1)
- return 1;
- r2 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_2);
- if (r2 == 1)
- {
- queue_truncate(bq, bqcnt);
- return 1;
- }
- r3 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_3);
- if (r3 == 1)
- {
- queue_truncate(bq, bqcnt);
- return 1;
- }
- if (r == -1 || r2 == -1 || r3 == -1)
- return -1;
- return 0;
- }
- }
- }
- }
- mode = rdflags == REL_AND || rdflags == REL_ELSE ? 0 : 1;
-
- /* get blocks of first argument */
- r = normalize_dep(pool, name, bq, flags);
- if (r == 0)
- {
- if (rdflags == REL_ELSE)
- return 0;
- if (rdflags == REL_AND && (flags & CPLXDEPS_DONTFIX) == 0)
- return 0;
- if (rdflags == REL_COND)
- {
- r = normalize_dep(pool, evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX);
- return invert_depblocks(pool, bq, bqcnt, r); /* invert block for COND */
- }
- return normalize_dep(pool, evr, bq, flags);
- }
- if (r == 1)
- {
- if (rdflags == REL_ELSE)
- {
- r = normalize_dep(pool, evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX);
- return invert_depblocks(pool, bq, bqcnt, r); /* invert block for ELSE */
- }
- if (rdflags == REL_OR || rdflags == REL_COND)
- return 1;
- return normalize_dep(pool, evr, bq, flags);
- }
-
- /* get blocks of second argument */
- bqcnt2 = bq->count;
- /* COND is OR with NEG on evr block, so we invert the todnf flag in that case */
- r = normalize_dep(pool, evr, bq, rdflags == REL_COND || rdflags == REL_ELSE ? ((flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX) : flags);
- if (rdflags == REL_COND || rdflags == REL_ELSE)
- r = invert_depblocks(pool, bq, bqcnt2, r); /* invert 2nd block */
- if (r == 0)
- {
- if (rdflags == REL_OR)
- return -1;
- if (rdflags == REL_AND && (flags & CPLXDEPS_DONTFIX) != 0)
- return -1;
- queue_truncate(bq, bqcnt);
- return 0;
- }
- if (r == 1)
- {
- if (rdflags == REL_COND || rdflags == REL_OR)
- {
- queue_truncate(bq, bqcnt);
- return 1;
- }
- return -1;
- }
- if (mode == todnf)
- {
- /* simple case: just join em. nothing more to do here. */
-#ifdef CPLXDEBUG
- printf("SIMPLE JOIN %d %d %d\n", bqcnt, bqcnt2, bq->count);
-#endif
- return -1;
- }
- else
- {
- /* complex case: mix em */
- int i, j, bqcnt3;
-#ifdef CPLXDEBUG
- printf("COMPLEX JOIN %d %d %d\n", bqcnt, bqcnt2, bq->count);
-#endif
- bqcnt2 = expand_simpledeps(pool, bq, bqcnt, bqcnt2);
- bqcnt3 = bq->count;
- for (i = bqcnt; i < bqcnt2; i++)
- {
- for (j = bqcnt2; j < bqcnt3; j++)
- {
- int a, b;
- int bqcnt4 = bq->count;
- int k = i;
-
- /* mix i block with j block, both blocks are sorted */
- while (bq->elements[k] && bq->elements[j])
- {
- if (bq->elements[k] < bq->elements[j])
- queue_push(bq, bq->elements[k++]);
- else
- {
- if (bq->elements[k] == bq->elements[j])
- k++;
- queue_push(bq, bq->elements[j++]);
- }
- }
- while (bq->elements[j])
- queue_push(bq, bq->elements[j++]);
- while (bq->elements[k])
- queue_push(bq, bq->elements[k++]);
-
- /* block is finished, check for A + -A */
- for (a = bqcnt4, b = bq->count - 1; a < b; )
- {
- if (-bq->elements[a] == bq->elements[b])
- break;
- if (-bq->elements[a] > bq->elements[b])
- a++;
- else
- b--;
- }
- if (a < b)
- queue_truncate(bq, bqcnt4); /* ignore this block */
- else
- queue_push(bq, 0); /* finish block */
- }
- /* advance to next block */
- while (bq->elements[i])
- i++;
- }
- i = -1;
- if (bqcnt3 == bq->count) /* ignored all blocks? */
- i = todnf ? 0 : 1;
- queue_deleten(bq, bqcnt, bqcnt3 - bqcnt);
- return i;
- }
- }
- }
-
- /* fallback case: just use package list */
- dp = pool_whatprovides(pool, dep);
- if (dp <= 2 || !pool->whatprovidesdata[dp])
- return dp == 2 ? 1 : 0;
- if (pool->whatprovidesdata[dp] == SYSTEMSOLVABLE)
- return 1;
- bqcnt = bq->count;
- if ((flags & CPLXDEPS_NAME) != 0)
- {
- while ((p = pool->whatprovidesdata[dp++]) != 0)
- {
- if (!pool_match_nevr(pool, pool->solvables + p, dep))
- continue;
- queue_push(bq, p);
- if (todnf)
- queue_push(bq, 0);
- }
- }
- else if (todnf)
- {
- while ((p = pool->whatprovidesdata[dp++]) != 0)
- queue_push2(bq, p, 0);
- }
- else
- queue_push2(bq, pool->nsolvables, dp); /* not yet expanded marker + offset */
- if (bq->count == bqcnt)
- return 0; /* no provider */
- if (!todnf)
- queue_push(bq, 0); /* finish block */
- return -1;
-}
-
-int
-pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags)
-{
- int i, bqcnt = bq->count;
-
- i = normalize_dep(pool, dep, bq, flags);
- if ((flags & CPLXDEPS_EXPAND) != 0)
- {
- if (i != 0 && i != 1)
- expand_simpledeps(pool, bq, bqcnt, 0);
- }
- if ((flags & CPLXDEPS_INVERT) != 0)
- i = invert_depblocks(pool, bq, bqcnt, i);
-#ifdef CPLXDEBUG
- if (i == 0)
- printf("NONE\n");
- else if (i == 1)
- printf("ALL\n");
- else
- print_depblocks(pool, bq, bqcnt);
-#endif
- return i;
-}
-
-void
-pool_add_pos_literals_complex_dep(Pool *pool, Id dep, Queue *q, Map *m, int neg)
-{
- while (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags != REL_AND && rd->flags != REL_OR && rd->flags != REL_COND)
- break;
- pool_add_pos_literals_complex_dep(pool, rd->name, q, m, neg);
- dep = rd->evr;
- if (rd->flags == REL_COND)
- {
- neg = !neg;
- if (ISRELDEP(dep))
- {
- Reldep *rd2 = GETRELDEP(pool, rd->evr);
- if (rd2->flags == REL_ELSE)
- {
- pool_add_pos_literals_complex_dep(pool, rd2->evr, q, m, !neg);
- dep = rd2->name;
- }
- }
- }
- }
- if (!neg)
- {
- Id p, pp;
- FOR_PROVIDES(p, pp, dep)
- if (!MAPTST(m, p))
- queue_push(q, p);
- }
-}
-
-#endif /* ENABLE_COMPLEX_DEPS */
-
+++ /dev/null
-/*
- * Copyright (c) 2014, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * cplxdeps.h (internal)
- */
-
-#ifndef LIBSOLV_CPLXDEPS_H
-#define LIBSOLV_CPLXDEPS_H
-
-extern int pool_is_complex_dep_rd(Pool *pool, Reldep *rd);
-
-static inline int
-pool_is_complex_dep(Pool *pool, Id dep)
-{
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags >= 8 && pool_is_complex_dep_rd(pool, rd))
- return 1;
- }
- return 0;
-}
-
-extern int pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags);
-extern void pool_add_pos_literals_complex_dep(Pool *pool, Id dep, Queue *q, Map *m, int neg);
-
-#define CPLXDEPS_TODNF (1 << 0)
-#define CPLXDEPS_EXPAND (1 << 1)
-#define CPLXDEPS_INVERT (1 << 2)
-#define CPLXDEPS_NAME (1 << 3)
-#define CPLXDEPS_DONTFIX (1 << 4)
-
-#define CPLXDEPS_ELSE_AND_1 (1 << 8)
-#define CPLXDEPS_ELSE_AND_2 (1 << 9)
-#define CPLXDEPS_ELSE_OR_1 (1 << 10)
-#define CPLXDEPS_ELSE_OR_2 (1 << 11)
-#define CPLXDEPS_ELSE_OR_3 (1 << 12)
-#define CPLXDEPS_ELSE_MASK (0x1f00)
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (c) 2007-2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * dataiterator.h
- *
- */
-
-#ifndef LIBSOLV_DATAITERATOR_H
-#define LIBSOLV_DATAITERATOR_H
-
-#include "pooltypes.h"
-#include "pool.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct _Repo;
-
-typedef struct _KeyValue {
- Id id;
- const char *str;
- unsigned int num;
- unsigned int num2;
-
- int entry; /* array entry, starts with 0 */
- int eof; /* last entry reached */
-
- struct _KeyValue *parent;
-} KeyValue;
-
-#define SOLV_KV_NUM64(kv) (((unsigned long long)((kv)->num2)) << 32 | (kv)->num)
-
-/* search matcher flags */
-#define SEARCH_STRINGMASK 15
-#define SEARCH_STRING 1
-#define SEARCH_STRINGSTART 2
-#define SEARCH_STRINGEND 3
-#define SEARCH_SUBSTRING 4
-#define SEARCH_GLOB 5
-#define SEARCH_REGEX 6
-#define SEARCH_ERROR 15
-#define SEARCH_NOCASE (1<<7)
-
-/* iterator control */
-#define SEARCH_NO_STORAGE_SOLVABLE (1<<8)
-#define SEARCH_SUB (1<<9)
-#define SEARCH_ARRAYSENTINEL (1<<10)
-#define SEARCH_DISABLED_REPOS (1<<11)
-#define SEARCH_COMPLETE_FILELIST (1<<12)
-
-/* stringification flags */
-#define SEARCH_SKIP_KIND (1<<16)
-/* By default we stringify just to the basename of a file because
- the construction of the full filename is costly. Specify this
- flag if you want to match full filenames */
-#define SEARCH_FILES (1<<17)
-#define SEARCH_CHECKSUMS (1<<18)
-
-/* dataiterator internal */
-#define SEARCH_THISSOLVID (1<<31)
-
-/*
- * Datamatcher: match a string against a query
- */
-typedef struct _Datamatcher {
- int flags; /* see matcher flags above */
- const char *match; /* the query string */
- void *matchdata; /* e.g. compiled regexp */
- int error;
-} Datamatcher;
-
-int datamatcher_init(Datamatcher *ma, const char *match, int flags);
-void datamatcher_free(Datamatcher *ma);
-int datamatcher_match(Datamatcher *ma, const char *str);
-int datamatcher_checkbasename(Datamatcher *ma, const char *str);
-
-
-/*
- * Dataiterator
- *
- * Iterator like interface to 'search' functionality
- *
- * Dataiterator is per-pool, additional filters can be applied
- * to limit the search domain. See dataiterator_init below.
- *
- * Use these like:
- * Dataiterator di;
- * dataiterator_init(&di, repo->pool, repo, 0, 0, "bla", SEARCH_SUBSTRING);
- * while (dataiterator_step(&di))
- * dosomething(di.solvid, di.key, di.kv);
- * dataiterator_free(&di);
- */
-typedef struct _Dataiterator
-{
- int state;
- int flags;
-
- Pool *pool;
- struct _Repo *repo;
- struct _Repodata *data;
-
- /* data pointers */
- unsigned char *dp;
- unsigned char *ddp;
- Id *idp;
- Id *keyp;
-
- /* the result */
- struct _Repokey *key;
- KeyValue kv;
-
- /* our matcher */
- Datamatcher matcher;
-
- /* iterators/filters */
- Id keyname;
- Id repodataid;
- Id solvid;
- Id repoid;
-
- Id keynames[3 + 1];
- int nkeynames;
- int rootlevel;
-
- /* recursion data */
- struct di_parent {
- KeyValue kv;
- unsigned char *dp;
- Id *keyp;
- } parents[3];
- int nparents;
-
- /* vertical data */
- unsigned char *vert_ddp;
- Id vert_off;
- Id vert_len;
- Id vert_storestate;
-
- /* strdup data */
- char *dupstr;
- int dupstrn;
-
-} Dataiterator;
-
-
-/*
- * Initialize dataiterator
- *
- * di: Pointer to Dataiterator to be initialized
- * pool: Search domain for the iterator
- * repo: if non-null, limit search to this repo
- * solvid: if non-null, limit search to this solvable
- * keyname: if non-null, limit search to this keyname
- * match: if non-null, limit search to this match
- */
-int dataiterator_init(Dataiterator *di, Pool *pool, struct _Repo *repo, Id p, Id keyname, const char *match, int flags);
-void dataiterator_init_clone(Dataiterator *di, Dataiterator *from);
-void dataiterator_set_search(Dataiterator *di, struct _Repo *repo, Id p);
-void dataiterator_set_keyname(Dataiterator *di, Id keyname);
-int dataiterator_set_match(Dataiterator *di, const char *match, int flags);
-
-void dataiterator_prepend_keyname(Dataiterator *di, Id keyname);
-void dataiterator_free(Dataiterator *di);
-int dataiterator_step(Dataiterator *di);
-void dataiterator_setpos(Dataiterator *di);
-void dataiterator_setpos_parent(Dataiterator *di);
-int dataiterator_match(Dataiterator *di, Datamatcher *ma);
-void dataiterator_skip_attribute(Dataiterator *di);
-void dataiterator_skip_solvable(Dataiterator *di);
-void dataiterator_skip_repo(Dataiterator *di);
-void dataiterator_jump_to_solvid(Dataiterator *di, Id solvid);
-void dataiterator_jump_to_repo(Dataiterator *di, struct _Repo *repo);
-void dataiterator_entersub(Dataiterator *di);
-void dataiterator_clonepos(Dataiterator *di, Dataiterator *from);
-void dataiterator_seek(Dataiterator *di, int whence);
-void dataiterator_strdup(Dataiterator *di);
-
-#define DI_SEEK_STAY (1 << 16)
-#define DI_SEEK_CHILD 1
-#define DI_SEEK_PARENT 2
-#define DI_SEEK_REWIND 3
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_DATAITERATOR_H */
+++ /dev/null
-/*
- * Copyright (c) 2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "pool.h"
-#include "util.h"
-#include "dirpool.h"
-
-#define DIR_BLOCK 127
-
-/* directories are stored as components,
- * components are simple ids from the string pool
- * /usr/bin -> "", "usr", "bin"
- * /usr/lib -> "", "usr", "lib"
- * foo/bar -> "foo", "bar"
- * /usr/games -> "", "usr", "games"
- *
- * all directories are stores in the "dirs" array
- * dirs[id] > 0 : component string pool id
- * dirs[id] <= 0 : -(parent directory id)
- *
- * Directories with the same parent are stored as
- * multiple blocks. We need multiple blocks because
- * we cannot insert entries into old blocks, as that
- * would shift the ids of already used directories.
- * Each block starts with (-parent_dirid) and contains
- * component ids of the directory entries.
- * (The (-parent_dirid) entry is not a valid directory
- * id, it's just used internally)
- *
- * There is also the aux "dirtraverse" array, which
- * is created on demand to speed things up a bit.
- * if dirs[id] > 0, dirtravers[id] points to the first
- * entry in the last block with parent id.
- * if dirs[id] <= 0, dirtravers[id] points to the entry
- * in the previous block with the same parent.
- * (Thus it acts as a linked list that starts at the
- * parent dirid and chains all the blocks with that
- * parent.)
- *
- * id dirs[id] dirtraverse[id]
- * 0 0 8 [no parent, block#0]
- * 1 "" 3
- * 2 -1 [parent 1, /, block #0]
- * 3 "usr" 12
- * 4 -3 [parent 3, /usr, block #0]
- * 5 "bin"
- * 6 "lib"
- * 7 0 1 [no parent, block#1]
- * 8 "foo" 10
- * 9 -8 [parent 8, foo, block #0]
- * 10 "bar"
- * 11 -3 5 [parent 3, /usr, block #1]
- * 12 "games"
- *
- * to find all children of dirid 3 ("/usr"), follow the
- * dirtraverse link to 12 -> "games". Then follow the
- * dirtraverse link of this block to 5 -> "bin", "lib"
- */
-
-void
-dirpool_init(Dirpool *dp)
-{
- memset(dp, 0, sizeof(*dp));
-}
-
-void
-dirpool_free(Dirpool *dp)
-{
- solv_free(dp->dirs);
- solv_free(dp->dirtraverse);
-}
-
-void
-dirpool_make_dirtraverse(Dirpool *dp)
-{
- Id parent, i, *dirtraverse;
- if (!dp->ndirs)
- return;
- dp->dirs = solv_extend_resize(dp->dirs, dp->ndirs, sizeof(Id), DIR_BLOCK);
- dirtraverse = solv_calloc_block(dp->ndirs, sizeof(Id), DIR_BLOCK);
- for (parent = 0, i = 0; i < dp->ndirs; i++)
- {
- if (dp->dirs[i] > 0)
- continue;
- parent = -dp->dirs[i];
- dirtraverse[i] = dirtraverse[parent];
- dirtraverse[parent] = i + 1;
- }
- dp->dirtraverse = dirtraverse;
-}
-
-Id
-dirpool_add_dir(Dirpool *dp, Id parent, Id comp, int create)
-{
- Id did, d, ds, *dirtraverse;
-
- if (!dp->ndirs)
- {
- if (!create)
- return 0;
- dp->ndirs = 2;
- dp->dirs = solv_extend_resize(dp->dirs, dp->ndirs, sizeof(Id), DIR_BLOCK);
- dp->dirs[0] = 0;
- dp->dirs[1] = 1; /* "" */
- }
- if (parent == 0 && comp == 1)
- return 1;
- if (!dp->dirtraverse)
- dirpool_make_dirtraverse(dp);
- /* check all entries with this parent if we
- * already have this component */
- dirtraverse = dp->dirtraverse;
- ds = dirtraverse[parent];
- while (ds)
- {
- /* ds: first component in this block
- * ds-1: parent link */
- for (d = ds--; d < dp->ndirs; d++)
- {
- if (dp->dirs[d] == comp)
- return d;
- if (dp->dirs[d] <= 0) /* reached end of this block */
- break;
- }
- if (ds)
- ds = dp->dirtraverse[ds];
- }
- if (!create)
- return 0;
- /* a new one, find last parent */
- for (did = dp->ndirs - 1; did > 0; did--)
- if (dp->dirs[did] <= 0)
- break;
- if (dp->dirs[did] != -parent)
- {
- /* make room for parent entry */
- dp->dirs = solv_extend(dp->dirs, dp->ndirs, 1, sizeof(Id), DIR_BLOCK);
- dp->dirtraverse = solv_extend(dp->dirtraverse, dp->ndirs, 1, sizeof(Id), DIR_BLOCK);
- /* new parent block, link in */
- dp->dirs[dp->ndirs] = -parent;
- dp->dirtraverse[dp->ndirs] = dp->dirtraverse[parent];
- dp->dirtraverse[parent] = ++dp->ndirs;
- }
- /* make room for new entry */
- dp->dirs = solv_extend(dp->dirs, dp->ndirs, 1, sizeof(Id), DIR_BLOCK);
- dp->dirtraverse = solv_extend(dp->dirtraverse, dp->ndirs, 1, sizeof(Id), DIR_BLOCK);
- dp->dirs[dp->ndirs] = comp;
- dp->dirtraverse[dp->ndirs] = 0;
- return dp->ndirs++;
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-#ifndef LIBSOLV_DIRPOOL_H
-#define LIBSOLV_DIRPOOL_H
-
-
-#include "pooltypes.h"
-#include "util.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct _Dirpool {
- Id *dirs;
- int ndirs;
- Id *dirtraverse;
-} Dirpool;
-
-void dirpool_init(Dirpool *dp);
-void dirpool_free(Dirpool *dp);
-
-void dirpool_make_dirtraverse(Dirpool *dp);
-Id dirpool_add_dir(Dirpool *dp, Id parent, Id comp, int create);
-
-/* return the parent directory of child did */
-static inline Id dirpool_parent(Dirpool *dp, Id did)
-{
- if (!did)
- return 0;
- while (dp->dirs[--did] > 0)
- ;
- return -dp->dirs[did];
-}
-
-/* return the next child entry of child did */
-static inline Id
-dirpool_sibling(Dirpool *dp, Id did)
-{
- /* if this block contains another entry, simply return it */
- if (did + 1 < dp->ndirs && dp->dirs[did + 1] > 0)
- return did + 1;
- /* end of block reached, rewind to get to the block's
- * dirtraverse entry */
- while (dp->dirs[--did] > 0)
- ;
- /* need to special case did == 0 to prevent looping */
- if (!did)
- return 0;
- if (!dp->dirtraverse)
- dirpool_make_dirtraverse(dp);
- return dp->dirtraverse[did];
-}
-
-/* return the first child entry of directory did */
-static inline Id
-dirpool_child(Dirpool *dp, Id did)
-{
- if (!dp->dirtraverse)
- dirpool_make_dirtraverse(dp);
- return dp->dirtraverse[did];
-}
-
-static inline void
-dirpool_free_dirtraverse(Dirpool *dp)
-{
- solv_free(dp->dirtraverse);
- dp->dirtraverse = 0;
-}
-
-static inline Id
-dirpool_compid(Dirpool *dp, Id did)
-{
- return dp->dirs[did];
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_DIRPOOL_H */
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * evr.c
- *
- * version compare
- */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include "evr.h"
-#include "pool.h"
-
-
-
-#if defined(DEBIAN) || defined(MULTI_SEMANTICS)
-
-/* debian type version compare */
-int
-solv_vercmp_deb(const char *s1, const char *q1, const char *s2, const char *q2)
-{
- int r, c1, c2;
- while (1)
- {
- c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
- c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
- if ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9'))
- {
- while (c1 == '0')
- c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
- while (c2 == '0')
- c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
- r = 0;
- while ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9'))
- {
- if (!r)
- r = c1 - c2;
- c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
- c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
- }
- if (c1 >= '0' && c1 <= '9')
- return 1;
- if (c2 >= '0' && c2 <= '9')
- return -1;
- if (r)
- return r < 0 ? -1 : 1;
- }
- c1 = c1 == '~' ? -1 : !c1 || (c1 >= '0' && c1 <= '9') || (c1 >= 'A' && c1 <= 'Z') || (c1 >= 'a' && c1 <= 'z') ? c1 : c1 + 256;
- c2 = c2 == '~' ? -1 : !c2 || (c2 >= '0' && c2 <= '9') || (c2 >= 'A' && c2 <= 'Z') || (c2 >= 'a' && c2 <= 'z') ? c2 : c2 + 256;
- r = c1 - c2;
- if (r)
- return r < 0 ? -1 : 1;
- if (!c1)
- return 0;
- }
-}
-
-#endif
-
-#if !defined(DEBIAN) || defined(MULTI_SEMANTICS)
-
-/* rpm type version compare */
-/* note: the code assumes that *q1 and *q2 are not alphanumeric! */
-
-int
-solv_vercmp_rpm(const char *s1, const char *q1, const char *s2, const char *q2)
-{
- int r = 0;
- const char *e1, *e2;
-
- for (;;)
- {
- while (s1 < q1 && !(*s1 >= '0' && *s1 <= '9') &&
- !(*s1 >= 'a' && *s1 <= 'z') && !(*s1 >= 'A' && *s1 <= 'Z') && *s1 != '~')
- s1++;
- while (s2 < q2 && !(*s2 >= '0' && *s2 <= '9') &&
- !(*s2 >= 'a' && *s2 <= 'z') && !(*s2 >= 'A' && *s2 <= 'Z') && *s2 != '~')
- s2++;
- if (s1 < q1 && *s1 == '~')
- {
- if (s2 < q2 && *s2 == '~')
- {
- s1++;
- s2++;
- continue;
- }
- return -1;
- }
- if (s2 < q2 && *s2 == '~')
- return 1;
- if (s1 >= q1 || s2 >= q2)
- break;
- 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;
-}
-
-int
-solv_vercmp_rpm_notilde(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;
-}
-
-#endif
-
-#if defined(HAIKU) || defined(MULTI_SEMANTICS)
-
-static int
-solv_cmp_version_part_haiku(const char *s1, const char *q1, const char *s2,
- const char *q2)
-{
- while (s1 < q1 && s2 < q2)
- {
- int cmp, len1, len2;
- const char *part1 = s1, *part2 = s2;
-
- /* compare non-number part */
- while (s1 < q1 && !isdigit(*s1))
- s1++;
- while (s2 < q2 && !isdigit(*s2))
- s2++;
-
- if (part1 != s1)
- {
- if (part2 == s2)
- return 1;
-
- len1 = s1 - part1;
- len2 = s2 - part2;
- cmp = strncmp(part1, part2, len1 < len2 ? len1 : len2);
- if (cmp != 0)
- return cmp;
- if (len1 != len2)
- return len1 - len2;
- }
- else if (part2 != s2)
- return -1;
-
- /* compare number part */
- part1 = s1;
- part2 = s2;
-
- while (s1 < q1 && isdigit(*s1))
- s1++;
- while (s2 < q2 && isdigit(*s2))
- s2++;
-
- while (part1 + 1 < s1 && *part1 == '0')
- part1++;
- while (part2 + 1 < s2 && *part2 == '0')
- part2++;
-
- len1 = s1 - part1;
- len2 = s2 - part2;
- if (len1 != len2)
- return len1 - len2;
- if (len1 == 0)
- return 0;
-
- cmp = strncmp(part1, part2, len1);
- if (cmp != 0)
- return cmp;
- }
-
- return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
-}
-
-int
-solv_vercmp_haiku(const char *s1, const char *q1, const char *s2, const char *q2)
-{
- const char *pre1 = s1;
- const char *pre2 = s2;
- int cmp;
-
- /* find pre-release separator */
- while (pre1 != q1 && *pre1 != '~')
- pre1++;
- while (pre2 != q2 && *pre2 != '~')
- pre2++;
-
- /* compare main versions */
- cmp = solv_cmp_version_part_haiku(s1, pre1, s2, pre2);
- if (cmp != 0)
- return cmp < 0 ? -1 : 1; /* must return -1, 0, or 1 */
-
- /* main versions are equal -- compare pre-release (none is greatest) */
- if (pre1 == q1)
- return pre2 == q2 ? 0 : 1;
- if (pre2 == q2)
- return -1;
-
- cmp = solv_cmp_version_part_haiku(pre1 + 1, q1, pre2 + 1, q2);
- return cmp == 0 ? 0 : cmp < 0 ? -1 : 1; /* must return -1, 0, or 1 */
-}
-
-#endif /* HAIKU */
-
-
-/*
- * the solv_vercmp variant your system uses.
- */
-int
-solv_vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
-{
-#if defined(DEBIAN)
- return solv_vercmp_deb(s1, q1, s2, q2);
-#elif defined(ARCHLINUX)
- return solv_vercmp_rpm_notilde(s1, q1, s2, q2);
-#elif defined(HAIKU)
- return solv_vercmp_haiku(s1, q1, s2, q2);
-#else
- return solv_vercmp_rpm(s1, q1, s2, q2);
-#endif
-}
-
-#if defined(MULTI_SEMANTICS)
-# define solv_vercmp (*(pool->disttype == DISTTYPE_DEB ? &solv_vercmp_deb : \
- pool->disttype == DISTTYPE_HAIKU ? solv_vercmp_haiku : \
- &solv_ver##cmp_rpm))
-#elif defined(DEBIAN)
-# define solv_vercmp solv_vercmp_deb
-#elif defined(ARCHLINUX)
-# define solv_vercmp solv_vercmp_rpm_notilde
-#elif defined(HAIKU)
-# define solv_vercmp solv_vercmp_haiku
-#else
-# define solv_vercmp solv_vercmp_rpm
-#endif
-
-/* edition (e:v-r) compare */
-int
-pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode)
-{
- int r;
- const char *s1, *s2;
- const char *r1, *r2;
-
- if (evr1 == evr2)
- return 0;
-
-#if 0
- POOL_DEBUG(DEBUG_EVRCMP, "evrcmp %s %s mode=%d\n", evr1, evr2, mode);
-#endif
- for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
- ;
- for (s2 = evr2; *s2 >= '0' && *s2 <= '9'; s2++)
- ;
- if (mode == EVRCMP_MATCH && (*evr1 == ':' || *evr2 == ':'))
- {
- /* empty epoch, skip epoch check */
- if (*s1 == ':')
- evr1 = s1 + 1;
- if (*s2 == ':')
- evr2 = s2 + 1;
- s1 = evr1;
- s2 = evr2;
- }
-
- /* compare the epoch */
- if (s1 == evr1 || *s1 != ':')
- s1 = 0;
- if (s2 == evr2 || *s2 != ':')
- s2 = 0;
- if (s1 && s2)
- {
- r = solv_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 = s2 + 1;
- }
-
- /* same epoch, now split into version/release */
- for (s1 = evr1, r1 = 0; *s1; s1++)
- if (*s1 == '-')
- r1 = s1;
- for (s2 = evr2, r2 = 0; *s2; s2++)
- if (*s2 == '-')
- r2 = s2;
- r = 0;
- if (mode != EVRCMP_MATCH || (evr1 != (r1 ? r1 : s1) && evr2 != (r2 ? r2 : s2)))
- r = solv_vercmp(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2);
- if (r)
- return r;
-
- if (mode == EVRCMP_COMPARE)
- {
- if (!r1 && r2)
- return -1;
- if (r1 && !r2)
- return 1;
- }
- if (mode == EVRCMP_COMPARE_EVONLY)
- return 0;
- if (mode == EVRCMP_MATCH_RELEASE)
- {
- /* rpm treats empty releases as missing, i.e "foo = 4-" is the same as "foo = 4" */
- if (r1 && r1 + 1 == s1)
- r1 = 0;
- if (r2 && r2 + 1 == s2)
- r2 = 0;
- }
- if (r1 && r2)
- {
- r1++;
- r2++;
- if (mode != EVRCMP_MATCH || (s1 != r1 && s2 != r2))
- {
- if (pool->havedistepoch)
- {
- const char *d1, *d2;
- for (d1 = r1; d1 < s1; d1++)
- if (*d1 == ':')
- break;
- for (d2 = r2; d2 < s2; d2++)
- if (*d2 == ':')
- break;
- /* XXX: promote just in one direction? */
- r = solv_vercmp(r1, d1 ? d1 : s1, r2, d2 ? d2 : s2);
- if (r == 0 && d1 < s1 && d2 < s2)
- r = solv_vercmp(d1 + 1, s1, d2 + 1, s2);
- }
- else
- r = solv_vercmp(r1, s1, r2, s2);
- }
- }
- else if (mode == EVRCMP_MATCH_RELEASE)
- {
- if (!r1 && r2)
- return -2;
- if (r1 && !r2)
- return 2;
- }
- return r;
-}
-
-int
-pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode)
-{
- const char *evr1, *evr2;
- if (evr1id == evr2id)
- return 0;
- evr1 = pool_id2str(pool, evr1id);
- evr2 = pool_id2str(pool, evr2id);
- return pool_evrcmp_str(pool, evr1, evr2, mode);
-}
-
-int
-pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release)
-{
- const char *evr1;
- const char *s1;
- const char *r1;
- int r;
-
- evr1 = pool_id2str(pool, evrid);
- for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
- ;
- if (s1 != evr1 && *s1 == ':')
- {
- if (epoch)
- {
- r = solv_vercmp(evr1, s1, epoch, epoch + strlen(epoch));
- if (r)
- return r;
- }
- evr1 = s1 + 1;
- }
- else if (epoch)
- {
- while (*epoch == '0')
- epoch++;
- if (*epoch)
- return -1;
- }
- for (s1 = evr1, r1 = 0; *s1; s1++)
- if (*s1 == '-')
- r1 = s1;
- if (version)
- {
- r = solv_vercmp(evr1, r1 ? r1 : s1, version, version + strlen(version));
- if (r)
- return r;
- }
- if (release)
- {
- if (!r1)
- return -1;
- r = solv_vercmp(r1 + 1, s1, release, release + strlen(release));
- if (r)
- return r;
- }
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * evr.h
- *
- */
-
-#ifndef LIBSOLV_EVR_H
-#define LIBSOLV_EVR_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "pooltypes.h"
-
-#define EVRCMP_COMPARE 0
-#define EVRCMP_MATCH_RELEASE 1
-#define EVRCMP_MATCH 2
-#define EVRCMP_COMPARE_EVONLY 3
-
-extern int solv_vercmp(const char *s1, const char *q1, const char *s2, const char *q2);
-
-extern int pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode);
-extern int pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode);
-extern int pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_EVR_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * hash.h
- * generic hash functions
- */
-
-#ifndef LIBSOLV_HASH_H
-#define LIBSOLV_HASH_H
-
-#include "pooltypes.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* value of a hash */
-typedef unsigned int Hashval;
-
-/* 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;
-}
-
-static inline Hashval
-strnhash(const char *str, unsigned len)
-{
- Hashval r = 0;
- unsigned int c;
- while (len-- && (c = *(const unsigned char *)str++) != 0)
- r += (r << 3) + c;
- return r;
-}
-
-static inline Hashval
-strhash_cont(const char *str, Hashval r)
-{
- 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) > 2 * num + 3
- *
- * used for Hashtable 'modulo' operation
- */
-static inline Hashval
-mkmask(unsigned int num)
-{
- num = num * 2 + 3;
- while (num & (num - 1))
- num &= num - 1;
- return num * 2 - 1;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_HASH_H */
+++ /dev/null
-/*
- * Copyright (c) 2007-2014, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * knownid.h
- *
- */
-
-/*
- * Warning: you're free to append new entries, but insert/delete breaks
- * the ABI!
- */
-
-#ifndef LIBSOLV_KNOWNID_H
-#define LIBSOLV_KNOWNID_H
-
-#undef KNOWNID
-#ifdef KNOWNID_INITIALIZE
-# define KNOWNID(a, b) b
-static const char *initpool_data[] = {
-#else
-# define KNOWNID(a, b) a
-enum solv_knownid {
-#endif
-
-KNOWNID(ID_NULL, "<NULL>"),
-KNOWNID(ID_EMPTY, ""),
-
-/* The following Ids are stored in the solvable and must
- * come in one block */
-KNOWNID(SOLVABLE_NAME, "solvable:name"),
-KNOWNID(SOLVABLE_ARCH, "solvable:arch"),
-KNOWNID(SOLVABLE_EVR, "solvable:evr"),
-KNOWNID(SOLVABLE_VENDOR, "solvable:vendor"),
-KNOWNID(SOLVABLE_PROVIDES, "solvable:provides"),
-KNOWNID(SOLVABLE_OBSOLETES, "solvable:obsoletes"),
-KNOWNID(SOLVABLE_CONFLICTS, "solvable:conflicts"),
-KNOWNID(SOLVABLE_REQUIRES, "solvable:requires"),
-KNOWNID(SOLVABLE_RECOMMENDS, "solvable:recommends"),
-KNOWNID(SOLVABLE_SUGGESTS, "solvable:suggests"),
-KNOWNID(SOLVABLE_SUPPLEMENTS, "solvable:supplements"),
-KNOWNID(SOLVABLE_ENHANCES, "solvable:enhances"),
-KNOWNID(RPM_RPMDBID, "rpm:dbid"),
-
-/* normal requires before this, prereqs after this */
-KNOWNID(SOLVABLE_PREREQMARKER, "solvable:prereqmarker"),
-/* normal provides before this, generated file provides after this */
-KNOWNID(SOLVABLE_FILEMARKER, "solvable:filemarker"),
-
-KNOWNID(NAMESPACE_INSTALLED, "namespace:installed"),
-KNOWNID(NAMESPACE_MODALIAS, "namespace:modalias"),
-KNOWNID(NAMESPACE_SPLITPROVIDES, "namespace:splitprovides"),
-KNOWNID(NAMESPACE_LANGUAGE, "namespace:language"),
-KNOWNID(NAMESPACE_FILESYSTEM, "namespace:filesystem"),
-KNOWNID(NAMESPACE_OTHERPROVIDERS, "namespace:otherproviders"),
-
-KNOWNID(SYSTEM_SYSTEM, "system:system"),
-
-/* special solvable architectures */
-KNOWNID(ARCH_SRC, "src"),
-KNOWNID(ARCH_NOSRC, "nosrc"),
-KNOWNID(ARCH_NOARCH, "noarch"),
-KNOWNID(ARCH_ALL, "all"),
-KNOWNID(ARCH_ANY, "any"),
-
-/* the meta tags used in solv file storage */
-KNOWNID(REPOSITORY_SOLVABLES, "repository:solvables"),
-KNOWNID(REPOSITORY_DELTAINFO, "repository:deltainfo"),
-
-/* sub-repository information, they will get loaded on demand */
-KNOWNID(REPOSITORY_EXTERNAL, "repository:external"),
-KNOWNID(REPOSITORY_KEYS, "repository:keys"),
-KNOWNID(REPOSITORY_LOCATION, "repository:location"),
-
-/* file provides already added to our solvables */
-KNOWNID(REPOSITORY_ADDEDFILEPROVIDES, "repository:addedfileprovides"),
-/* inode of the rpm database for rpm --rebuilddb detection */
-KNOWNID(REPOSITORY_RPMDBCOOKIE, "repository:rpmdbcookie"),
-
-/* the known data types */
-KNOWNID(REPOKEY_TYPE_VOID, "repokey:type:void"),
-KNOWNID(REPOKEY_TYPE_CONSTANT, "repokey:type:constant"),
-KNOWNID(REPOKEY_TYPE_CONSTANTID, "repokey:type:constantid"),
-KNOWNID(REPOKEY_TYPE_ID, "repokey:type:id"),
-KNOWNID(REPOKEY_TYPE_NUM, "repokey:type:num"),
-KNOWNID(REPOKEY_TYPE_U32, "repokey:type:num32"),
-KNOWNID(REPOKEY_TYPE_DIR, "repokey:type:dir"),
-KNOWNID(REPOKEY_TYPE_STR, "repokey:type:str"),
-KNOWNID(REPOKEY_TYPE_BINARY, "repokey:type:binary"),
-KNOWNID(REPOKEY_TYPE_IDARRAY, "repokey:type:idarray"),
-KNOWNID(REPOKEY_TYPE_REL_IDARRAY, "repokey:type:relidarray"),
-KNOWNID(REPOKEY_TYPE_DIRSTRARRAY, "repokey:type:dirstrarray"),
-KNOWNID(REPOKEY_TYPE_DIRNUMNUMARRAY, "repokey:type:dirnumnumarray"),
-KNOWNID(REPOKEY_TYPE_MD5, "repokey:type:md5"),
-KNOWNID(REPOKEY_TYPE_SHA1, "repokey:type:sha1"),
-KNOWNID(REPOKEY_TYPE_SHA224, "repokey:type:sha224"),
-KNOWNID(REPOKEY_TYPE_SHA256, "repokey:type:sha256"),
-KNOWNID(REPOKEY_TYPE_SHA384, "repokey:type:sha384"),
-KNOWNID(REPOKEY_TYPE_SHA512, "repokey:type:sha512"),
-KNOWNID(REPOKEY_TYPE_FIXARRAY, "repokey:type:fixarray"),
-KNOWNID(REPOKEY_TYPE_FLEXARRAY, "repokey:type:flexarray"),
-KNOWNID(REPOKEY_TYPE_DELETED, "repokey:type:deleted"), /* internal only */
-
-KNOWNID(SOLVABLE_SUMMARY, "solvable:summary"),
-KNOWNID(SOLVABLE_DESCRIPTION, "solvable:description"),
-KNOWNID(SOLVABLE_DISTRIBUTION, "solvable:distribution"),
-KNOWNID(SOLVABLE_AUTHORS, "solvable:authors"),
-KNOWNID(SOLVABLE_PACKAGER, "solvable:packager"),
-KNOWNID(SOLVABLE_GROUP, "solvable:group"),
-KNOWNID(SOLVABLE_URL, "solvable:url"),
-KNOWNID(SOLVABLE_KEYWORDS, "solvable:keywords"),
-KNOWNID(SOLVABLE_LICENSE, "solvable:license"),
-KNOWNID(SOLVABLE_BUILDTIME, "solvable:buildtime"),
-KNOWNID(SOLVABLE_BUILDHOST, "solvable:buildhost"),
-KNOWNID(SOLVABLE_EULA, "solvable:eula"),
-KNOWNID(SOLVABLE_CPEID, "solvable:cpeid"),
-KNOWNID(SOLVABLE_MESSAGEINS, "solvable:messageins"),
-KNOWNID(SOLVABLE_MESSAGEDEL, "solvable:messagedel"),
-KNOWNID(SOLVABLE_INSTALLSIZE, "solvable:installsize"),
-KNOWNID(SOLVABLE_DISKUSAGE, "solvable:diskusage"),
-KNOWNID(SOLVABLE_FILELIST, "solvable:filelist"),
-KNOWNID(SOLVABLE_INSTALLTIME, "solvable:installtime"),
-KNOWNID(SOLVABLE_MEDIADIR, "solvable:mediadir"),
-KNOWNID(SOLVABLE_MEDIAFILE, "solvable:mediafile"),
-KNOWNID(SOLVABLE_MEDIANR, "solvable:medianr"),
-KNOWNID(SOLVABLE_MEDIABASE, "solvable:mediabase"), /* <location xml:base=... > */
-KNOWNID(SOLVABLE_DOWNLOADSIZE, "solvable:downloadsize"),
-KNOWNID(SOLVABLE_SOURCEARCH, "solvable:sourcearch"),
-KNOWNID(SOLVABLE_SOURCENAME, "solvable:sourcename"),
-KNOWNID(SOLVABLE_SOURCEEVR, "solvable:sourceevr"),
-KNOWNID(SOLVABLE_ISVISIBLE, "solvable:isvisible"),
-KNOWNID(SOLVABLE_TRIGGERS, "solvable:triggers"),
-KNOWNID(SOLVABLE_CHECKSUM, "solvable:checksum"),
-KNOWNID(SOLVABLE_PKGID, "solvable:pkgid"), /* pkgid: md5sum over header + payload */
-KNOWNID(SOLVABLE_HDRID, "solvable:hdrid"), /* hdrid: sha1sum over header only */
-KNOWNID(SOLVABLE_LEADSIGID, "solvable:leadsigid"), /* leadsigid: md5sum over lead + sigheader */
-
-KNOWNID(SOLVABLE_PATCHCATEGORY, "solvable:patchcategory"),
-KNOWNID(SOLVABLE_HEADEREND, "solvable:headerend"),
-KNOWNID(SOLVABLE_CHANGELOG, "solvable:changelog"),
-KNOWNID(SOLVABLE_CHANGELOG_AUTHOR, "solvable:changelog:author"),
-KNOWNID(SOLVABLE_CHANGELOG_TIME, "solvable:changelog:time"),
-KNOWNID(SOLVABLE_CHANGELOG_TEXT, "solvable:changelog:text"),
-
-/* stuff for solvables of type pattern */
-KNOWNID(SOLVABLE_CATEGORY, "solvable:category"),
-KNOWNID(SOLVABLE_INCLUDES, "solvable:includes"),
-KNOWNID(SOLVABLE_EXTENDS, "solvable:extends"),
-KNOWNID(SOLVABLE_ICON, "solvable:icon"),
-KNOWNID(SOLVABLE_ORDER, "solvable:order"),
-
-/* extra definitions for updates (i.e. patch: solvables) */
-KNOWNID(UPDATE_REBOOT, "update:reboot"), /* reboot suggested (kernel update) */
-KNOWNID(UPDATE_RESTART, "update:restart"), /* restart suggested (update stack update) */
-KNOWNID(UPDATE_RELOGIN, "update:relogin"), /* relogin suggested */
-
-KNOWNID(UPDATE_MESSAGE, "update:message"), /* informative message */
-KNOWNID(UPDATE_SEVERITY, "update:severity"), /* "Important", ...*/
-KNOWNID(UPDATE_RIGHTS, "update:rights"), /* copyright */
-
-/* 'content' of patch, usually list of packages */
-KNOWNID(UPDATE_COLLECTION, "update:collection"), /* "name evr arch" */
-KNOWNID(UPDATE_COLLECTION_NAME, "update:collection:name"), /* name */
-KNOWNID(UPDATE_COLLECTION_EVR, "update:collection:evr"), /* epoch:version-release */
-KNOWNID(UPDATE_COLLECTION_ARCH, "update:collection:arch"), /* architecture */
-KNOWNID(UPDATE_COLLECTION_FILENAME, "update:collection:filename"), /* filename (of rpm) */
-KNOWNID(UPDATE_COLLECTION_FLAGS, "update:collection:flags"), /* reboot(1)/restart(2) suggested if this rpm gets updated */
-
-KNOWNID(UPDATE_REFERENCE, "update:reference"), /* external references for the update */
-KNOWNID(UPDATE_REFERENCE_TYPE, "update:reference:type"), /* type, e.g. 'bugzilla' or 'cve' */
-KNOWNID(UPDATE_REFERENCE_HREF, "update:reference:href"), /* href, e.g. 'http://bugzilla...' */
-KNOWNID(UPDATE_REFERENCE_ID, "update:reference:id"), /* id, e.g. bug number */
-KNOWNID(UPDATE_REFERENCE_TITLE, "update:reference:title"), /* title, e.g. "the bla forz scribs on fuggle" */
-
-/* extra definitions for products */
-KNOWNID(PRODUCT_REFERENCEFILE, "product:referencefile"), /* installed product only */
-KNOWNID(PRODUCT_SHORTLABEL, "product:shortlabel"), /* not in repomd? */
-KNOWNID(PRODUCT_DISTPRODUCT, "product:distproduct"), /* obsolete */
-KNOWNID(PRODUCT_DISTVERSION, "product:distversion"), /* obsolete */
-KNOWNID(PRODUCT_TYPE, "product:type"), /* e.g. 'base' */
-KNOWNID(PRODUCT_URL, "product:url"),
-KNOWNID(PRODUCT_URL_TYPE, "product:url:type"),
-KNOWNID(PRODUCT_FLAGS, "product:flags"), /* e.g. 'update', 'no_you' */
-KNOWNID(PRODUCT_PRODUCTLINE, "product:productline"), /* installed product only */
-KNOWNID(PRODUCT_REGISTER_TARGET, "product:regtarget"), /* installed and available product */
-KNOWNID(PRODUCT_REGISTER_RELEASE, "product:regrelease"), /* installed product only */
-KNOWNID(PRODUCT_UPDATES_REPOID, "product:updates:repoid"),
-KNOWNID(PRODUCT_UPDATES, "product:updates"),
-KNOWNID(PRODUCT_ENDOFLIFE, "product:endoflife"),
-
-/* argh, should rename to repository and unify with REPOMD */
-KNOWNID(SUSETAGS_DATADIR, "susetags:datadir"),
-KNOWNID(SUSETAGS_DESCRDIR, "susetags:descrdir"),
-KNOWNID(SUSETAGS_DEFAULTVENDOR, "susetags:defaultvendor"),
-KNOWNID(SUSETAGS_FILE, "susetags:file"),
-KNOWNID(SUSETAGS_FILE_NAME, "susetags:file:name"),
-KNOWNID(SUSETAGS_FILE_TYPE, "susetags:file:type"),
-KNOWNID(SUSETAGS_FILE_CHECKSUM, "susetags:file:checksum"),
-KNOWNID(SUSETAGS_SHARE_NAME, "susetags:share:name"),
-KNOWNID(SUSETAGS_SHARE_EVR, "susetags:share:evr"),
-KNOWNID(SUSETAGS_SHARE_ARCH, "susetags:share:arch"),
-
-/* timestamp then the repository was generated */
-KNOWNID(REPOSITORY_TIMESTAMP, "repository:timestamp"),
-/* hint when the metadata could be outdated w/respect to generated timestamp */
-KNOWNID(REPOSITORY_EXPIRE, "repository:expire"),
-/* which things does this repo provides updates for, if it does (array) */
-KNOWNID(REPOSITORY_UPDATES, "repository:updates"), /* obsolete? */
-/* which products this repository is supposed to be for (array) */
-KNOWNID(REPOSITORY_DISTROS, "repository:distros"),
-KNOWNID(REPOSITORY_PRODUCT_LABEL, "repository:product:label"),
-KNOWNID(REPOSITORY_PRODUCT_CPEID, "repository:product:cpeid"),
-KNOWNID(REPOSITORY_REPOID, "repository:repoid"), /* obsolete? */
-/* keyword (tags) for this repository */
-KNOWNID(REPOSITORY_KEYWORDS, "repository:keywords"),
-/* revision of the repository. arbitrary string */
-KNOWNID(REPOSITORY_REVISION, "repository:revision"),
-KNOWNID(REPOSITORY_TOOLVERSION, "repository:toolversion"),
-
-KNOWNID(DELTA_PACKAGE_NAME, "delta:pkgname"),
-KNOWNID(DELTA_PACKAGE_EVR, "delta:pkgevr"),
-KNOWNID(DELTA_PACKAGE_ARCH, "delta:pkgarch"),
-KNOWNID(DELTA_LOCATION_DIR, "delta:locdir"),
-KNOWNID(DELTA_LOCATION_NAME, "delta:locname"),
-KNOWNID(DELTA_LOCATION_EVR, "delta:locevr"),
-KNOWNID(DELTA_LOCATION_SUFFIX, "delta:locsuffix"),
-KNOWNID(DELTA_DOWNLOADSIZE, "delta:downloadsize"),
-KNOWNID(DELTA_CHECKSUM, "delta:checksum"),
-KNOWNID(DELTA_BASE_EVR, "delta:baseevr"),
-KNOWNID(DELTA_SEQ_NAME, "delta:seqname"),
-KNOWNID(DELTA_SEQ_EVR, "delta:seqevr"),
-KNOWNID(DELTA_SEQ_NUM, "delta:seqnum"),
-KNOWNID(DELTA_LOCATION_BASE, "delta:locbase"), /* <location xml:base=... > */
-
-KNOWNID(REPOSITORY_REPOMD, "repository:repomd"),
-KNOWNID(REPOSITORY_REPOMD_TYPE, "repository:repomd:type"),
-KNOWNID(REPOSITORY_REPOMD_LOCATION, "repository:repomd:location"),
-KNOWNID(REPOSITORY_REPOMD_TIMESTAMP, "repository:repomd:timestamp"),
-KNOWNID(REPOSITORY_REPOMD_CHECKSUM, "repository:repomd:checksum"),
-KNOWNID(REPOSITORY_REPOMD_OPENCHECKSUM, "repository:repomd:openchecksum"),
-KNOWNID(REPOSITORY_REPOMD_SIZE, "repository:repomd:size"),
-
-KNOWNID(PUBKEY_KEYID, "pubkey:keyid"),
-KNOWNID(PUBKEY_FINGERPRINT, "pubkey:fingerprint"),
-KNOWNID(PUBKEY_EXPIRES, "pubkey:expires"),
-KNOWNID(PUBKEY_SIGNATURES, "pubkey:signatures"),
-KNOWNID(PUBKEY_DATA, "pubkey:data"),
-KNOWNID(PUBKEY_SUBKEYOF, "pubkey:subkeyof"),
-
-KNOWNID(SIGNATURE_ISSUER, "signature:issuer"),
-KNOWNID(SIGNATURE_TIME, "signature:time"),
-KNOWNID(SIGNATURE_EXPIRES, "signature:expires"),
-KNOWNID(SIGNATURE_DATA, "signature:data"),
-
-KNOWNID(PRODUCT_REGISTER_FLAVOR, "product:regflavor"), /* installed and available product */
-
-KNOWNID(SOLVABLE_INSTALLSTATUS, "solvable:installstatus"), /* debian install status */
-
-KNOWNID(ID_NUM_INTERNAL, 0)
-
-#ifdef KNOWNID_INITIALIZE
-};
-#else
-};
-#endif
-
-#undef KNOWNID
-
-#endif
-
-
+++ /dev/null
-SOLV_1.0 {
- global:
- dataiterator_clonepos;
- dataiterator_entersub;
- dataiterator_free;
- dataiterator_init;
- dataiterator_init_clone;
- dataiterator_jump_to_repo;
- dataiterator_jump_to_solvid;
- dataiterator_match;
- dataiterator_prepend_keyname;
- dataiterator_seek;
- dataiterator_set_keyname;
- dataiterator_set_match;
- dataiterator_set_search;
- dataiterator_setpos;
- dataiterator_setpos_parent;
- dataiterator_skip_attribute;
- dataiterator_skip_repo;
- dataiterator_skip_solvable;
- dataiterator_step;
- dataiterator_strdup;
- datamatcher_free;
- datamatcher_init;
- datamatcher_match;
- dirpool_add_dir;
- dirpool_free;
- dirpool_init;
- dirpool_make_dirtraverse;
- map_and;
- map_subtract;
- map_free;
- map_grow;
- map_init;
- map_init_clone;
- map_or;
- policy_filter_unwanted;
- policy_findupdatepackages;
- policy_illegal2str;
- policy_illegal_archchange;
- policy_illegal_vendorchange;
- policy_is_illegal;
- pool_add_fileconflicts_deps;
- pool_add_userinstalled_jobs;
- pool_addfileprovides;
- pool_addfileprovides_queue;
- pool_addrelproviders;
- pool_addvendorclass;
- pool_alloctmpspace;
- pool_arch2color_slow;
- pool_bin2hex;
- pool_calc_duchanges;
- pool_calc_installsizechange;
- pool_clear_pos;
- pool_create;
- pool_create_state_maps;
- pool_createwhatprovides;
- pool_debug;
- pool_dep2str;
- pool_error;
- pool_errstr;
- pool_evrcmp;
- pool_evrcmp_str;
- pool_evrmatch;
- pool_flush_namespaceproviders;
- pool_free;
- pool_freeallrepos;
- pool_freeidhashes;
- pool_freetmpspace;
- pool_freewhatprovides;
- pool_get_flag;
- pool_get_rootdir;
- pool_id2evr;
- pool_id2langid;
- pool_id2rel;
- pool_id2str;
- pool_ids2whatprovides;
- pool_intersect_evrs;
- pool_isemptyupdatejob;
- pool_job2solvables;
- pool_job2str;
- pool_lookup_bin_checksum;
- pool_lookup_checksum;
- pool_lookup_deltalocation;
- pool_lookup_id;
- pool_lookup_idarray;
- pool_lookup_num;
- pool_lookup_str;
- pool_lookup_void;
- pool_match_dep;
- pool_match_nevr_rel;
- pool_prepend_rootdir;
- pool_prepend_rootdir_tmp;
- pool_queuetowhatprovides;
- pool_rel2id;
- pool_search;
- pool_selection2str;
- pool_set_custom_vendorcheck;
- pool_set_flag;
- pool_set_installed;
- pool_set_languages;
- pool_set_rootdir;
- pool_setarch;
- pool_setarchpolicy;
- pool_setdebugcallback;
- pool_setdebuglevel;
- pool_setdebugmask;
- pool_setdisttype;
- pool_setloadcallback;
- pool_setnamespacecallback;
- pool_setvendorclasses;
- pool_shrink_rels;
- pool_shrink_strings;
- pool_solvable2str;
- pool_str2id;
- pool_strn2id;
- pool_tmpappend;
- pool_tmpjoin;
- pool_trivial_installable;
- pool_trivial_installable_multiversionmap;
- pool_vendor2mask;
- pool_whatmatchesdep;
- queue_alloc_one;
- queue_alloc_one_head;
- queue_delete;
- queue_delete2;
- queue_deleten;
- queue_free;
- queue_init;
- queue_init_buffer;
- queue_init_clone;
- queue_insert;
- queue_insert2;
- queue_insertn;
- queue_prealloc;
- repo_add_deparray;
- repo_add_idarray;
- repo_add_poolstr_array;
- repo_add_repodata;
- repo_add_solv;
- repo_add_solvable;
- repo_add_solvable_block;
- repo_add_solvable_block_before;
- repo_addid;
- repo_addid_dep;
- repo_create;
- repo_disable_paging;
- repo_empty;
- repo_fix_conflicts;
- repo_fix_supplements;
- repo_free;
- repo_free_solvable;
- repo_free_solvable_block;
- repo_id2repodata;
- repo_internalize;
- repo_last_repodata;
- repo_lookup_bin_checksum;
- repo_lookup_binary;
- repo_lookup_checksum;
- repo_lookup_deparray;
- repo_lookup_id;
- repo_lookup_idarray;
- repo_lookup_num;
- repo_lookup_str;
- repo_lookup_type;
- repo_lookup_void;
- repo_matchvalue;
- repo_reserve_ids;
- repo_search;
- repo_set_deparray;
- repo_set_id;
- repo_set_idarray;
- repo_set_num;
- repo_set_poolstr;
- repo_set_str;
- repo_sidedata_create;
- repo_unset;
- repo_write;
- repo_write_filtered;
- repo_write_stdkeyfilter;
- repodata_add_dirnumnum;
- repodata_add_dirstr;
- repodata_add_fixarray;
- repodata_add_flexarray;
- repodata_add_idarray;
- repodata_add_poolstr_array;
- repodata_chk2str;
- repodata_create_stubs;
- repodata_dir2str;
- repodata_disable_paging;
- repodata_empty;
- repodata_extend;
- repodata_extend_block;
- repodata_free;
- repodata_free_dircache;
- repodata_free_schemahash;
- repodata_freedata;
- repodata_globalize_id;
- repodata_initdata;
- repodata_internalize;
- repodata_key2id;
- repodata_localize_id;
- repodata_lookup_bin_checksum;
- repodata_lookup_binary;
- repodata_lookup_dirstrarray_uninternalized;
- repodata_lookup_id;
- repodata_lookup_id_uninternalized;
- repodata_lookup_idarray;
- repodata_lookup_num;
- repodata_lookup_str;
- repodata_lookup_type;
- repodata_lookup_void;
- repodata_memused;
- repodata_merge_attrs;
- repodata_merge_some_attrs;
- repodata_new_handle;
- repodata_schema2id;
- repodata_search;
- repodata_set_binary;
- repodata_set_bin_checksum;
- repodata_set_checksum;
- repodata_set_constant;
- repodata_set_constantid;
- repodata_set_deltalocation;
- repodata_set_id;
- repodata_set_idarray;
- repodata_set_location;
- repodata_set_num;
- repodata_set_poolstr;
- repodata_set_sourcepkg;
- repodata_set_str;
- repodata_set_void;
- repodata_setpos_kv;
- repodata_shrink;
- repodata_str2dir;
- repodata_stringify;
- repodata_swap_attrs;
- repodata_translate_id;
- repodata_unset;
- repodata_unset_uninternalized;
- repodata_write;
- repodata_write_filtered;
- repopagestore_compress_page;
- selection_add;
- selection_filter;
- selection_make;
- selection_make_matchdeps;
- selection_solvables;
- solv_bin2hex;
- solv_calloc;
- solv_chksum_add;
- solv_chksum_cmp;
- solv_chksum_create;
- solv_chksum_create_clone;
- solv_chksum_create_from_bin;
- solv_chksum_free;
- solv_chksum_get;
- solv_chksum_get_type;
- solv_chksum_isfinished;
- solv_chksum_len;
- solv_chksum_str2type;
- solv_chksum_type2str;
- solv_depmarker;
- solv_dupappend;
- solv_dupjoin;
- solv_extend_realloc;
- solv_free;
- solv_hex2bin;
- solv_latin1toutf8;
- solv_malloc;
- solv_malloc2;
- solv_oom;
- solv_realloc;
- solv_realloc2;
- solv_replacebadutf8;
- solv_sort;
- solv_strdup;
- solv_timems;
- solv_validutf8;
- solv_vercmp;
- solv_vercmp_deb;
- solv_vercmp_haiku;
- solv_vercmp_rpm;
- solv_vercmp_rpm_notilde;
- solv_version;
- solv_version_major;
- solv_version_minor;
- solv_version_patch;
- solvable_add_deparray;
- solvable_add_idarray;
- solvable_add_poolstr_array;
- solvable_get_location;
- solvable_identical;
- solvable_lookup_bin_checksum;
- solvable_lookup_bool;
- solvable_lookup_checksum;
- solvable_lookup_deparray;
- solvable_lookup_id;
- solvable_lookup_idarray;
- solvable_lookup_location;
- solvable_lookup_num;
- solvable_lookup_sizek;
- solvable_lookup_sourcepkg;
- solvable_lookup_str;
- solvable_lookup_str_lang;
- solvable_lookup_str_poollang;
- solvable_lookup_type;
- solvable_lookup_void;
- solvable_selfprovidedep;
- solvable_set_deparray;
- solvable_set_id;
- solvable_set_idarray;
- solvable_set_num;
- solvable_set_poolstr;
- solvable_set_str;
- solvable_trivial_installable_map;
- solvable_trivial_installable_queue;
- solvable_trivial_installable_repo;
- solvable_unset;
- solver_allruleinfos;
- solver_alternative2str;
- solver_alternatives_count;
- solver_calc_duchanges;
- solver_calc_installsizechange;
- solver_calculate_multiversionmap;
- solver_calculate_noobsmap;
- solver_create;
- solver_create_state_maps;
- solver_create_transaction;
- solver_describe_decision;
- solver_describe_weakdep_decision;
- solver_disableproblem;
- solver_enableproblem;
- solver_findallproblemrules;
- solver_findproblemrule;
- solver_free;
- solver_freedupmaps;
- solver_get_alternative;
- solver_get_decisionblock;
- solver_get_decisionlevel;
- solver_get_decisionqueue;
- solver_get_flag;
- solver_get_lastdecisionblocklevel;
- solver_get_orphaned;
- solver_get_recommendations;
- solver_get_unneeded;
- solver_get_userinstalled;
- solver_next_problem;
- solver_next_solution;
- solver_next_solutionelement;
- solver_prepare_solutions;
- solver_printallsolutions;
- solver_printcompleteprobleminfo;
- solver_printdecisionq;
- solver_printdecisions;
- solver_printproblem;
- solver_printprobleminfo;
- solver_printproblemruleinfo;
- solver_printrule;
- solver_printruleclass;
- solver_printruleelement;
- solver_printsolution;
- solver_printtrivial;
- solver_printwatches;
- solver_problem2str;
- solver_problem_count;
- solver_problemruleinfo2str;
- solver_rule2job;
- solver_rule2jobidx;
- solver_rule2pkgrule;
- solver_rule2rules;
- solver_rule2solvable;
- solver_ruleclass;
- solver_ruleinfo;
- solver_ruleliterals;
- solver_rulecmp;
- solver_select2str;
- solver_set_flag;
- solver_solution_count;
- solver_solutionelement2str;
- solver_solutionelement_count;
- solver_solutionelement_internalid;
- solver_solutionelement_extrajobflags;
- solver_solve;
- solver_take_solution;
- solver_take_solutionelement;
- solver_trivial_installable;
- solver_unifyrules;
- stringpool_clone;
- stringpool_free;
- stringpool_freehash;
- stringpool_init;
- stringpool_init_empty;
- stringpool_shrink;
- stringpool_str2id;
- stringpool_strn2id;
- transaction_add_obsoleted;
- transaction_all_obs_pkgs;
- transaction_calc_duchanges;
- transaction_calc_installsizechange;
- transaction_check_order;
- transaction_classify;
- transaction_classify_pkgs;
- transaction_create;
- transaction_create_clone;
- transaction_create_decisionq;
- transaction_free;
- transaction_free_orderdata;
- transaction_installedresult;
- transaction_obs_pkg;
- transaction_order;
- transaction_order_add_choices;
- transaction_order_get_cycle;
- transaction_order_get_cycleids;
- transaction_print;
- transaction_type;
- local:
- *;
-};
+++ /dev/null
-/*
- * Copyright (c) 2013, SUSE Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * linkedpkg.c
- *
- * Linked packages are "pseudo" packages that are bound to real packages but
- * contain different information (name/summary/description). They are normally
- * somehow generated from the real packages, either when the repositories are
- * created or automatically from the packages by looking at the provides.
- *
- * We currently support:
- *
- * application:
- * created from AppStream appdata xml in the repository (which is generated
- * from files in /usr/share/appdata)
- *
- * product:
- * created from product data in the repository (which is generated from files
- * in /etc/products.d). In the future we may switch to using product()
- * provides of packages.
- *
- * pattern:
- * created from pattern() provides of packages.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "evr.h"
-#include "linkedpkg.h"
-
-#ifdef ENABLE_LINKED_PKGS
-
-void
-find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
-{
- Id req = 0;
- Id prv = 0;
- Id p, pp;
- Id pkgname = 0, appdataid = 0;
-
- /* find appdata requires */
- if (s->requires)
- {
- Id *reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0) /* go through all requires */
- {
- if (ISRELDEP(req))
- continue;
- if (!strncmp("appdata(", pool_id2str(pool, req), 8))
- appdataid = req;
- else
- pkgname = req;
- }
- }
- req = appdataid ? appdataid : pkgname;
- if (!req)
- return;
- /* find application-appdata provides */
- if (s->provides)
- {
- Id *prvp = s->repo->idarraydata + s->provides;
- const char *reqs = pool_id2str(pool, req);
- const char *prvs;
- while ((prv = *prvp++) != 0) /* go through all provides */
- {
- if (ISRELDEP(prv))
- continue;
- prvs = pool_id2str(pool, prv);
- if (strncmp("application-appdata(", prvs, 20))
- continue;
- if (appdataid)
- {
- if (!strcmp(prvs + 12, reqs))
- break;
- }
- else
- {
- int reqsl = strlen(reqs);
- if (!strncmp(prvs + 20, reqs, reqsl) && !strcmp(prvs + 20 + reqsl, ")"))
- break;
- }
- }
- }
- if (!prv)
- return; /* huh, no provides found? */
- /* now link em */
- FOR_PROVIDES(p, pp, req)
- if (pool->solvables[p].repo == s->repo)
- if (!pkgname || pool->solvables[p].name == pkgname)
- queue_push(qr, p);
- if (!qr->count && pkgname && appdataid)
- {
- /* huh, no matching package? try without pkgname filter */
- FOR_PROVIDES(p, pp, req)
- if (pool->solvables[p].repo == s->repo)
- queue_push(qr, p);
- }
- if (qp)
- {
- FOR_PROVIDES(p, pp, prv)
- if (pool->solvables[p].repo == s->repo)
- queue_push(qp, p);
- }
- if (reqidp)
- *reqidp = req;
- if (prvidp)
- *prvidp = prv;
-}
-
-void
-find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
-{
- Id p, pp, namerelid;
- char *str;
- unsigned int sbt = 0;
-
- /* search for project requires */
- namerelid = 0;
- if (s->requires)
- {
- Id req, *reqp = s->repo->idarraydata + s->requires;
- const char *nn = pool_id2str(pool, s->name);
- int nnl = strlen(nn);
- while ((req = *reqp++) != 0) /* go through all requires */
- if (ISRELDEP(req))
- {
- const char *rn;
- Reldep *rd = GETRELDEP(pool, req);
- if (rd->flags != REL_EQ || rd->evr != s->evr)
- continue;
- rn = pool_id2str(pool, rd->name);
- if (!strncmp(rn, "product(", 8) && !strncmp(rn + 8, nn + 8, nnl - 8) && !strcmp( rn + nnl, ")"))
- {
- namerelid = req;
- break;
- }
- }
- }
- if (!namerelid)
- {
- /* too bad. construct from scratch */
- str = pool_tmpjoin(pool, pool_id2str(pool, s->name), ")", 0);
- str[7] = '(';
- namerelid = pool_rel2id(pool, pool_str2id(pool, str, 1), s->evr, REL_EQ, 1);
- }
- FOR_PROVIDES(p, pp, namerelid)
- {
- Solvable *ps = pool->solvables + p;
- if (ps->repo != s->repo || ps->arch != s->arch)
- continue;
- queue_push(qr, p);
- }
- if (qr->count > 1)
- {
- /* multiple providers. try buildtime filter */
- sbt = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
- if (sbt)
- {
- unsigned int bt;
- int i, j;
- int filterqp = 1;
- for (i = j = 0; i < qr->count; i++)
- {
- bt = solvable_lookup_num(pool->solvables + qr->elements[i], SOLVABLE_BUILDTIME, 0);
- if (!bt)
- filterqp = 0; /* can't filter */
- if (!bt || bt == sbt)
- qr->elements[j++] = qr->elements[i];
- }
- if (j)
- qr->count = j;
- if (!j || !filterqp)
- sbt = 0; /* filter failed */
- }
- }
- if (!qr->count && s->repo == pool->installed)
- {
- /* oh no! Look up reference file */
- Dataiterator di;
- const char *refbasename = solvable_lookup_str(s, PRODUCT_REFERENCEFILE);
- dataiterator_init(&di, pool, s->repo, 0, SOLVABLE_FILELIST, refbasename, SEARCH_STRING);
- while (dataiterator_step(&di))
- queue_push(qr, di.solvid);
- dataiterator_free(&di);
- if (qp)
- {
- dataiterator_init(&di, pool, s->repo, 0, PRODUCT_REFERENCEFILE, refbasename, SEARCH_STRING);
- while (dataiterator_step(&di))
- queue_push(qp, di.solvid);
- dataiterator_free(&di);
- }
- }
- else if (qp)
- {
- /* find qp */
- FOR_PROVIDES(p, pp, s->name)
- {
- Solvable *ps = pool->solvables + p;
- if (s->name != ps->name || ps->repo != s->repo || ps->arch != s->arch || s->evr != ps->evr)
- continue;
- if (sbt && solvable_lookup_num(ps, SOLVABLE_BUILDTIME, 0) != sbt)
- continue;
- queue_push(qp, p);
- }
- }
- if (reqidp)
- *reqidp = namerelid;
- if (prvidp)
- *prvidp = solvable_selfprovidedep(s);
-}
-
-void
-find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
-{
- Id p, pp, *pr, apevr = 0, aprel = 0;
-
- /* check if autopattern */
- if (!s->provides)
- return;
- for (pr = s->repo->idarraydata + s->provides; (p = *pr++) != 0; )
- if (ISRELDEP(p))
- {
- Reldep *rd = GETRELDEP(pool, p);
- if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
- {
- aprel = p;
- apevr = rd->evr;
- break;
- }
- }
- if (!apevr)
- return;
- FOR_PROVIDES(p, pp, apevr)
- {
- Solvable *s2 = pool->solvables + p;
- if (s2->repo == s->repo && s2->name == apevr && s2->evr == s->evr && s2->vendor == s->vendor)
- queue_push(qr, p);
- }
- if (qp)
- {
- FOR_PROVIDES(p, pp, aprel)
- {
- Solvable *s2 = pool->solvables + p;
- if (s2->repo == s->repo && s2->evr == s->evr && s2->vendor == s->vendor)
- queue_push(qp, p);
- }
- }
- if (reqidp)
- *reqidp = apevr;
- if (prvidp)
- *prvidp = aprel;
-}
-
-/* the following two functions are used in solvable_lookup_str_base to do
- * translated lookups on the product/pattern packages
- */
-Id
-find_autopattern_name(Pool *pool, Solvable *s)
-{
- Id prv, *prvp;
- if (!s->provides)
- return 0;
- for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
- if (ISRELDEP(prv))
- {
- Reldep *rd = GETRELDEP(pool, prv);
- if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
- return strncmp(pool_id2str(pool, rd->evr), "pattern:", 8) != 0 ? rd->evr : 0;
- }
- return 0;
-}
-
-Id
-find_autoproduct_name(Pool *pool, Solvable *s)
-{
- Id prv, *prvp;
- if (!s->provides)
- return 0;
- for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
- if (ISRELDEP(prv))
- {
- Reldep *rd = GETRELDEP(pool, prv);
- if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autoproduct()"))
- return strncmp(pool_id2str(pool, rd->evr), "product:", 8) != 0 ? rd->evr : 0;
- }
- return 0;
-}
-
-void
-find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
-{
- const char *name = pool_id2str(pool, s->name);
- if (name[0] == 'a' && !strncmp("application:", name, 12))
- find_application_link(pool, s, reqidp, qr, prvidp, qp);
- else if (name[0] == 'p' && !strncmp("pattern:", name, 7))
- find_pattern_link(pool, s, reqidp, qr, prvidp, qp);
- else if (name[0] == 'p' && !strncmp("product:", name, 8))
- find_product_link(pool, s, reqidp, qr, prvidp, qp);
-}
-
-static int
-name_min_max(Pool *pool, Solvable *s, Id *namep, Id *minp, Id *maxp)
-{
- Queue q;
- Id qbuf[4];
- Id name, min, max;
- int i;
-
- queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
- find_package_link(pool, s, 0, &q, 0, 0);
- if (!q.count)
- {
- queue_free(&q);
- return 0;
- }
- s = pool->solvables + q.elements[0];
- name = s->name;
- min = max = s->evr;
- for (i = 1; i < q.count; i++)
- {
- s = pool->solvables + q.elements[i];
- if (s->name != name)
- {
- queue_free(&q);
- return 0;
- }
- if (s->evr == min || s->evr == max)
- continue;
- if (pool_evrcmp(pool, min, s->evr, EVRCMP_COMPARE) >= 0)
- min = s->evr;
- else if (min == max || pool_evrcmp(pool, max, s->evr, EVRCMP_COMPARE) <= 0)
- max = s->evr;
- }
- queue_free(&q);
- *namep = name;
- *minp = min;
- *maxp = max;
- return 1;
-}
-
-int
-pool_link_evrcmp(Pool *pool, Solvable *s1, Solvable *s2)
-{
- Id name1, evrmin1, evrmax1;
- Id name2, evrmin2, evrmax2;
-
- if (s1->name != s2->name)
- return 0; /* can't compare */
- if (!name_min_max(pool, s1, &name1, &evrmin1, &evrmax1))
- return 0;
- if (!name_min_max(pool, s2, &name2, &evrmin2, &evrmax2))
- return 0;
- /* compare linked names */
- if (name1 != name2)
- return 0;
- if (evrmin1 == evrmin2 && evrmax1 == evrmax2)
- return 0;
- /* now compare evr intervals */
- if (evrmin1 == evrmax1 && evrmin2 == evrmax2)
- return pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE);
- if (evrmin1 != evrmax2 && pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE) > 0)
- return 1;
- if (evrmax1 != evrmin2 && pool_evrcmp(pool, evrmax1, evrmin2, EVRCMP_COMPARE) < 0)
- return -1;
- return 0;
-}
-
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * linkedpkg.h (internal)
- */
-
-#ifndef LIBSOLV_LINKEDPKG_H
-#define LIBSOLV_LINKEDPKG_H
-
-static inline int
-has_package_link(Pool *pool, Solvable *s)
-{
- const char *name = pool_id2str(pool, s->name);
- if (name[0] == 'a' && !strncmp("application:", name, 12))
- return 1;
- if (name[0] == 'p' && !strncmp("pattern:", name, 7))
- return 1;
- if (name[0] == 'p' && !strncmp("product:", name, 8))
- return 1;
- return 0;
-}
-
-extern void find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
-extern void find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
-extern void find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
-
-extern Id find_autopattern_name(Pool *pool, Solvable *s);
-extern Id find_autoproduct_name(Pool *pool, Solvable *s);
-
-/* generic */
-extern void find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
-extern int pool_link_evrcmp(Pool *pool, Solvable *s1, Solvable *s2);
-
-#endif
+++ /dev/null
-/*
- * This is an OpenSSL-compatible implementation of the RSA Data Security,
- * Inc. MD5 Message-Digest Algorithm.
- *
- * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
- * the public domain.
- *
- * This differs from Colin Plumb's older public domain implementation in
- * that no 32-bit integer data type is required, there's no compile-time
- * endianness configuration, and the function prototypes match OpenSSL's.
- * The primary goals are portability and ease of use.
- *
- * This implementation is meant to be fast, but not as fast as possible.
- * Some known optimizations are not included to reduce source code size
- * and avoid compile-time configuration.
- */
-
-#include <string.h>
-#include "md5.h"
-
-
-/*
- * The basic MD5 functions.
- *
- * F is optimized compared to its RFC 1321 definition just like in Colin
- * Plumb's implementation.
- */
-#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
-#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define I(x, y, z) ((y) ^ ((x) | ~(z)))
-
-/*
- * The MD5 transformation for all four rounds.
- */
-#define STEP(f, a, b, c, d, x, t, s) \
- (a) += f((b), (c), (d)) + (x) + (t); \
- (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
- (a) += (b);
-
-/*
- * SET reads 4 input bytes in little-endian byte order and stores them
- * in a properly aligned word in host byte order.
- *
- * The check for little-endian architectures which tolerate unaligned
- * memory accesses is just an optimization. Nothing will break if it
- * doesn't work.
- */
-#if defined(__i386__) || defined(__vax__)
-#define SET(n) \
- (*(MD5_u32plus *)&ptr[(n) * 4])
-#define GET(n) \
- SET(n)
-#else
-#define SET(n) \
- (ctx->block[(n)] = \
- (MD5_u32plus)ptr[(n) * 4] | \
- ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
- ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
- ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
-#define GET(n) \
- (ctx->block[(n)])
-#endif
-
-/*
- * This processes one or more 64-byte data blocks, but does NOT update
- * the bit counters. There're no alignment requirements.
- */
-static void *body(MD5_CTX *ctx, void *data, unsigned long size)
-{
- unsigned char *ptr;
- MD5_u32plus a, b, c, d;
- MD5_u32plus saved_a, saved_b, saved_c, saved_d;
-
- ptr = data;
-
- a = ctx->a;
- b = ctx->b;
- c = ctx->c;
- d = ctx->d;
-
- do {
- saved_a = a;
- saved_b = b;
- saved_c = c;
- saved_d = d;
-
-/* Round 1 */
- STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
- STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
- STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
- STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
- STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
- STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
- STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
- STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
- STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
- STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
- STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
- STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
- STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
- STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
- STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
- STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
-
-/* Round 2 */
- STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
- STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
- STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
- STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
- STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
- STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
- STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
- STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
- STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
- STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
- STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
- STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
- STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
- STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
- STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
- STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
-
-/* Round 3 */
- STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
- STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
- STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
- STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
- STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
- STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
- STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
- STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
- STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
- STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
- STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
- STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
- STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
- STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
- STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
- STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
-
-/* Round 4 */
- STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
- STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
- STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
- STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
- STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
- STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
- STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
- STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
- STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
- STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
- STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
- STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
- STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
- STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
- STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
- STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
-
- a += saved_a;
- b += saved_b;
- c += saved_c;
- d += saved_d;
-
- ptr += 64;
- } while (size -= 64);
-
- ctx->a = a;
- ctx->b = b;
- ctx->c = c;
- ctx->d = d;
-
- return ptr;
-}
-
-void solv_MD5_Init(MD5_CTX *ctx)
-{
- ctx->a = 0x67452301;
- ctx->b = 0xefcdab89;
- ctx->c = 0x98badcfe;
- ctx->d = 0x10325476;
-
- ctx->lo = 0;
- ctx->hi = 0;
-}
-
-void solv_MD5_Update(MD5_CTX *ctx, void *data, unsigned long size)
-{
- MD5_u32plus saved_lo;
- unsigned long used, free;
-
- saved_lo = ctx->lo;
- if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
- ctx->hi++;
- ctx->hi += size >> 29;
-
- used = saved_lo & 0x3f;
-
- if (used) {
- free = 64 - used;
-
- if (size < free) {
- memcpy(&ctx->buffer[used], data, size);
- return;
- }
-
- memcpy(&ctx->buffer[used], data, free);
- data = (unsigned char *)data + free;
- size -= free;
- body(ctx, ctx->buffer, 64);
- }
-
- if (size >= 64) {
- data = body(ctx, data, size & ~(unsigned long)0x3f);
- size &= 0x3f;
- }
-
- memcpy(ctx->buffer, data, size);
-}
-
-void solv_MD5_Final(unsigned char *result, MD5_CTX *ctx)
-{
- unsigned long used, free;
-
- used = ctx->lo & 0x3f;
-
- ctx->buffer[used++] = 0x80;
-
- free = 64 - used;
-
- if (free < 8) {
- memset(&ctx->buffer[used], 0, free);
- body(ctx, ctx->buffer, 64);
- used = 0;
- free = 64;
- }
-
- memset(&ctx->buffer[used], 0, free - 8);
-
- ctx->lo <<= 3;
- ctx->buffer[56] = ctx->lo;
- ctx->buffer[57] = ctx->lo >> 8;
- ctx->buffer[58] = ctx->lo >> 16;
- ctx->buffer[59] = ctx->lo >> 24;
- ctx->buffer[60] = ctx->hi;
- ctx->buffer[61] = ctx->hi >> 8;
- ctx->buffer[62] = ctx->hi >> 16;
- ctx->buffer[63] = ctx->hi >> 24;
-
- body(ctx, ctx->buffer, 64);
-
- result[0] = ctx->a;
- result[1] = ctx->a >> 8;
- result[2] = ctx->a >> 16;
- result[3] = ctx->a >> 24;
- result[4] = ctx->b;
- result[5] = ctx->b >> 8;
- result[6] = ctx->b >> 16;
- result[7] = ctx->b >> 24;
- result[8] = ctx->c;
- result[9] = ctx->c >> 8;
- result[10] = ctx->c >> 16;
- result[11] = ctx->c >> 24;
- result[12] = ctx->d;
- result[13] = ctx->d >> 8;
- result[14] = ctx->d >> 16;
- result[15] = ctx->d >> 24;
-
- memset(ctx, 0, sizeof(*ctx));
-}
+++ /dev/null
-/*
- * This is an OpenSSL-compatible implementation of the RSA Data Security,
- * Inc. MD5 Message-Digest Algorithm.
- *
- * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
- * the public domain. See md5.c for more information.
- */
-
-/* Any 32-bit or wider unsigned integer data type will do */
-typedef unsigned long MD5_u32plus;
-
-typedef struct {
- MD5_u32plus lo, hi;
- MD5_u32plus a, b, c, d;
- unsigned char buffer[64];
- MD5_u32plus block[16];
-} MD5_CTX;
-
-extern void solv_MD5_Init(MD5_CTX *ctx);
-extern void solv_MD5_Update(MD5_CTX *ctx, void *data, unsigned long size);
-extern void solv_MD5_Final(unsigned char *result, MD5_CTX *ctx);
+++ /dev/null
-/*
- * Copyright (c) 2007-2015, SUSE LLC
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * order.c
- *
- * Transaction ordering
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-
-#include "transaction.h"
-#include "bitmap.h"
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-
-struct _TransactionElement {
- Id p; /* solvable id */
- Id edges; /* pointer into edges data */
- Id mark;
-};
-
-struct _TransactionOrderdata {
- struct _TransactionElement *tes;
- int ntes;
- Id *invedgedata;
- int ninvedgedata;
- Queue *cycles;
-};
-
-#define TYPE_BROKEN (1<<0)
-#define TYPE_CON (1<<1)
-
-#define TYPE_REQ_P (1<<2)
-#define TYPE_PREREQ_P (1<<3)
-
-#define TYPE_REQ (1<<4)
-#define TYPE_PREREQ (1<<5)
-
-#define TYPE_CYCLETAIL (1<<16)
-#define TYPE_CYCLEHEAD (1<<17)
-
-#define EDGEDATA_BLOCK 127
-
-void
-transaction_clone_orderdata(Transaction *trans, Transaction *srctrans)
-{
- struct _TransactionOrderdata *od = srctrans->orderdata;
- if (!od)
- return;
- trans->orderdata = solv_calloc(1, sizeof(*trans->orderdata));
- trans->orderdata->tes = solv_memdup2(od->tes, od->ntes, sizeof(*od->tes));
- trans->orderdata->ntes = od->ntes;
- trans->orderdata->invedgedata = solv_memdup2(od->invedgedata, od->ninvedgedata, sizeof(Id));
- trans->orderdata->ninvedgedata = od->ninvedgedata;
- if (od->cycles)
- {
- trans->orderdata->cycles = solv_calloc(1, sizeof(Queue));
- queue_init_clone(trans->orderdata->cycles, od->cycles);
- }
-}
-
-void
-transaction_free_orderdata(Transaction *trans)
-{
- if (trans->orderdata)
- {
- struct _TransactionOrderdata *od = trans->orderdata;
- od->tes = solv_free(od->tes);
- od->invedgedata = solv_free(od->invedgedata);
- if (od->cycles)
- {
- queue_free(od->cycles);
- od->cycles = solv_free(od->cycles);
- }
- trans->orderdata = solv_free(trans->orderdata);
- }
-}
-
-struct orderdata {
- Transaction *trans;
- struct _TransactionElement *tes;
- int ntes;
- Id *edgedata;
- int nedgedata;
- Id *invedgedata;
-
- Queue cycles;
- Queue cyclesdata;
- int ncycles;
-};
-
-static int
-addteedge(struct orderdata *od, int from, int to, int type)
-{
- int i;
- struct _TransactionElement *te;
-
- if (from == to)
- return 0;
-
- /* printf("edge %d(%s) -> %d(%s) type %x\n", from, pool_solvid2str(pool, od->tes[from].p), to, pool_solvid2str(pool, od->tes[to].p), type); */
-
- te = od->tes + from;
- for (i = te->edges; od->edgedata[i]; i += 2)
- if (od->edgedata[i] == to)
- break;
- /* test of brokenness */
- if (type == TYPE_BROKEN)
- return od->edgedata[i] && (od->edgedata[i + 1] & TYPE_BROKEN) != 0 ? 1 : 0;
- if (od->edgedata[i])
- {
- od->edgedata[i + 1] |= type;
- return 0;
- }
- if (i + 1 == od->nedgedata)
- {
- /* printf("tail add %d\n", i - te->edges); */
- if (!i)
- te->edges = ++i;
- od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3, sizeof(Id), EDGEDATA_BLOCK);
- }
- else
- {
- /* printf("extend %d\n", i - te->edges); */
- od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3 + (i - te->edges), sizeof(Id), EDGEDATA_BLOCK);
- if (i > te->edges)
- memcpy(od->edgedata + od->nedgedata, od->edgedata + te->edges, sizeof(Id) * (i - te->edges));
- i = od->nedgedata + (i - te->edges);
- te->edges = od->nedgedata;
- }
- od->edgedata[i] = to;
- od->edgedata[i + 1] = type;
- od->edgedata[i + 2] = 0; /* end marker */
- od->nedgedata = i + 3;
- return 0;
-}
-
-static int
-addedge(struct orderdata *od, Id from, Id to, int type)
-{
- Transaction *trans = od->trans;
- Pool *pool = trans->pool;
- Solvable *s;
- struct _TransactionElement *te;
- int i;
-
- /* printf("addedge %d %d type %d\n", from, to, type); */
- s = pool->solvables + from;
- if (s->repo == pool->installed && trans->transaction_installed[from - pool->installed->start])
- {
- /* obsolete, map to install */
- if (trans->transaction_installed[from - pool->installed->start] > 0)
- from = trans->transaction_installed[from - pool->installed->start];
- else
- {
- int ret = 0;
- Queue ti;
- Id tibuf[5];
-
- queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
- transaction_all_obs_pkgs(trans, from, &ti);
- for (i = 0; i < ti.count; i++)
- ret |= addedge(od, ti.elements[i], to, type);
- queue_free(&ti);
- return ret;
- }
- }
- s = pool->solvables + to;
- if (s->repo == pool->installed && trans->transaction_installed[to - pool->installed->start])
- {
- /* obsolete, map to install */
- if (trans->transaction_installed[to - pool->installed->start] > 0)
- to = trans->transaction_installed[to - pool->installed->start];
- else
- {
- int ret = 0;
- Queue ti;
- Id tibuf[5];
-
- queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
- transaction_all_obs_pkgs(trans, to, &ti);
- for (i = 0; i < ti.count; i++)
- ret |= addedge(od, from, ti.elements[i], type);
- queue_free(&ti);
- return ret;
- }
- }
-
- /* map from/to to te numbers */
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- if (te->p == to)
- break;
- if (i == od->ntes)
- return 0;
- to = i;
-
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- if (te->p == from)
- break;
- if (i == od->ntes)
- return 0;
-
- return addteedge(od, i, to, type);
-}
-
-static inline int
-havescripts(Pool *pool, Id solvid)
-{
- Solvable *s = pool->solvables + solvid;
- const char *dep;
- if (s->requires)
- {
- Id req, *reqp;
- int inpre = 0;
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- {
- inpre = 1;
- continue;
- }
- if (!inpre)
- continue;
- dep = pool_id2str(pool, req);
- if (*dep == '/' && strcmp(dep, "/sbin/ldconfig") != 0)
- return 1;
- }
- }
- return 0;
-}
-
-static void
-addsolvableedges(struct orderdata *od, Solvable *s)
-{
- Transaction *trans = od->trans;
- Pool *pool = trans->pool;
- Id req, *reqp, con, *conp;
- Id p, p2, pp2;
- int i, j, pre, numins;
- Repo *installed = pool->installed;
- Solvable *s2;
- Queue reqq;
- int provbyinst;
-
-#if 0
- printf("addsolvableedges %s\n", pool_solvable2str(pool, s));
-#endif
- p = s - pool->solvables;
- queue_init(&reqq);
- if (s->requires)
- {
- reqp = s->repo->idarraydata + s->requires;
- pre = TYPE_REQ;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- {
- pre = TYPE_PREREQ;
- continue;
- }
-#if 0
- if (pre != TYPE_PREREQ && installed && s->repo == installed)
- {
- /* ignore normal requires if we're getting obsoleted */
- if (trans->transaction_installed[p - pool->installed->start])
- continue;
- }
-#endif
- queue_empty(&reqq);
- numins = 0; /* number of packages to be installed providing it */
- provbyinst = 0; /* provided by kept package */
- FOR_PROVIDES(p2, pp2, req)
- {
- s2 = pool->solvables + p2;
- if (p2 == p)
- {
- reqq.count = 0; /* self provides */
- break;
- }
- if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
- {
- provbyinst = 1;
-#if 0
- printf("IGNORE inst provides %s by %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s2));
- reqq.count = 0; /* provided by package that stays installed */
- break;
-#else
- continue;
-#endif
- }
- if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
- continue; /* package stays uninstalled */
-
- if (s->repo == installed)
- {
- /* s gets uninstalled */
- queue_pushunique(&reqq, p2);
- if (s2->repo != installed)
- numins++;
- }
- else
- {
- if (s2->repo == installed)
- continue; /* s2 gets uninstalled */
- queue_pushunique(&reqq, p2);
- }
- }
- if (provbyinst)
- {
- /* prune to harmless ->inst edges */
- for (i = j = 0; i < reqq.count; i++)
- if (pool->solvables[reqq.elements[i]].repo != installed)
- reqq.elements[j++] = reqq.elements[i];
- reqq.count = j;
- }
-
- if (numins && reqq.count)
- {
- if (s->repo == installed)
- {
- for (i = 0; i < reqq.count; i++)
- {
- if (pool->solvables[reqq.elements[i]].repo == installed)
- {
- for (j = 0; j < reqq.count; j++)
- {
- if (pool->solvables[reqq.elements[j]].repo != installed)
- {
- if (trans->transaction_installed[reqq.elements[i] - pool->installed->start] == reqq.elements[j])
- continue; /* no self edge */
-#if 0
- printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, reqq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, reqq.elements[j]));
-#endif
- addedge(od, reqq.elements[i], reqq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
- }
- }
- }
- }
- }
- /* no mixed types, remove all deps on uninstalls */
- for (i = j = 0; i < reqq.count; i++)
- if (pool->solvables[reqq.elements[i]].repo != installed)
- reqq.elements[j++] = reqq.elements[i];
- reqq.count = j;
- }
- if (!reqq.count)
- continue;
- for (i = 0; i < reqq.count; i++)
- {
- p2 = reqq.elements[i];
- if (pool->solvables[p2].repo != installed)
- {
- /* all elements of reqq are installs, thus have different TEs */
- if (pool->solvables[p].repo != installed)
- {
-#if 0
- printf("add inst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
- addedge(od, p, p2, pre);
- }
- else
- {
-#if 0
- printf("add uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
- addedge(od, p, p2, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
- }
- }
- else
- {
- if (s->repo != installed)
- continue; /* no inst->uninst edges, please! */
-
- /* uninst -> uninst edge. Those make trouble. Only add if we must */
- if (trans->transaction_installed[p - installed->start] && !havescripts(pool, p))
- {
- /* p is obsoleted by another package and has no scripts */
- /* we assume that the obsoletor is good enough to replace p */
- continue;
- }
-#if 0
- printf("add uninst->uninst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
- addedge(od, p2, p, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
- }
- }
- }
- }
- if (s->conflicts)
- {
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- FOR_PROVIDES(p2, pp2, con)
- {
- if (p2 == p)
- continue;
- s2 = pool->solvables + p2;
- if (!s2->repo)
- continue;
- if (s->repo == installed)
- {
- if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
- {
- /* deinstall p before installing p2 */
-#if 0
- printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, con), pool_solvid2str(pool, p));
-#endif
- addedge(od, p2, p, TYPE_CON);
- }
- }
- else
- {
- if (s2->repo == installed && MAPTST(&trans->transactsmap, p2))
- {
- /* deinstall p2 before installing p */
-#if 0
- printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, con), pool_solvid2str(pool, p2));
-#endif
- addedge(od, p, p2, TYPE_CON);
- }
- }
-
- }
- }
- }
- if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &reqq) && reqq.count)
- {
- /* we're getting deinstalled/updated. Try to do this before our
- * triggers are hit */
- for (i = 0; i < reqq.count; i++)
- {
- Id tri = reqq.elements[i];
- FOR_PROVIDES(p2, pp2, tri)
- {
- if (p2 == p)
- continue;
- s2 = pool->solvables + p2;
- if (!s2->repo)
- continue;
- if (s2->name == s->name)
- continue; /* obsoleted anyway */
- if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
- {
- /* deinstall/update p before installing p2 */
-#if 0
- printf("add trigger uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, tri), pool_solvid2str(pool, p));
-#endif
- addedge(od, p2, p, TYPE_CON);
- }
- }
- }
- }
- queue_free(&reqq);
-}
-
-
-/* break an edge in a cycle */
-static void
-breakcycle(struct orderdata *od, Id *cycle)
-{
- Pool *pool = od->trans->pool;
- Id ddegmin, ddegmax, ddeg;
- int k, l;
- struct _TransactionElement *te;
-
- l = 0;
- ddegmin = ddegmax = 0;
- for (k = 0; cycle[k + 1]; k += 2)
- {
- ddeg = od->edgedata[cycle[k + 1] + 1];
- if (ddeg > ddegmax)
- ddegmax = ddeg;
- if (!k || ddeg < ddegmin)
- {
- l = k;
- ddegmin = ddeg;
- continue;
- }
- if (ddeg == ddegmin)
- {
- if (havescripts(pool, od->tes[cycle[l]].p) && !havescripts(pool, od->tes[cycle[k]].p))
- {
- /* prefer k, as l comes from a package with contains scriptlets */
- l = k;
- continue;
- }
- /* same edge value, check for prereq */
- }
- }
-
- /* record brkoen cycle starting with the tail */
- queue_push(&od->cycles, od->cyclesdata.count); /* offset into data */
- queue_push(&od->cycles, k / 2); /* cycle elements */
- queue_push(&od->cycles, od->edgedata[cycle[l + 1] + 1]); /* broken edge */
- queue_push(&od->cycles, (ddegmax << 16) | ddegmin); /* max/min values */
- od->ncycles++;
- for (k = l;;)
- {
- k += 2;
- if (!cycle[k + 1])
- k = 0;
- queue_push(&od->cyclesdata, cycle[k]);
- if (k == l)
- break;
- }
- queue_push(&od->cyclesdata, 0); /* mark end */
-
- /* break that edge */
- od->edgedata[cycle[l + 1] + 1] |= TYPE_BROKEN;
-
-#if 1
- if (ddegmin < TYPE_REQ)
- return;
-#endif
-
- /* cycle recorded, print it */
- if (ddegmin >= TYPE_REQ && (ddegmax & TYPE_PREREQ) != 0)
- POOL_DEBUG(SOLV_DEBUG_STATS, "CRITICAL ");
- POOL_DEBUG(SOLV_DEBUG_STATS, "cycle: --> ");
- for (k = 0; cycle[k + 1]; k += 2)
- {
- te = od->tes + cycle[k];
- if ((od->edgedata[cycle[k + 1] + 1] & TYPE_BROKEN) != 0)
- POOL_DEBUG(SOLV_DEBUG_STATS, "%s ##%x##> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
- else
- POOL_DEBUG(SOLV_DEBUG_STATS, "%s --%x--> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "\n");
-}
-
-static inline void
-dump_tes(struct orderdata *od)
-{
- Pool *pool = od->trans->pool;
- int i, j;
- Queue obsq;
- struct _TransactionElement *te, *te2;
-
- queue_init(&obsq);
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- {
- Solvable *s = pool->solvables + te->p;
- POOL_DEBUG(SOLV_DEBUG_RESULT, "TE %4d: %c%s\n", i, s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
- if (s->repo != pool->installed)
- {
- queue_empty(&obsq);
- transaction_all_obs_pkgs(od->trans, te->p, &obsq);
- for (j = 0; j < obsq.count; j++)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " -%s\n", pool_solvid2str(pool, obsq.elements[j]));
- }
- for (j = te->edges; od->edgedata[j]; j += 2)
- {
- te2 = od->tes + od->edgedata[j];
- if ((od->edgedata[j + 1] & TYPE_BROKEN) == 0)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " --%x--> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
- else
- POOL_DEBUG(SOLV_DEBUG_RESULT, " ##%x##> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
- }
- }
-}
-
-static void
-reachable(struct orderdata *od, Id i)
-{
- struct _TransactionElement *te = od->tes + i;
- int j, k;
-
- if (te->mark != 0)
- return;
- te->mark = 1;
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (!od->tes[k].mark)
- reachable(od, k);
- if (od->tes[k].mark == 2)
- {
- te->mark = 2;
- return;
- }
- }
- te->mark = -1;
-}
-
-static void
-addcycleedges(struct orderdata *od, Id *cycle, Queue *todo)
-{
-#if 0
- Transaction *trans = od->trans;
- Pool *pool = trans->pool;
-#endif
- struct _TransactionElement *te;
- int i, j, k, tail;
- int head;
-
-#if 0
- printf("addcycleedges\n");
- for (i = 0; (j = cycle[i]) != 0; i++)
- printf("cycle %s\n", pool_solvid2str(pool, od->tes[j].p));
-#endif
-
- /* first add all the tail cycle edges */
-
- /* see what we can reach from the cycle */
- queue_empty(todo);
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- te->mark = 0;
- for (i = 0; (j = cycle[i]) != 0; i++)
- {
- od->tes[j].mark = -1;
- queue_push(todo, j);
- }
- while (todo->count)
- {
- i = queue_pop(todo);
- te = od->tes + i;
- if (te->mark > 0)
- continue;
- te->mark = te->mark < 0 ? 2 : 1;
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (od->tes[k].mark > 0)
- continue; /* no need to visit again */
- queue_push(todo, k);
- }
- }
- /* now all cycle TEs are marked with 2, all TEs reachable
- * from the cycle are marked with 1 */
- tail = cycle[0];
- od->tes[tail].mark = 1; /* no need to add edges */
-
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- {
- if (te->mark)
- continue; /* reachable from cycle */
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (od->tes[k].mark != 2)
- continue;
- /* We found an edge to the cycle. Add an extra edge to the tail */
- /* the TE was not reachable, so we're not creating a new cycle! */
-#if 0
- printf("adding TO TAIL cycle edge %d->%d %s->%s!\n", i, tail, pool_solvid2str(pool, od->tes[i].p), pool_solvid2str(pool, od->tes[tail].p));
-#endif
- j -= te->edges; /* in case we move */
- addteedge(od, i, tail, TYPE_CYCLETAIL);
- j += te->edges;
- break; /* one edge is enough */
- }
- }
-
- /* now add all head cycle edges */
-
- /* reset marks */
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- te->mark = 0;
- head = 0;
- for (i = 0; (j = cycle[i]) != 0; i++)
- {
- head = j;
- od->tes[j].mark = 2;
- }
- /* first the head to save some time */
- te = od->tes + head;
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (!od->tes[k].mark)
- reachable(od, k);
- if (od->tes[k].mark == -1)
- od->tes[k].mark = -2; /* no need for another edge */
- }
- for (i = 0; cycle[i] != 0; i++)
- {
- if (cycle[i] == head)
- break;
- te = od->tes + cycle[i];
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- /* see if we can reach a cycle TE from k */
- if (!od->tes[k].mark)
- reachable(od, k);
- if (od->tes[k].mark == -1)
- {
-#if 0
- printf("adding FROM HEAD cycle edge %d->%d %s->%s [%s]!\n", head, k, pool_solvid2str(pool, od->tes[head].p), pool_solvid2str(pool, od->tes[k].p), pool_solvid2str(pool, od->tes[cycle[i]].p));
-#endif
- addteedge(od, head, k, TYPE_CYCLEHEAD);
- od->tes[k].mark = -2; /* no need to add that one again */
- }
- }
- }
-}
-
-void
-transaction_order(Transaction *trans, int flags)
-{
- Pool *pool = trans->pool;
- Queue *tr = &trans->steps;
- Repo *installed = pool->installed;
- Id p;
- Solvable *s;
- int i, j, k, numte, numedge;
- struct orderdata od;
- struct _TransactionElement *te;
- Queue todo, obsq, samerepoq, uninstq;
- int cycstart, cycel;
- Id *cycle;
- int oldcount;
- int start, now;
- Repo *lastrepo;
- int lastmedia;
- Id *temedianr;
-
- start = now = solv_timems(0);
- POOL_DEBUG(SOLV_DEBUG_STATS, "ordering transaction\n");
- /* free old data if present */
- if (trans->orderdata)
- {
- struct _TransactionOrderdata *od = trans->orderdata;
- od->tes = solv_free(od->tes);
- od->invedgedata = solv_free(od->invedgedata);
- trans->orderdata = solv_free(trans->orderdata);
- }
-
- /* create a transaction element for every active component */
- numte = 0;
- for (i = 0; i < tr->count; i++)
- {
- p = tr->elements[i];
- s = pool->solvables + p;
- if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
- continue;
- numte++;
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "transaction elements: %d\n", numte);
- if (!numte)
- return; /* nothing to do... */
-
- numte++; /* leave first one zero */
- memset(&od, 0, sizeof(od));
- od.trans = trans;
- od.ntes = numte;
- od.tes = solv_calloc(numte, sizeof(*od.tes));
- od.edgedata = solv_extend(0, 0, 1, sizeof(Id), EDGEDATA_BLOCK);
- od.edgedata[0] = 0;
- od.nedgedata = 1;
- queue_init(&od.cycles);
-
- /* initialize TEs */
- for (i = 0, te = od.tes + 1; i < tr->count; i++)
- {
- p = tr->elements[i];
- s = pool->solvables + p;
- if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
- continue;
- te->p = p;
- te++;
- }
-
- /* create dependency graph */
- for (i = 0; i < tr->count; i++)
- addsolvableedges(&od, pool->solvables + tr->elements[i]);
-
- /* count edges */
- numedge = 0;
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- for (j = te->edges; od.edgedata[j]; j += 2)
- numedge++;
- POOL_DEBUG(SOLV_DEBUG_STATS, "edges: %d, edge space: %d\n", numedge, od.nedgedata / 2);
- POOL_DEBUG(SOLV_DEBUG_STATS, "edge creation took %d ms\n", solv_timems(now));
-
-#if 0
- dump_tes(&od);
-#endif
-
- now = solv_timems(0);
- /* kill all cycles */
- queue_init(&todo);
- for (i = numte - 1; i > 0; i--)
- queue_push(&todo, i);
-
- while (todo.count)
- {
- i = queue_pop(&todo);
- /* printf("- look at TE %d\n", i); */
- if (i < 0)
- {
- i = -i;
- od.tes[i].mark = 2; /* done with that one */
- continue;
- }
- te = od.tes + i;
- if (te->mark == 2)
- continue; /* already finished before */
- if (te->mark == 0)
- {
- int edgestovisit = 0;
- /* new node, visit edges */
- for (j = te->edges; (k = od.edgedata[j]) != 0; j += 2)
- {
- if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (od.tes[k].mark == 2)
- continue; /* no need to visit again */
- if (!edgestovisit++)
- queue_push(&todo, -i); /* end of edges marker */
- queue_push(&todo, k);
- }
- if (!edgestovisit)
- te->mark = 2; /* no edges, done with that one */
- else
- te->mark = 1; /* under investigation */
- continue;
- }
- /* oh no, we found a cycle */
- /* find start of cycle node (<0) */
- for (j = todo.count - 1; j >= 0; j--)
- if (todo.elements[j] == -i)
- break;
- assert(j >= 0);
- cycstart = j;
- /* build te/edge chain */
- k = cycstart;
- for (j = k; j < todo.count; j++)
- if (todo.elements[j] < 0)
- todo.elements[k++] = -todo.elements[j];
- cycel = k - cycstart;
- assert(cycel > 1);
- /* make room for edges, two extra element for cycle loop + terminating 0 */
- while (todo.count < cycstart + 2 * cycel + 2)
- queue_push(&todo, 0);
- cycle = todo.elements + cycstart;
- cycle[cycel] = i; /* close the loop */
- cycle[2 * cycel + 1] = 0; /* terminator */
- for (k = cycel; k > 0; k--)
- {
- cycle[k * 2] = cycle[k];
- te = od.tes + cycle[k - 1];
- assert(te->mark == 1);
- te->mark = 0; /* reset investigation marker */
- /* printf("searching for edge from %d to %d\n", cycle[k - 1], cycle[k]); */
- for (j = te->edges; od.edgedata[j]; j += 2)
- if (od.edgedata[j] == cycle[k])
- break;
- assert(od.edgedata[j]);
- cycle[k * 2 - 1] = j;
- }
- /* now cycle looks like this: */
- /* te1 edge te2 edge te3 ... teN edge te1 0 */
- breakcycle(&od, cycle);
- /* restart with start of cycle */
- todo.count = cycstart + 1;
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "cycles broken: %d\n", od.ncycles);
- POOL_DEBUG(SOLV_DEBUG_STATS, "cycle breaking took %d ms\n", solv_timems(now));
-
- now = solv_timems(0);
- /* now go through all broken cycles and create cycle edges to help
- the ordering */
- for (i = od.cycles.count - 4; i >= 0; i -= 4)
- {
- if (od.cycles.elements[i + 2] >= TYPE_REQ)
- addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
- }
- for (i = od.cycles.count - 4; i >= 0; i -= 4)
- {
- if (od.cycles.elements[i + 2] < TYPE_REQ)
- addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "cycle edge creation took %d ms\n", solv_timems(now));
-
-#if 0
- dump_tes(&od);
-#endif
- /* all edges are finally set up and there are no cycles, now the easy part.
- * Create an ordered transaction */
- now = solv_timems(0);
- /* first invert all edges */
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- te->mark = 1; /* term 0 */
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- {
- for (j = te->edges; od.edgedata[j]; j += 2)
- {
- if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- od.tes[od.edgedata[j]].mark++;
- }
- }
- j = 1;
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- {
- te->mark += j;
- j = te->mark;
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "invedge space: %d\n", j + 1);
- od.invedgedata = solv_calloc(j + 1, sizeof(Id));
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- {
- for (j = te->edges; od.edgedata[j]; j += 2)
- {
- if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- od.invedgedata[--od.tes[od.edgedata[j]].mark] = i;
- }
- }
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- te->edges = te->mark; /* edges now points into invedgedata */
- od.edgedata = solv_free(od.edgedata);
- od.nedgedata = j + 1;
-
- /* now the final ordering */
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- te->mark = 0;
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- for (j = te->edges; od.invedgedata[j]; j++)
- od.tes[od.invedgedata[j]].mark++;
-
- queue_init(&samerepoq);
- queue_init(&uninstq);
- queue_empty(&todo);
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- if (te->mark == 0)
- {
- if (installed && pool->solvables[te->p].repo == installed)
- queue_push(&uninstq, i);
- else
- queue_push(&todo, i);
- }
- assert(todo.count > 0 || uninstq.count > 0);
- oldcount = tr->count;
- queue_empty(tr);
-
- queue_init(&obsq);
-
- lastrepo = 0;
- lastmedia = 0;
- temedianr = solv_calloc(numte, sizeof(Id));
- for (i = 1; i < numte; i++)
- {
- Solvable *s = pool->solvables + od.tes[i].p;
- if (installed && s->repo == installed)
- j = 1;
- else
- j = solvable_lookup_num(s, SOLVABLE_MEDIANR, 1);
- temedianr[i] = j;
- }
- for (;;)
- {
- /* select an TE i */
- if (uninstq.count)
- i = queue_shift(&uninstq);
- else if (samerepoq.count)
- i = queue_shift(&samerepoq);
- else if (todo.count)
- {
- /* find next repo/media */
- for (j = 0; j < todo.count; j++)
- {
- if (!j || temedianr[todo.elements[j]] < lastmedia)
- {
- i = j;
- lastmedia = temedianr[todo.elements[j]];
- }
- }
- lastrepo = pool->solvables[od.tes[todo.elements[i]].p].repo;
-
- /* move all matching TEs to samerepoq */
- for (i = j = 0; j < todo.count; j++)
- {
- int k = todo.elements[j];
- if (temedianr[k] == lastmedia && pool->solvables[od.tes[k].p].repo == lastrepo)
- queue_push(&samerepoq, k);
- else
- todo.elements[i++] = k;
- }
- todo.count = i;
-
- assert(samerepoq.count);
- i = queue_shift(&samerepoq);
- }
- else
- break;
-
- te = od.tes + i;
- queue_push(tr, te->p);
-#if 0
-printf("do %s [%d]\n", pool_solvid2str(pool, te->p), temedianr[i]);
-#endif
- s = pool->solvables + te->p;
- for (j = te->edges; od.invedgedata[j]; j++)
- {
- struct _TransactionElement *te2 = od.tes + od.invedgedata[j];
- assert(te2->mark > 0);
- if (--te2->mark == 0)
- {
- Solvable *s = pool->solvables + te2->p;
-#if 0
-printf("free %s [%d]\n", pool_solvid2str(pool, te2->p), temedianr[od.invedgedata[j]]);
-#endif
- if (installed && s->repo == installed)
- queue_push(&uninstq, od.invedgedata[j]);
- else if (s->repo == lastrepo && temedianr[od.invedgedata[j]] == lastmedia)
- queue_push(&samerepoq, od.invedgedata[j]);
- else
- queue_push(&todo, od.invedgedata[j]);
- }
- }
- }
- solv_free(temedianr);
- queue_free(&todo);
- queue_free(&samerepoq);
- queue_free(&uninstq);
- queue_free(&obsq);
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- assert(te->mark == 0);
-
- /* add back obsoleted packages */
- transaction_add_obsoleted(trans);
- assert(tr->count == oldcount);
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "creating new transaction took %d ms\n", solv_timems(now));
- POOL_DEBUG(SOLV_DEBUG_STATS, "transaction ordering took %d ms\n", solv_timems(start));
-
- if ((flags & (SOLVER_TRANSACTION_KEEP_ORDERDATA | SOLVER_TRANSACTION_KEEP_ORDERCYCLES)) != 0)
- {
- struct _TransactionOrderdata *tod;
- trans->orderdata = tod = solv_calloc(1, sizeof(*trans->orderdata));
- if ((flags & SOLVER_TRANSACTION_KEEP_ORDERCYCLES) != 0)
- {
- Queue *cycles = tod->cycles = solv_calloc(1, sizeof(Queue));
- queue_init_clone(cycles, &od.cyclesdata);
- /* map from tes to packages */
- for (i = 0; i < cycles->count; i++)
- if (cycles->elements[i])
- cycles->elements[i] = od.tes[cycles->elements[i]].p;
- queue_insertn(cycles, cycles->count, od.cycles.count, od.cycles.elements);
- queue_push(cycles, od.cycles.count / 4);
- }
- if ((flags & SOLVER_TRANSACTION_KEEP_ORDERDATA) != 0)
- {
- tod->tes = od.tes;
- tod->ntes = numte;
- tod->invedgedata = od.invedgedata;
- tod->ninvedgedata = od.nedgedata;
- od.tes = 0;
- od.invedgedata = 0;
- }
- }
- solv_free(od.tes);
- solv_free(od.invedgedata);
- queue_free(&od.cycles);
- queue_free(&od.cyclesdata);
-}
-
-
-int
-transaction_order_add_choices(Transaction *trans, Id chosen, Queue *choices)
-{
- int i, j;
- struct _TransactionOrderdata *od = trans->orderdata;
- struct _TransactionElement *te;
-
- if (!od)
- return choices->count;
- if (!chosen)
- {
- /* initialization step */
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- te->mark = 0;
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- {
- for (j = te->edges; od->invedgedata[j]; j++)
- od->tes[od->invedgedata[j]].mark++;
- }
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- if (!te->mark)
- queue_push(choices, te->p);
- return choices->count;
- }
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- if (te->p == chosen)
- break;
- if (i == od->ntes)
- return choices->count;
- if (te->mark > 0)
- {
- /* hey! out-of-order installation! */
- te->mark = -1;
- }
- for (j = te->edges; od->invedgedata[j]; j++)
- {
- te = od->tes + od->invedgedata[j];
- assert(te->mark > 0 || te->mark == -1);
- if (te->mark > 0 && --te->mark == 0)
- queue_push(choices, te->p);
- }
- return choices->count;
-}
-
-void
-transaction_add_obsoleted(Transaction *trans)
-{
- Pool *pool = trans->pool;
- Repo *installed = pool->installed;
- Id p;
- Solvable *s;
- int i, j, k, max;
- Map done;
- Queue obsq, *steps;
-
- if (!installed || !trans->steps.count)
- return;
- /* calculate upper bound */
- max = 0;
- FOR_REPO_SOLVABLES(installed, p, s)
- if (MAPTST(&trans->transactsmap, p))
- max++;
- if (!max)
- return;
- /* make room */
- steps = &trans->steps;
- queue_insertn(steps, 0, max, 0);
-
- /* now add em */
- map_init(&done, installed->end - installed->start);
- queue_init(&obsq);
- for (j = 0, i = max; i < steps->count; i++)
- {
- p = trans->steps.elements[i];
- if (pool->solvables[p].repo == installed)
- {
- if (!trans->transaction_installed[p - pool->installed->start])
- trans->steps.elements[j++] = p;
- continue;
- }
- trans->steps.elements[j++] = p;
- queue_empty(&obsq);
- transaction_all_obs_pkgs(trans, p, &obsq);
- for (k = 0; k < obsq.count; k++)
- {
- p = obsq.elements[k];
- assert(p >= installed->start && p < installed->end);
- if (!MAPTST(&trans->transactsmap, p)) /* just in case */
- continue;
- if (MAPTST(&done, p - installed->start))
- continue;
- MAPSET(&done, p - installed->start);
- trans->steps.elements[j++] = p;
- }
- }
-
- /* free unneeded space */
- queue_truncate(steps, j);
- map_free(&done);
- queue_free(&obsq);
-}
-
-static void
-transaction_check_pkg(Transaction *trans, Id tepkg, Id pkg, Map *ins, Map *seen, int onlyprereq, Id noconfpkg, int depth)
-{
- Pool *pool = trans->pool;
- Id p, pp;
- Solvable *s;
- int good;
-
- if (MAPTST(seen, pkg))
- return;
- MAPSET(seen, pkg);
- s = pool->solvables + pkg;
-#if 0
- printf("- %*s%c%s\n", depth * 2, "", s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
-#endif
- if (s->requires)
- {
- Id req, *reqp;
- int inpre = 0;
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- {
- inpre = 1;
- continue;
- }
- if (onlyprereq && !inpre)
- continue;
- if (!strncmp(pool_id2str(pool, req), "rpmlib(", 7))
- continue;
- good = 0;
- /* first check kept packages, then freshly installed, then not yet uninstalled */
- FOR_PROVIDES(p, pp, req)
- {
- if (!MAPTST(ins, p))
- continue;
- if (MAPTST(&trans->transactsmap, p))
- continue;
- good++;
- transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
- }
- if (!good)
- {
- FOR_PROVIDES(p, pp, req)
- {
- if (!MAPTST(ins, p))
- continue;
- if (pool->solvables[p].repo == pool->installed)
- continue;
- good++;
- transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
- }
- }
- if (!good)
- {
- FOR_PROVIDES(p, pp, req)
- {
- if (!MAPTST(ins, p))
- continue;
- good++;
- transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
- }
- }
- if (!good)
- {
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %c%s: nothing provides %s needed by %c%s\n", pool->solvables[tepkg].repo == pool->installed ? '-' : '+', pool_solvid2str(pool, tepkg), pool_dep2str(pool, req), s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
- }
- }
- }
-}
-
-void
-transaction_check_order(Transaction *trans)
-{
- Pool *pool = trans->pool;
- Solvable *s;
- Id p, lastins;
- Map ins, seen;
- int i;
-
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\nchecking transaction order...\n");
- map_init(&ins, pool->nsolvables);
- map_init(&seen, pool->nsolvables);
- if (pool->installed)
- FOR_REPO_SOLVABLES(pool->installed, p, s)
- MAPSET(&ins, p);
- lastins = 0;
- for (i = 0; i < trans->steps.count; i++)
- {
- p = trans->steps.elements[i];
- s = pool->solvables + p;
- if (s->repo != pool->installed)
- lastins = p;
- if (s->repo != pool->installed)
- MAPSET(&ins, p);
- if (havescripts(pool, p))
- {
- MAPZERO(&seen);
- transaction_check_pkg(trans, p, p, &ins, &seen, 1, lastins, 0);
- }
- if (s->repo == pool->installed)
- MAPCLR(&ins, p);
- }
- map_free(&seen);
- map_free(&ins);
- POOL_DEBUG(SOLV_DEBUG_RESULT, "transaction order check done.\n");
-}
-
-void
-transaction_order_get_cycleids(Transaction *trans, Queue *q, int minseverity)
-{
- struct _TransactionOrderdata *od = trans->orderdata;
- Queue *cq;
- int i, cid, ncycles;
-
- queue_empty(q);
- if (!od || !od->cycles || !od->cycles->count)
- return;
- cq = od->cycles;
- ncycles = cq->elements[cq->count - 1];
- i = cq->count - 1 - ncycles * 4;
- for (cid = 1; cid <= ncycles; cid++, i += 4)
- {
- if (minseverity)
- {
- int cmin = cq->elements[i + 3] & 0xffff;
- int cmax = (cq->elements[i + 3] >> 16) & 0xffff;
- if (minseverity >= SOLVER_ORDERCYCLE_NORMAL && cmin < TYPE_REQ)
- continue;
- if (minseverity >= SOLVER_ORDERCYCLE_CRITICAL && (cmax & TYPE_PREREQ) == 0)
- continue;
- }
- queue_push(q, cid);
- }
-}
-
-int
-transaction_order_get_cycle(Transaction *trans, Id cid, Queue *q)
-{
- struct _TransactionOrderdata *od = trans->orderdata;
- Queue *cq;
- int cmin, cmax, severity;
- int ncycles;
-
- queue_empty(q);
- if (!od || !od->cycles || !od->cycles->count)
- return SOLVER_ORDERCYCLE_HARMLESS;
- cq = od->cycles;
- ncycles = cq->elements[cq->count - 1];
- if (cid < 1 || cid > ncycles)
- return SOLVER_ORDERCYCLE_HARMLESS;
- cid = cq->count - 1 - 4 * (ncycles - cid + 1);
- cmin = cq->elements[cid + 3] & 0xffff;
- cmax = (cq->elements[cid + 3] >> 16) & 0xffff;
- if (cmin < TYPE_REQ)
- severity = SOLVER_ORDERCYCLE_HARMLESS;
- else if ((cmax & TYPE_PREREQ) == 0)
- severity = SOLVER_ORDERCYCLE_NORMAL;
- else
- severity = SOLVER_ORDERCYCLE_CRITICAL;
- if (q)
- queue_insertn(q, 0, cq->elements[cid + 1], cq->elements + cq->elements[cid]);
- return severity;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * Generic policy interface for SAT solver
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "solver.h"
-#include "solver_private.h"
-#include "evr.h"
-#include "policy.h"
-#include "poolvendor.h"
-#include "poolarch.h"
-#include "linkedpkg.h"
-#include "cplxdeps.h"
-
-
-
-/*-----------------------------------------------------------------*/
-
-/*
- * prep for prune_best_version
- * sort by name
- */
-
-static int
-prune_to_best_version_sortcmp(const void *ap, const void *bp, void *dp)
-{
- Pool *pool = dp;
- int r;
- Id a = *(Id *)ap;
- Id b = *(Id *)bp;
- Solvable *sa, *sb;
-
- sa = pool->solvables + a;
- sb = pool->solvables + b;
- r = sa->name - sb->name;
- if (r)
- {
- const char *na, *nb;
- /* different names. We use real strcmp here so that the result
- * is not depending on some random solvable order */
- na = pool_id2str(pool, sa->name);
- nb = pool_id2str(pool, sb->name);
- return strcmp(na, nb);
- }
- if (sa->arch != sb->arch)
- {
- int aa, ab;
- aa = (sa->arch <= pool->lastarch) ? pool->id2arch[sa->arch] : 0;
- ab = (sb->arch <= pool->lastarch) ? pool->id2arch[sb->arch] : 0;
- if (aa != ab && aa > 1 && ab > 1)
- return aa - ab; /* lowest score first */
- }
-
- /* the same name, bring installed solvables to the front */
- if (pool->installed)
- {
- if (sa->repo == pool->installed)
- {
- if (sb->repo != pool->installed)
- return -1;
- }
- else if (sb->repo == pool->installed)
- return 1;
- }
- /* sort by repository sub-prio (installed repo handled above) */
- r = (sb->repo ? sb->repo->subpriority : 0) - (sa->repo ? sa->repo->subpriority : 0);
- if (r)
- return r;
- /* no idea about the order, sort by id */
- return a - b;
-}
-
-
-/*
- * prune to repository with highest priority.
- * does not prune installed solvables.
- */
-
-static void
-prune_to_highest_prio(Pool *pool, Queue *plist)
-{
- int i, j;
- Solvable *s;
- int bestprio = 0, bestprioset = 0;
-
- /* prune to highest priority */
- for (i = 0; i < plist->count; i++) /* find highest prio in queue */
- {
- s = pool->solvables + plist->elements[i];
- if (pool->installed && s->repo == pool->installed)
- continue;
- if (!bestprioset || s->repo->priority > bestprio)
- {
- bestprio = s->repo->priority;
- bestprioset = 1;
- }
- }
- if (!bestprioset)
- return;
- for (i = j = 0; i < plist->count; i++) /* remove all with lower prio */
- {
- s = pool->solvables + plist->elements[i];
- if (s->repo->priority == bestprio || (pool->installed && s->repo == pool->installed))
- plist->elements[j++] = plist->elements[i];
- }
- plist->count = j;
-}
-
-
-/* installed packages involed in a dup operation can only be kept
- * if they are identical to a non-installed one */
-static void
-solver_prune_installed_dup_packages(Solver *solv, Queue *plist)
-{
- Pool *pool = solv->pool;
- int i, j, bestprio = 0;
-
- /* find bestprio (again) */
- for (i = 0; i < plist->count; i++)
- {
- Solvable *s = pool->solvables + plist->elements[i];
- if (s->repo != pool->installed)
- {
- bestprio = s->repo->priority;
- break;
- }
- }
- if (i == plist->count)
- return; /* only installed packages, could not find prio */
- for (i = j = 0; i < plist->count; i++)
- {
- Id p = plist->elements[i];
- Solvable *s = pool->solvables + p;
- if (s->repo != pool->installed && s->repo->priority < bestprio)
- continue;
- if (s->repo == pool->installed && (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))))
- {
- Id p2, pp2;
- int keepit = 0;
- FOR_PROVIDES(p2, pp2, s->name)
- {
- Solvable *s2 = pool->solvables + p2;
- if (s2->repo == pool->installed || s2->evr != s->evr || s2->repo->priority < bestprio)
- continue;
- if (!solvable_identical(s, s2))
- continue;
- keepit = 1;
- if (s2->repo->priority > bestprio)
- {
- /* new max prio! */
- bestprio = s2->repo->priority;
- j = 0;
- }
- }
- if (!keepit)
- continue; /* no identical package found, ignore installed package */
- }
- plist->elements[j++] = p;
- }
- if (j)
- plist->count = j;
-}
-
-/*
- * like prune_to_highest_prio, but calls solver prune_installed_dup_packages
- * when there are dup packages
- */
-static inline void
-solver_prune_to_highest_prio(Solver *solv, Queue *plist)
-{
- prune_to_highest_prio(solv->pool, plist);
- if (plist->count > 1 && solv->pool->installed && (solv->dupmap_all || solv->dupinvolvedmap.size))
- solver_prune_installed_dup_packages(solv, plist);
-}
-
-
-static void
-solver_prune_to_highest_prio_per_name(Solver *solv, Queue *plist)
-{
- Pool *pool = solv->pool;
- Queue pq;
- int i, j, k;
- Id name;
-
- queue_init(&pq);
- solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
- queue_push(&pq, plist->elements[0]);
- name = pool->solvables[pq.elements[0]].name;
- for (i = 1, j = 0; i < plist->count; i++)
- {
- if (pool->solvables[plist->elements[i]].name != name)
- {
- if (pq.count > 2)
- solver_prune_to_highest_prio(solv, &pq);
- for (k = 0; k < pq.count; k++)
- plist->elements[j++] = pq.elements[k];
- queue_empty(&pq);
- queue_push(&pq, plist->elements[i]);
- name = pool->solvables[pq.elements[0]].name;
- }
- }
- if (pq.count > 2)
- solver_prune_to_highest_prio(solv, &pq);
- for (k = 0; k < pq.count; k++)
- plist->elements[j++] = pq.elements[k];
- queue_free(&pq);
- plist->count = j;
-}
-
-
-#ifdef ENABLE_COMPLEX_DEPS
-
-/* simple fixed-size hash for package ids */
-#define CPLXDEPHASH_EMPTY(elements) (memset(elements, 0, sizeof(Id) * 256))
-#define CPLXDEPHASH_SET(elements, p) (elements[(p) & 255] |= (1 << ((p) >> 8 & 31)))
-#define CPLXDEPHASH_TST(elements, p) (elements[(p) & 255] && (elements[(p) & 255] & (1 << ((p) >> 8 & 31))))
-
-static void
-check_complex_dep(Solver *solv, Id dep, Map *m, Queue **cqp)
-{
- Pool *pool = solv->pool;
- Queue q;
- queue_init(&q);
- Id p;
- int i, qcnt;
-
-#if 0
- printf("check_complex_dep %s\n", pool_dep2str(pool, dep));
-#endif
- i = pool_normalize_complex_dep(pool, dep, &q, CPLXDEPS_EXPAND);
- if (i == 0 || i == 1)
- {
- queue_free(&q);
- return;
- }
- qcnt = q.count;
- for (i = 0; i < qcnt; i++)
- {
- /* we rely on the fact that blocks are ordered here.
- * if we reach a positive element, we know that we
- * saw all negative ones */
- for (; (p = q.elements[i]) < 0; i++)
- {
- if (solv->decisionmap[-p] < 0)
- break;
- if (solv->decisionmap[-p] == 0)
- queue_push(&q, -p); /* undecided negative literal */
- }
- if (p <= 0)
- {
-#if 0
- printf("complex dep block cannot be true or no pos literals\n");
-#endif
- while (q.elements[i])
- i++;
- if (qcnt != q.count)
- queue_truncate(&q, qcnt);
- continue;
- }
- if (qcnt == q.count)
- {
- /* all negative literals installed, add positive literals to map */
- for (; (p = q.elements[i]) != 0; i++)
- MAPSET(m, p);
- }
- else
- {
- /* at least one undecided negative literal, postpone */
- int j, k;
- Queue *cq;
-#if 0
- printf("add new complex dep block\n");
- for (j = qcnt; j < q.count; j++)
- printf(" - %s\n", pool_solvid2str(pool, q.elements[j]));
-#endif
- while (q.elements[i])
- i++;
- if (!(cq = *cqp))
- {
- cq = solv_calloc(1, sizeof(Queue));
- queue_init(cq);
- queue_insertn(cq, 0, 256, 0); /* allocate hash area */
- *cqp = cq;
- }
- for (j = qcnt; j < q.count; j++)
- {
- p = q.elements[j];
- /* check if we already have this (dep, p) entry */
- for (k = 256; k < cq->count; k += 2)
- if (cq->elements[k + 1] == dep && cq->elements[k] == p)
- break;
- if (k == cq->count)
- {
- /* a new one. add to cq and hash */
- queue_push2(cq, p, dep);
- CPLXDEPHASH_SET(cq->elements, p);
- }
- }
- queue_truncate(&q, qcnt);
- }
- }
- queue_free(&q);
-}
-
-static void
-recheck_complex_deps(Solver *solv, Id p, Map *m, Queue **cqp)
-{
- Queue *cq = *cqp;
- Id pp;
- int i;
-#if 0
- printf("recheck_complex_deps for package %s\n", pool_solvid2str(solv->pool, p));
-#endif
- /* make sure that we don't have a false hit */
- for (i = 256; i < cq->count; i += 2)
- if (cq->elements[i] == p)
- break;
- if (i == cq->count)
- return; /* false alert */
- if (solv->decisionmap[p] <= 0)
- return; /* just in case... */
-
- /* rebuild the hash, call check_complex_dep for our package */
- CPLXDEPHASH_EMPTY(cq->elements);
- for (i = 256; i < cq->count; i += 2)
- if ((pp = cq->elements[i]) == p)
- {
- Id dep = cq->elements[i + 1];
- queue_deleten(cq, i, 2);
- i -= 2;
- check_complex_dep(solv, dep, m, &cq);
- }
- else
- CPLXDEPHASH_SET(cq->elements, pp);
-}
-
-#endif
-
-
-void
-policy_update_recommendsmap(Solver *solv)
-{
- Pool *pool = solv->pool;
- Solvable *s;
- Id p, pp, rec, *recp, sug, *sugp;
-
- if (solv->recommends_index < 0)
- {
- MAPZERO(&solv->recommendsmap);
- MAPZERO(&solv->suggestsmap);
-#ifdef ENABLE_COMPLEX_DEPS
- if (solv->recommendscplxq)
- {
- queue_free(solv->recommendscplxq);
- solv->recommendscplxq = solv_free(solv->recommendscplxq);
- }
- if (solv->suggestscplxq)
- {
- queue_free(solv->suggestscplxq);
- solv->suggestscplxq = solv_free(solv->suggestscplxq);
- }
-#endif
- solv->recommends_index = 0;
- }
- while (solv->recommends_index < solv->decisionq.count)
- {
- p = solv->decisionq.elements[solv->recommends_index++];
- if (p < 0)
- continue;
- s = pool->solvables + p;
-#ifdef ENABLE_COMPLEX_DEPS
- /* re-check postponed complex blocks */
- if (solv->recommendscplxq && CPLXDEPHASH_TST(solv->recommendscplxq->elements, p))
- recheck_complex_deps(solv, p, &solv->recommendsmap, &solv->recommendscplxq);
- if (solv->suggestscplxq && CPLXDEPHASH_TST(solv->suggestscplxq->elements, p))
- recheck_complex_deps(solv, p, &solv->suggestsmap, &solv->suggestscplxq);
-#endif
- if (s->recommends)
- {
- recp = s->repo->idarraydata + s->recommends;
- while ((rec = *recp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, rec))
- {
- check_complex_dep(solv, rec, &solv->recommendsmap, &solv->recommendscplxq);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, rec)
- MAPSET(&solv->recommendsmap, p);
- }
- }
- if (s->suggests)
- {
- sugp = s->repo->idarraydata + s->suggests;
- while ((sug = *sugp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, sug))
- {
- check_complex_dep(solv, sug, &solv->suggestsmap, &solv->suggestscplxq);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, sug)
- MAPSET(&solv->suggestsmap, p);
- }
- }
- }
-}
-
-/* bring suggested/enhanced packages to front
- * installed packages count as suggested */
-static void
-prefer_suggested(Solver *solv, Queue *plist)
-{
- Pool *pool = solv->pool;
- int i, count;
-
- /* update our recommendsmap/suggestsmap */
- if (solv->recommends_index < solv->decisionq.count)
- policy_update_recommendsmap(solv);
-
- for (i = 0, count = plist->count; i < count; i++)
- {
- Id p = plist->elements[i];
- Solvable *s = pool->solvables + p;
- if ((pool->installed && s->repo == pool->installed) ||
- MAPTST(&solv->suggestsmap, p) ||
- solver_is_enhancing(solv, s))
- continue; /* good package */
- /* bring to back */
- if (i < plist->count - 1)
- {
- memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id));
- plist->elements[plist->count - 1] = p;
- }
- i--;
- count--;
- }
-}
-
-/*
- * prune to recommended/suggested packages.
- * does not prune installed packages (they are also somewhat recommended).
- */
-static void
-prune_to_recommended(Solver *solv, Queue *plist)
-{
- Pool *pool = solv->pool;
- int i, j, k, ninst;
- Solvable *s;
- Id p;
-
- ninst = 0;
- if (pool->installed)
- {
- for (i = 0; i < plist->count; i++)
- {
- p = plist->elements[i];
- s = pool->solvables + p;
- if (pool->installed && s->repo == pool->installed)
- ninst++;
- }
- }
- if (plist->count - ninst < 2)
- return;
-
- /* update our recommendsmap/suggestsmap */
- if (solv->recommends_index < solv->decisionq.count)
- policy_update_recommendsmap(solv);
-
- /* prune to recommended/supplemented */
- ninst = 0;
- for (i = j = 0; i < plist->count; i++)
- {
- p = plist->elements[i];
- s = pool->solvables + p;
- if (pool->installed && s->repo == pool->installed)
- {
- ninst++;
- if (j)
- plist->elements[j++] = p;
- continue;
- }
- if (!MAPTST(&solv->recommendsmap, p))
- if (!solver_is_supplementing(solv, s))
- continue;
- if (!j && ninst)
- {
- for (k = 0; j < ninst; k++)
- {
- s = pool->solvables + plist->elements[k];
- if (pool->installed && s->repo == pool->installed)
- plist->elements[j++] = plist->elements[k];
- }
- }
- plist->elements[j++] = p;
- }
- if (j)
- plist->count = j;
-
-#if 0
- /* anything left to prune? */
- if (plist->count - ninst < 2)
- return;
-
- /* prune to suggested/enhanced */
- ninst = 0;
- for (i = j = 0; i < plist->count; i++)
- {
- p = plist->elements[i];
- s = pool->solvables + p;
- if (pool->installed && s->repo == pool->installed)
- {
- ninst++;
- if (j)
- plist->elements[j++] = p;
- continue;
- }
- if (!MAPTST(&solv->suggestsmap, p))
- if (!solver_is_enhancing(solv, s))
- continue;
- if (!j && ninst)
- {
- for (k = 0; j < ninst; k++)
- {
- s = pool->solvables + plist->elements[k];
- if (pool->installed && s->repo == pool->installed)
- plist->elements[j++] = plist->elements[k];
- }
- }
- plist->elements[j++] = p;
- }
- if (j)
- plist->count = j;
-#endif
-}
-
-static void
-prune_to_best_arch(const Pool *pool, Queue *plist)
-{
- Id a, bestscore;
- Solvable *s;
- int i, j;
-
- if (!pool->id2arch || plist->count < 2)
- return;
- bestscore = 0;
- for (i = 0; i < plist->count; i++)
- {
- s = pool->solvables + plist->elements[i];
- a = s->arch;
- a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
- if (a && a != 1 && (!bestscore || a < bestscore))
- bestscore = a;
- }
- if (!bestscore)
- return;
- 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 ^ bestscore) & 0xffff0000) != 0)
- continue;
- plist->elements[j++] = plist->elements[i];
- }
- if (j)
- plist->count = j;
-}
-
-
-struct trj_data {
- Pool *pool;
- Queue *plist;
- Id *stack;
- Id nstack;
- Id *low;
- Id firstidx;
- Id idx;
-};
-
-/* This is Tarjan's SCC algorithm, slightly modified */
-static void
-trj_visit(struct trj_data *trj, Id node)
-{
- Id *low = trj->low;
- Pool *pool = trj->pool;
- Queue *plist = trj->plist;
- Id myidx, stackstart;
- Solvable *s;
- int i;
- Id p, pp, obs, *obsp;
-
- low[node] = myidx = trj->idx++;
- trj->stack[(stackstart = trj->nstack++)] = node;
-
- s = pool->solvables + plist->elements[node];
- if (s->obsoletes)
- {
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(p, pp, obs)
- {
- Solvable *ps = pool->solvables + p;
- if (ps->name == s->name)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- /* hmm, expensive. should use hash if plist is big */
- for (i = 0; i < plist->count; i++)
- {
- if (node != i && plist->elements[i] == p)
- {
- Id l = low[i];
- if (!l)
- {
- if (!ps->obsoletes)
- {
- /* don't bother */
- trj->idx++;
- low[i] = -1;
- continue;
- }
- trj_visit(trj, i);
- l = low[i];
- }
- if (l < 0)
- continue;
- if (l < trj->firstidx)
- {
- int k;
- /* this means we have reached an old SCC found earlier.
- * delete it as we obsolete it */
- for (k = l; ; k++)
- {
- if (low[trj->stack[k]] == l)
- low[trj->stack[k]] = -1;
- else
- break;
- }
- }
- else if (l < low[node])
- low[node] = l;
- }
- }
- }
- }
- }
- if (low[node] == myidx) /* found a SCC? */
- {
- /* we're only interested in SCCs that contain the first node,
- * as all others are "obsoleted" */
- if (myidx != trj->firstidx)
- myidx = -1;
- for (i = stackstart; i < trj->nstack; i++)
- low[trj->stack[i]] = myidx;
- trj->nstack = stackstart; /* empty stack */
- }
-}
-
-/*
- * remove entries from plist that are obsoleted by other entries
- * with different name.
- */
-static void
-prune_obsoleted(Pool *pool, Queue *plist)
-{
- Id data_buf[2 * 16], *data;
- struct trj_data trj;
- int i, j;
- Solvable *s;
-
- if (plist->count <= 16)
- {
- memset(data_buf, 0, sizeof(data_buf));
- data = data_buf;
- }
- else
- data = solv_calloc(plist->count, 2 * sizeof(Id));
- trj.pool = pool;
- trj.plist = plist;
- trj.low = data;
- trj.idx = 1;
- trj.stack = data + plist->count - 1; /* -1 so we can index with idx (which starts with 1) */
- for (i = 0; i < plist->count; i++)
- {
- if (trj.low[i])
- continue;
- s = pool->solvables + plist->elements[i];
- if (s->obsoletes)
- {
- trj.firstidx = trj.nstack = trj.idx;
- trj_visit(&trj, i);
- }
- else
- {
- Id myidx = trj.idx++;
- trj.low[i] = myidx;
- trj.stack[myidx] = i;
- }
- }
- for (i = j = 0; i < plist->count; i++)
- if (trj.low[i] >= 0)
- plist->elements[j++] = plist->elements[i];
- plist->count = j;
- if (data != data_buf)
- solv_free(data);
-}
-
-/* this is prune_obsoleted special-cased for two elements */
-static void
-prune_obsoleted_2(Pool *pool, Queue *plist)
-{
- int i;
- Solvable *s;
- Id p, pp, obs, *obsp;
- Id other;
- int obmap = 0;
-
- for (i = 0; i < 2; i++)
- {
- s = pool->solvables + plist->elements[i];
- other = plist->elements[1 - i];
- if (s->obsoletes)
- {
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(p, pp, obs)
- {
- Solvable *ps;
- if (p != other)
- continue;
- ps = pool->solvables + p;
- if (ps->name == s->name)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- obmap |= 1 << i;
- break;
- }
- if (p)
- break;
- }
- }
- }
- if (obmap == 0 || obmap == 3)
- return;
- if (obmap == 2)
- plist->elements[0] = plist->elements[1];
- plist->count = 1;
-}
-
-/*
- * bring those elements to the front of the queue that
- * have a installed solvable with the same name
- */
-static void
-move_installed_to_front(Pool *pool, Queue *plist)
-{
- int i, j;
- Solvable *s;
- Id p, pp;
-
- for (i = j = 0; i < plist->count; i++)
- {
- s = pool->solvables + plist->elements[i];
- if (s->repo != pool->installed)
- {
- FOR_PROVIDES(p, pp, s->name)
- {
- Solvable *ps = pool->solvables + p;
- if (s->name == ps->name && ps->repo == pool->installed)
- {
- s = ps;
- break;
- }
- }
- }
- if (s->repo == pool->installed)
- {
- if (i != j)
- {
- p = plist->elements[i];
- if (i - j == 1)
- plist->elements[i] = plist->elements[j];
- else
- memmove(plist->elements + j + 1, plist->elements + j, (i - j) * sizeof(Id));
- plist->elements[j] = p;
- }
- else if (j + 2 == plist->count)
- break; /* no need to check last element if all prev ones are installed */
- j++;
- }
- }
-}
-
-/*
- * prune_to_best_version
- *
- * sort list of packages (given through plist) by name and evr
- * return result through plist
- */
-void
-prune_to_best_version(Pool *pool, Queue *plist)
-{
- int i, j, r;
- Solvable *s, *best;
-
- if (plist->count < 2) /* no need to prune for a single entry */
- return;
- POOL_DEBUG(SOLV_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
-
- /* sort by name first, prefer installed */
- solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
-
- /* now find best 'per name' */
- best = 0;
- for (i = j = 0; i < plist->count; i++)
- {
- s = pool->solvables + plist->elements[i];
-
- POOL_DEBUG(SOLV_DEBUG_POLICY, "- %s[%s]\n",
- pool_solvable2str(pool, s),
- (pool->installed && s->repo == pool->installed) ? "installed" : "not installed");
-
- if (!best) /* if no best yet, the current is best */
- {
- best = s;
- continue;
- }
-
- /* name switch: finish group, re-init */
- if (best->name != s->name) /* new name */
- {
- plist->elements[j++] = best - pool->solvables; /* move old best to front */
- best = s; /* take current as new best */
- continue;
- }
- r = best->evr != s->evr ? pool_evrcmp(pool, best->evr, s->evr, EVRCMP_COMPARE) : 0;
-#ifdef ENABLE_LINKED_PKGS
- if (r == 0 && has_package_link(pool, s))
- r = pool_link_evrcmp(pool, best, s);
-#endif
- if (r < 0)
- best = s;
- }
- plist->elements[j++] = best - pool->solvables; /* finish last group */
- plist->count = j;
-
- /* we reduced the list to one package per name, now look at
- * package obsoletes */
- if (plist->count > 1)
- {
- if (plist->count == 2)
- prune_obsoleted_2(pool, plist);
- else
- prune_obsoleted(pool, plist);
- }
- if (plist->count > 1 && pool->installed)
- move_installed_to_front(pool, plist);
-}
-
-
-static int
-sort_by_name_evr_sortcmp(const void *ap, const void *bp, void *dp)
-{
- Pool *pool = dp;
- Id a, *aa = (Id *)ap;
- Id b, *bb = (Id *)bp;
- Id r = aa[1] - bb[1];
- if (r)
- return r < 0 ? -1 : 1;
- if (aa[2] == bb[2])
- return 0;
- a = aa[2] < 0 ? -aa[2] : aa[2];
- b = bb[2] < 0 ? -bb[2] : bb[2];
- if (pool->disttype != DISTTYPE_DEB && a != b)
- {
- /* treat release-less versions different */
- const char *as = pool_id2str(pool, a);
- const char *bs = pool_id2str(pool, b);
- if (strchr(as, '-'))
- {
- if (!strchr(bs, '-'))
- return -2;
- }
- else
- {
- if (strchr(bs, '-'))
- return 2;
- }
- }
- r = pool_evrcmp(pool, b, a, EVRCMP_COMPARE);
- if (!r && (aa[2] < 0 || bb[2] < 0))
- {
- if (bb[2] >= 0)
- return 1;
- if (aa[2] >= 0)
- return -1;
- }
- if (r)
- return r < 0 ? -1 : 1;
- return 0;
-}
-
-/* common end of sort_by_srcversion and sort_by_common_dep */
-static void
-sort_by_name_evr_array(Pool *pool, Queue *plist, int count, int ent)
-{
- Id lastname;
- int i, j, bad, havebad;
- Id *pp, *elements = plist->elements;
-
- if (ent < 2)
- {
- queue_truncate(plist, count);
- return;
- }
- solv_sort(elements + count * 2, ent, sizeof(Id) * 3, sort_by_name_evr_sortcmp, pool);
- lastname = 0;
- bad = havebad = 0;
- for (i = 0, pp = elements + count * 2; i < ent; i++, pp += 3)
- {
- if (lastname && pp[1] == lastname)
- {
- if (pp[0] != pp[-3] && sort_by_name_evr_sortcmp(pp - 3, pp, pool) == -1)
- {
-#if 0
- printf("%s - %s: bad %s %s - %s\n", pool_solvid2str(pool, elements[pp[-3]]), pool_solvid2str(pool, elements[pp[0]]), pool_dep2str(pool, lastname), pool_id2str(pool, pp[-1] < 0 ? -pp[-1] : pp[-1]), pool_id2str(pool, pp[2] < 0 ? -pp[2] : pp[2]));
-#endif
- bad++;
- havebad = 1;
- }
- }
- else
- {
- bad = 0;
- lastname = pp[1];
- }
- elements[count + pp[0]] += bad;
- }
-
-#if 0
-for (i = 0; i < count; i++)
- printf("%s badness %d\n", pool_solvid2str(pool, elements[i]), elements[count + i]);
-#endif
-
- if (havebad)
- {
- /* simple stable insertion sort */
- if (pool->installed)
- for (i = 0; i < count; i++)
- if (pool->solvables[elements[i]].repo == pool->installed)
- elements[i + count] = 0;
- for (i = 1; i < count; i++)
- for (j = i, pp = elements + count + j; j > 0; j--, pp--)
- if (pp[-1] > pp[0])
- {
- Id *pp2 = pp - count;
- Id p = pp[-1];
- pp[-1] = pp[0];
- pp[0] = p;
- p = pp2[-1];
- pp2[-1] = pp2[0];
- pp2[0] = p;
- }
- else
- break;
- }
- queue_truncate(plist, count);
-}
-
-#if 0
-static void
-sort_by_srcversion(Pool *pool, Queue *plist)
-{
- int i, count = plist->count, ent = 0;
- queue_insertn(plist, count, count, 0);
- for (i = 0; i < count; i++)
- {
- Id name, evr, p = plist->elements[i];
- Solvable *s = pool->solvables + p;
- if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
- name = s->name;
- else
- name = solvable_lookup_id(s, SOLVABLE_SOURCENAME);
- if (solvable_lookup_void(s, SOLVABLE_SOURCEEVR))
- evr = s->evr;
- else
- evr = solvable_lookup_id(s, SOLVABLE_SOURCEEVR);
- if (!name || !evr || ISRELDEP(evr))
- continue;
- queue_push(plist, i);
- queue_push2(plist, name, evr);
- ent++;
- }
- sort_by_name_evr_array(pool, plist, count, ent);
-}
-#endif
-
-static void
-sort_by_common_dep(Pool *pool, Queue *plist)
-{
- int i, count = plist->count, ent = 0;
- Id id, *dp;
- queue_insertn(plist, count, count, 0);
- for (i = 0; i < count; i++)
- {
- Id p = plist->elements[i];
- Solvable *s = pool->solvables + p;
- if (!s->provides)
- continue;
- for (dp = s->repo->idarraydata + s->provides; (id = *dp++) != 0; )
- {
- Reldep *rd;
- if (!ISRELDEP(id))
- continue;
- rd = GETRELDEP(pool, id);
- if ((rd->flags == REL_EQ || rd->flags == (REL_EQ | REL_LT) || rd->flags == REL_LT) && !ISRELDEP(rd->evr))
- {
- if (rd->flags == REL_EQ)
- {
- /* ignore hashes */
- const char *s = pool_id2str(pool, rd->evr);
- if (strlen(s) >= 4)
- {
- while ((*s >= 'a' && *s <= 'f') || (*s >= '0' && *s <= '9'))
- s++;
- if (!*s)
- continue;
- }
- }
- queue_push(plist, i);
- queue_push2(plist, rd->name, rd->flags == REL_LT ? -rd->evr : rd->evr);
- ent++;
- }
- }
- }
- sort_by_name_evr_array(pool, plist, count, ent);
-}
-
-/* check if we have an update candidate */
-static void
-dislike_old_versions(Pool *pool, Queue *plist)
-{
- int i, count;
-
- for (i = 0, count = plist->count; i < count; i++)
- {
- Id p = plist->elements[i];
- Solvable *s = pool->solvables + p;
- Repo *repo = s->repo;
- Id q, qq;
- int bad = 0;
-
- if (!repo || repo == pool->installed)
- continue;
- FOR_PROVIDES(q, qq, s->name)
- {
- Solvable *qs = pool->solvables + q;
- if (q == p)
- continue;
- if (s->name != qs->name || s->arch != qs->arch)
- continue;
- if (repo->priority != qs->repo->priority)
- {
- if (repo->priority > qs->repo->priority)
- continue;
- bad = 1;
- break;
- }
- if (pool_evrcmp(pool, qs->evr, s->evr, EVRCMP_COMPARE) > 0)
- {
- bad = 1;
- break;
- }
- }
- if (!bad)
- continue;
- /* bring to back */
- if (i < plist->count - 1)
- {
- memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id));
- plist->elements[plist->count - 1] = p;
- }
- i--;
- count--;
- }
-}
-
-/*
- * POLICY_MODE_CHOOSE: default, do all pruning steps
- * POLICY_MODE_RECOMMEND: leave out prune_to_recommended
- * POLICY_MODE_SUGGEST: leave out prune_to_recommended, do prio pruning just per name
- */
-void
-policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
-{
- Pool *pool = solv->pool;
- if (plist->count > 1)
- {
- if (mode != POLICY_MODE_SUGGEST)
- solver_prune_to_highest_prio(solv, plist);
- else
- solver_prune_to_highest_prio_per_name(solv, plist);
- }
- if (plist->count > 1)
- prune_to_best_arch(pool, plist);
- if (plist->count > 1)
- prune_to_best_version(pool, plist);
- if (plist->count > 1 && (mode == POLICY_MODE_CHOOSE || mode == POLICY_MODE_CHOOSE_NOREORDER))
- {
- prune_to_recommended(solv, plist);
- if (plist->count > 1 && mode != POLICY_MODE_CHOOSE_NOREORDER)
- {
- /* do some fancy reordering */
-#if 0
- sort_by_srcversion(pool, plist);
-#endif
- dislike_old_versions(pool, plist);
- sort_by_common_dep(pool, plist);
- prefer_suggested(solv, plist);
- }
- }
-}
-
-
-/* check if there is an illegal architecture change if
- * installed solvable s1 is replaced by s2 */
-int
-policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
-{
- Pool *pool = solv->pool;
- Id a1 = s1->arch, a2 = s2->arch;
-
- /* we allow changes to/from noarch */
- if (a1 == a2 || a1 == pool->noarchid || a2 == pool->noarchid)
- 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;
-}
-
-/* check if there is an illegal vendor change if
- * installed solvable s1 is replaced by s2 */
-int
-policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
-{
- Pool *pool = solv->pool;
- Id v1, v2;
- Id vendormask1, vendormask2;
-
- if (pool->custom_vendorcheck)
- return pool->custom_vendorcheck(pool, s1, s2);
-
- /* treat a missing vendor as empty string */
- v1 = s1->vendor ? s1->vendor : ID_EMPTY;
- v2 = s2->vendor ? s2->vendor : ID_EMPTY;
- if (v1 == v2)
- return 0;
- vendormask1 = pool_vendor2mask(pool, v1);
- if (!vendormask1)
- return 1; /* can't match */
- vendormask2 = pool_vendor2mask(pool, v2);
- if ((vendormask1 & vendormask2) != 0)
- return 0;
- return 1; /* no class matches */
-}
-
-/* check if it is illegal to replace installed
- * package "is" with package "s" (which must obsolete "is")
- */
-int
-policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore)
-{
- Pool *pool = solv->pool;
- int ret = 0;
- int duppkg = solv->dupmap_all ? 1 : 0;
- if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !(duppkg ? solv->dup_allowdowngrade : solv->allowdowngrade))
- {
- if (is->name == s->name && pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0)
- ret |= POLICY_ILLEGAL_DOWNGRADE;
- }
- if (!(ignore & POLICY_ILLEGAL_ARCHCHANGE) && !(duppkg ? solv->dup_allowarchchange : solv->allowarchchange))
- {
- if (is->arch != s->arch && policy_illegal_archchange(solv, is, s))
- ret |= POLICY_ILLEGAL_ARCHCHANGE;
- }
- if (!(ignore & POLICY_ILLEGAL_VENDORCHANGE) && !(duppkg ? solv->dup_allowvendorchange : solv->allowvendorchange))
- {
- if (is->vendor != s->vendor && policy_illegal_vendorchange(solv, is, s))
- ret |= POLICY_ILLEGAL_VENDORCHANGE;
- }
- if (!(ignore & POLICY_ILLEGAL_NAMECHANGE) && !(duppkg ? solv->dup_allownamechange : solv->allownamechange))
- {
- if (is->name != s->name)
- ret |= POLICY_ILLEGAL_NAMECHANGE;
- }
- return ret;
-}
-
-/*-------------------------------------------------------------------
- *
- * create reverse obsoletes map for installed solvables
- *
- * For each installed solvable find which packages with *different* names
- * obsolete the solvable.
- * This index is used in policy_findupdatepackages() below.
- */
-void
-policy_create_obsolete_index(Solver *solv)
-{
- Pool *pool = solv->pool;
- Solvable *s;
- Repo *installed = solv->installed;
- Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data;
- int i, n, cnt;
-
- solv->obsoletes = solv_free(solv->obsoletes);
- solv->obsoletes_data = solv_free(solv->obsoletes_data);
- if (!installed || installed->start == installed->end)
- return;
- cnt = installed->end - installed->start;
- solv->obsoletes = obsoletes = solv_calloc(cnt, sizeof(Id));
- for (i = 1; i < pool->nsolvables; i++)
- {
- s = pool->solvables + i;
- if (!s->obsoletes)
- continue;
- if (!pool_installable(pool, s))
- continue;
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(p, pp, obs)
- {
- Solvable *ps = pool->solvables + p;;
- if (ps->repo != installed)
- continue;
- if (ps->name == s->name)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- obsoletes[p - installed->start]++;
- }
- }
- }
- n = 0;
- for (i = 0; i < cnt; i++)
- if (obsoletes[i])
- {
- n += obsoletes[i] + 1;
- obsoletes[i] = n;
- }
- solv->obsoletes_data = obsoletes_data = solv_calloc(n + 1, sizeof(Id));
- POOL_DEBUG(SOLV_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1);
- for (i = pool->nsolvables - 1; i > 0; i--)
- {
- s = pool->solvables + i;
- if (!s->obsoletes)
- continue;
- if (!pool_installable(pool, s))
- continue;
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(p, pp, obs)
- {
- Solvable *ps = pool->solvables + p;;
- if (ps->repo != installed)
- continue;
- if (ps->name == s->name)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- if (obsoletes_data[obsoletes[p - installed->start]] != i)
- obsoletes_data[--obsoletes[p - installed->start]] = i;
- }
- }
- }
-}
-
-
-/*
- * find update candidates
- *
- * s: installed solvable to be updated
- * qs: [out] queue to hold Ids of candidates
- * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
- * 2 = dup mode
- *
- */
-void
-policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
-{
- /* installed packages get a special upgrade allowed rule */
- Pool *pool = solv->pool;
- Id p, pp, n, p2, pp2;
- Id obs, *obsp;
- Solvable *ps;
- int haveprovobs = 0;
- int allowdowngrade = allow_all ? 1 : solv->allowdowngrade;
- int allownamechange = allow_all ? 1 : solv->allownamechange;
- int allowarchchange = allow_all ? 1 : solv->allowarchchange;
- int allowvendorchange = allow_all ? 1 : solv->allowvendorchange;
- if (allow_all == 2)
- {
- allowdowngrade = solv->dup_allowdowngrade;
- allownamechange = solv->dup_allownamechange;
- allowarchchange = solv->dup_allowarchchange;
- allowvendorchange = solv->dup_allowvendorchange;
- }
-
- queue_empty(qs);
-
- n = s - pool->solvables;
-
- /*
- * look for updates for s
- */
- FOR_PROVIDES(p, pp, s->name) /* every provider of s' name */
- {
- if (p == n) /* skip itself */
- continue;
-
- ps = pool->solvables + p;
- if (s->name == ps->name) /* name match */
- {
- if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- if (!allowdowngrade && pool_evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0)
- continue;
- }
- else if (!allownamechange)
- continue;
- else if ((!solv->noupdateprovide || solv->needupdateprovide) && ps->obsoletes) /* provides/obsoletes combination ? */
- {
- /* check if package ps obsoletes installed package s */
- /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
- * use it to limit our update candidates */
- if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, ps))
- continue;
- obsp = ps->repo->idarraydata + ps->obsoletes;
- while ((obs = *obsp++) != 0) /* for all obsoletes */
- {
- FOR_PROVIDES(p2, pp2, obs) /* and all matching providers of the obsoletes */
- {
- Solvable *ps2 = pool->solvables + p2;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs))
- continue;
- 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
- */
- haveprovobs = 1;
- }
- else
- continue;
- if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
- continue;
- if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
- continue;
- queue_push(qs, p);
- }
- if (!allownamechange)
- return;
- /* if we have found some valid candidates and noupdateprovide is not set, we're
- done. otherwise we fallback to all obsoletes */
- if (solv->needupdateprovide || (!solv->noupdateprovide && haveprovobs))
- return;
- if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
- {
- Id *opp;
- for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
- {
- ps = pool->solvables + p;
- if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
- continue;
- if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
- continue;
- /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
- * use it to limit our update candidates */
- if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- queue_push(qs, p);
- }
- }
-}
-
-const char *
-policy_illegal2str(Solver *solv, int illegal, Solvable *s, Solvable *rs)
-{
- Pool *pool = solv->pool;
- const char *str;
- if (illegal == POLICY_ILLEGAL_DOWNGRADE)
- {
- str = pool_tmpjoin(pool, "downgrade of ", pool_solvable2str(pool, s), 0);
- return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
- }
- if (illegal == POLICY_ILLEGAL_NAMECHANGE)
- {
- str = pool_tmpjoin(pool, "name change of ", pool_solvable2str(pool, s), 0);
- return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
- }
- if (illegal == POLICY_ILLEGAL_ARCHCHANGE)
- {
- str = pool_tmpjoin(pool, "architecture change of ", pool_solvable2str(pool, s), 0);
- return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
- }
- if (illegal == POLICY_ILLEGAL_VENDORCHANGE)
- {
- str = pool_tmpjoin(pool, "vendor change from '", pool_id2str(pool, s->vendor), "' (");
- if (rs->vendor)
- {
- str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to '");
- str = pool_tmpappend(pool, str, pool_id2str(pool, rs->vendor), "' (");
- }
- else
- str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to no vendor (");
- return pool_tmpappend(pool, str, pool_solvable2str(pool, rs), ")");
- }
- return "unknown illegal change";
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * Generic policy interface for SAT solver
- * The policy* function can be "overloaded" by defining a callback in the solver struct.
- */
-
-#include "solver.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define POLICY_MODE_CHOOSE 0
-#define POLICY_MODE_RECOMMEND 1
-#define POLICY_MODE_SUGGEST 2
-#define POLICY_MODE_CHOOSE_NOREORDER 3 /* internal, do not use */
-
-
-#define POLICY_ILLEGAL_DOWNGRADE 1
-#define POLICY_ILLEGAL_ARCHCHANGE 2
-#define POLICY_ILLEGAL_VENDORCHANGE 4
-#define POLICY_ILLEGAL_NAMECHANGE 8
-
-extern void policy_filter_unwanted(Solver *solv, Queue *plist, int mode);
-extern int policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2);
-extern int policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2);
-extern int policy_is_illegal(Solver *solv, Solvable *s1, Solvable *s2, int ignore);
-extern void policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allowall);
-extern const char *policy_illegal2str(Solver *solv, int illegal, Solvable *s, Solvable *rs);
-extern void policy_update_recommendsmap(Solver *solv);
-
-extern void policy_create_obsolete_index(Solver *solv);
-
-/* internal, do not use */
-extern void prune_to_best_version(Pool *pool, Queue *plist);
-
-
-#ifdef __cplusplus
-}
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * pool.c
- *
- * The pool contains information about solvables
- * stored optimized for memory consumption and fast retrieval.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "pool.h"
-#include "poolvendor.h"
-#include "repo.h"
-#include "poolid.h"
-#include "poolid_private.h"
-#include "poolarch.h"
-#include "util.h"
-#include "bitmap.h"
-#include "evr.h"
-
-#define SOLVABLE_BLOCK 255
-
-#undef LIBSOLV_KNOWNID_H
-#define KNOWNID_INITIALIZE
-#include "knownid.h"
-#undef KNOWNID_INITIALIZE
-
-/* create pool */
-Pool *
-pool_create(void)
-{
- Pool *pool;
- Solvable *s;
-
- pool = (Pool *)solv_calloc(1, sizeof(*pool));
-
- stringpool_init (&pool->ss, initpool_data);
-
- /* alloc space for RelDep 0 */
- pool->rels = solv_extend_resize(0, 1, sizeof(Reldep), REL_BLOCK);
- pool->nrels = 1;
- memset(pool->rels, 0, sizeof(Reldep));
-
- /* alloc space for Solvable 0 and system solvable */
- pool->solvables = solv_extend_resize(0, 2, sizeof(Solvable), SOLVABLE_BLOCK);
- pool->nsolvables = 2;
- memset(pool->solvables, 0, 2 * sizeof(Solvable));
-
- queue_init(&pool->vendormap);
- queue_init(&pool->pooljobs);
- queue_init(&pool->lazywhatprovidesq);
-
-#if defined(DEBIAN)
- pool->disttype = DISTTYPE_DEB;
- pool->noarchid = ARCH_ALL;
-#elif defined(ARCHLINUX)
- pool->disttype = DISTTYPE_ARCH;
- pool->noarchid = ARCH_ANY;
-#elif defined(HAIKU)
- pool->disttype = DISTTYPE_HAIKU;
- pool->noarchid = ARCH_ANY;
- pool->obsoleteusesprovides = 1;
-#else
- pool->disttype = DISTTYPE_RPM;
- pool->noarchid = ARCH_NOARCH;
-#endif
-
- /* initialize the system solvable */
- s = pool->solvables + SYSTEMSOLVABLE;
- s->name = SYSTEM_SYSTEM;
- s->arch = pool->noarchid;
- s->evr = ID_EMPTY;
-
- pool->debugmask = SOLV_DEBUG_RESULT; /* FIXME */
-#if defined(FEDORA) || defined(MAGEIA)
- pool->implicitobsoleteusescolors = 1;
-#endif
-#ifdef RPM5
- pool->noobsoletesmultiversion = 1;
- pool->forbidselfconflicts = 1;
- pool->obsoleteusesprovides = 1;
- pool->implicitobsoleteusesprovides = 1;
- pool->havedistepoch = 1;
-#endif
- return pool;
-}
-
-
-/* free all the resources of our pool */
-void
-pool_free(Pool *pool)
-{
- int i;
-
- pool_freewhatprovides(pool);
- pool_freeidhashes(pool);
- pool_freeallrepos(pool, 1);
- solv_free(pool->id2arch);
- solv_free(pool->id2color);
- solv_free(pool->solvables);
- stringpool_free(&pool->ss);
- solv_free(pool->rels);
- pool_setvendorclasses(pool, 0);
- queue_free(&pool->vendormap);
- queue_free(&pool->pooljobs);
- queue_free(&pool->lazywhatprovidesq);
- for (i = 0; i < POOL_TMPSPACEBUF; i++)
- solv_free(pool->tmpspace.buf[i]);
- for (i = 0; i < pool->nlanguages; i++)
- free((char *)pool->languages[i]);
- solv_free((void *)pool->languages);
- solv_free(pool->languagecache);
- solv_free(pool->errstr);
- solv_free(pool->rootdir);
- solv_free(pool);
-}
-
-void
-pool_freeallrepos(Pool *pool, int reuseids)
-{
- int i;
-
- pool_freewhatprovides(pool);
- for (i = 1; i < pool->nrepos; i++)
- if (pool->repos[i])
- repo_freedata(pool->repos[i]);
- pool->repos = solv_free(pool->repos);
- pool->nrepos = 0;
- pool->urepos = 0;
- /* the first two solvables don't belong to a repo */
- pool_free_solvable_block(pool, 2, pool->nsolvables - 2, reuseids);
-}
-
-int
-pool_setdisttype(Pool *pool, int disttype)
-{
-#ifdef MULTI_SEMANTICS
- int olddisttype = pool->disttype;
- switch(disttype)
- {
- case DISTTYPE_RPM:
- pool->noarchid = ARCH_NOARCH;
- break;
- case DISTTYPE_DEB:
- pool->noarchid = ARCH_ALL;
- break;
- case DISTTYPE_ARCH:
- case DISTTYPE_HAIKU:
- pool->noarchid = ARCH_ANY;
- break;
- default:
- return -1;
- }
- pool->disttype = disttype;
- pool->solvables[SYSTEMSOLVABLE].arch = pool->noarchid;
- return olddisttype;
-#else
- return pool->disttype == disttype ? disttype : -1;
-#endif
-}
-
-int
-pool_get_flag(Pool *pool, int flag)
-{
- switch (flag)
- {
- case POOL_FLAG_PROMOTEEPOCH:
- return pool->promoteepoch;
- case POOL_FLAG_FORBIDSELFCONFLICTS:
- return pool->forbidselfconflicts;
- case POOL_FLAG_OBSOLETEUSESPROVIDES:
- return pool->obsoleteusesprovides;
- case POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES:
- return pool->implicitobsoleteusesprovides;
- case POOL_FLAG_OBSOLETEUSESCOLORS:
- return pool->obsoleteusescolors;
- case POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS:
- return pool->implicitobsoleteusescolors;
- case POOL_FLAG_NOINSTALLEDOBSOLETES:
- return pool->noinstalledobsoletes;
- case POOL_FLAG_HAVEDISTEPOCH:
- return pool->havedistepoch;
- case POOL_FLAG_NOOBSOLETESMULTIVERSION:
- return pool->noobsoletesmultiversion;
- case POOL_FLAG_ADDFILEPROVIDESFILTERED:
- return pool->addfileprovidesfiltered;
- case POOL_FLAG_NOWHATPROVIDESAUX:
- return pool->nowhatprovidesaux;
- default:
- break;
- }
- return -1;
-}
-
-int
-pool_set_flag(Pool *pool, int flag, int value)
-{
- int old = pool_get_flag(pool, flag);
- switch (flag)
- {
- case POOL_FLAG_PROMOTEEPOCH:
- pool->promoteepoch = value;
- break;
- case POOL_FLAG_FORBIDSELFCONFLICTS:
- pool->forbidselfconflicts = value;
- break;
- case POOL_FLAG_OBSOLETEUSESPROVIDES:
- pool->obsoleteusesprovides = value;
- break;
- case POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES:
- pool->implicitobsoleteusesprovides = value;
- break;
- case POOL_FLAG_OBSOLETEUSESCOLORS:
- pool->obsoleteusescolors = value;
- break;
- case POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS:
- pool->implicitobsoleteusescolors = value;
- break;
- case POOL_FLAG_NOINSTALLEDOBSOLETES:
- pool->noinstalledobsoletes = value;
- break;
- case POOL_FLAG_HAVEDISTEPOCH:
- pool->havedistepoch = value;
- break;
- case POOL_FLAG_NOOBSOLETESMULTIVERSION:
- pool->noobsoletesmultiversion = value;
- break;
- case POOL_FLAG_ADDFILEPROVIDESFILTERED:
- pool->addfileprovidesfiltered = value;
- break;
- case POOL_FLAG_NOWHATPROVIDESAUX:
- pool->nowhatprovidesaux = value;
- break;
- default:
- break;
- }
- return old;
-}
-
-
-Id
-pool_add_solvable(Pool *pool)
-{
- pool->solvables = solv_extend(pool->solvables, pool->nsolvables, 1, sizeof(Solvable), SOLVABLE_BLOCK);
- memset(pool->solvables + pool->nsolvables, 0, sizeof(Solvable));
- return pool->nsolvables++;
-}
-
-Id
-pool_add_solvable_block(Pool *pool, int count)
-{
- Id nsolvables = pool->nsolvables;
- if (!count)
- return nsolvables;
- pool->solvables = solv_extend(pool->solvables, pool->nsolvables, count, sizeof(Solvable), SOLVABLE_BLOCK);
- memset(pool->solvables + nsolvables, 0, sizeof(Solvable) * count);
- pool->nsolvables += count;
- return nsolvables;
-}
-
-void
-pool_free_solvable_block(Pool *pool, Id start, int count, int reuseids)
-{
- if (!count)
- return;
- if (reuseids && start + count == pool->nsolvables)
- {
- /* might want to shrink solvable array */
- pool->nsolvables = start;
- return;
- }
- memset(pool->solvables + start, 0, sizeof(Solvable) * count);
-}
-
-
-void
-pool_set_installed(Pool *pool, Repo *installed)
-{
- if (pool->installed == installed)
- return;
- pool->installed = installed;
- pool_freewhatprovides(pool);
-}
-
-static int
-pool_shrink_whatprovides_sortcmp(const void *ap, const void *bp, void *dp)
-{
- int r;
- Pool *pool = dp;
- Id oa, ob, *da, *db;
- oa = pool->whatprovides[*(Id *)ap];
- ob = pool->whatprovides[*(Id *)bp];
- if (oa == ob)
- return *(Id *)ap - *(Id *)bp;
- da = pool->whatprovidesdata + oa;
- db = pool->whatprovidesdata + ob;
- while (*db)
- if ((r = (*da++ - *db++)) != 0)
- return r;
- if (*da)
- return *da;
- return *(Id *)ap - *(Id *)bp;
-}
-
-/*
- * pool_shrink_whatprovides - unify whatprovides data
- *
- * whatprovides_rel must be empty for this to work!
- *
- */
-static void
-pool_shrink_whatprovides(Pool *pool)
-{
- Id i, n, id;
- Id *sorted;
- Id lastid, *last, *dp, *lp;
- Offset o;
- int r;
-
- if (pool->ss.nstrings < 3)
- return;
- sorted = solv_malloc2(pool->ss.nstrings, sizeof(Id));
- for (i = id = 0; id < pool->ss.nstrings; id++)
- if (pool->whatprovides[id] >= 4)
- sorted[i++] = id;
- n = i;
- solv_sort(sorted, n, sizeof(Id), pool_shrink_whatprovides_sortcmp, pool);
- last = 0;
- lastid = 0;
- for (i = 0; i < n; i++)
- {
- id = sorted[i];
- o = pool->whatprovides[id];
- dp = pool->whatprovidesdata + o;
- if (last)
- {
- lp = last;
- while (*dp)
- if (*dp++ != *lp++)
- {
- last = 0;
- break;
- }
- if (last && *lp)
- last = 0;
- if (last)
- {
- pool->whatprovides[id] = -lastid;
- continue;
- }
- }
- last = pool->whatprovidesdata + o;
- lastid = id;
- }
- solv_free(sorted);
- dp = pool->whatprovidesdata + 4;
- for (id = 1; id < pool->ss.nstrings; id++)
- {
- o = pool->whatprovides[id];
- if (!o)
- continue;
- if ((Id)o < 0)
- {
- i = -(Id)o;
- if (i >= id)
- abort();
- pool->whatprovides[id] = pool->whatprovides[i];
- continue;
- }
- if (o < 4)
- continue;
- lp = pool->whatprovidesdata + o;
- if (lp < dp)
- abort();
- pool->whatprovides[id] = dp - pool->whatprovidesdata;
- while ((*dp++ = *lp++) != 0)
- ;
- }
- o = dp - pool->whatprovidesdata;
- POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesdata from %d to %d\n", pool->whatprovidesdataoff, o);
- if (pool->whatprovidesdataoff == o)
- return;
- r = pool->whatprovidesdataoff - o;
- pool->whatprovidesdataoff = o;
- pool->whatprovidesdata = solv_realloc(pool->whatprovidesdata, (o + pool->whatprovidesdataleft) * sizeof(Id));
- if (r > pool->whatprovidesdataleft)
- r = pool->whatprovidesdataleft;
- memset(pool->whatprovidesdata + o, 0, r * sizeof(Id));
-}
-
-/* this gets rid of all the zeros in the aux */
-static void
-pool_shrink_whatprovidesaux(Pool *pool)
-{
- int num = pool->whatprovidesauxoff;
- Id id;
- Offset newoff;
- Id *op, *wp = pool->whatprovidesauxdata + 1;
- int i;
-
- for (i = 0; i < num; i++)
- {
- Offset o = pool->whatprovidesaux[i];
- if (o < 2)
- continue;
- op = pool->whatprovidesauxdata + o;
- pool->whatprovidesaux[i] = wp - pool->whatprovidesauxdata;
- if (op < wp)
- abort();
- while ((id = *op++) != 0)
- *wp++ = id;
- }
- newoff = wp - pool->whatprovidesauxdata;
- pool->whatprovidesauxdata = solv_realloc(pool->whatprovidesauxdata, newoff * sizeof(Id));
- POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesauxdata from %d to %d\n", pool->whatprovidesauxdataoff, newoff);
- pool->whatprovidesauxdataoff = newoff;
-}
-
-
-/*
- * pool_createwhatprovides()
- *
- * create hashes over pool of solvables to ease provide lookups
- *
- */
-void
-pool_createwhatprovides(Pool *pool)
-{
- int i, num, np, extra;
- Offset off;
- Solvable *s;
- Id id;
- Offset *idp, n;
- Offset *whatprovides;
- Id *whatprovidesdata, *dp, *whatprovidesauxdata;
- Offset *whatprovidesaux;
- Repo *installed = pool->installed;
- unsigned int now;
-
- now = solv_timems(0);
- POOL_DEBUG(SOLV_DEBUG_STATS, "number of solvables: %d, memory used: %d K\n", pool->nsolvables, pool->nsolvables * (int)sizeof(Solvable) / 1024);
- POOL_DEBUG(SOLV_DEBUG_STATS, "number of ids: %d + %d\n", pool->ss.nstrings, pool->nrels);
- POOL_DEBUG(SOLV_DEBUG_STATS, "string memory used: %d K array + %d K data, rel memory used: %d K array\n", pool->ss.nstrings / (1024 / (int)sizeof(Id)), pool->ss.sstrings / 1024, pool->nrels * (int)sizeof(Reldep) / 1024);
- if (pool->ss.stringhashmask || pool->relhashmask)
- POOL_DEBUG(SOLV_DEBUG_STATS, "string hash memory: %d K, rel hash memory : %d K\n", (pool->ss.stringhashmask + 1) / (int)(1024/sizeof(Id)), (pool->relhashmask + 1) / (int)(1024/sizeof(Id)));
-
- pool_freeidhashes(pool); /* XXX: should not be here! */
- pool_freewhatprovides(pool);
- num = pool->ss.nstrings;
- pool->whatprovides = whatprovides = solv_calloc_block(num, sizeof(Offset), WHATPROVIDES_BLOCK);
- pool->whatprovides_rel = solv_calloc_block(pool->nrels, sizeof(Offset), WHATPROVIDES_BLOCK);
-
- /* count providers for each name */
- for (i = pool->nsolvables - 1; i > 0; i--)
- {
- Id *pp;
- s = pool->solvables + i;
- if (!s->provides || !s->repo || s->repo->disabled)
- continue;
- /* we always need the installed solvable in the whatprovides data,
- otherwise obsoletes/conflicts on them won't work */
- if (s->repo != installed && !pool_installable(pool, s))
- continue;
- pp = s->repo->idarraydata + s->provides;
- while ((id = *pp++) != 0)
- {
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- id = rd->name;
- }
- whatprovides[id]++; /* inc count of providers */
- }
- }
-
- off = 4; /* first entry is undef, second is empty list, third is system solvable */
- np = 0; /* number of names provided */
- for (i = 0, idp = whatprovides; i < num; i++, idp++)
- {
- n = *idp;
- if (!n) /* no providers */
- {
- *idp = 1; /* offset for empty list */
- continue;
- }
- off += n; /* make space for all providers */
- *idp = off++; /* now idp points to terminating zero */
- np++; /* inc # of provider 'slots' for stats */
- }
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "provide ids: %d\n", np);
-
- /* reserve some space for relation data */
- extra = 2 * pool->nrels;
- if (extra < 256)
- extra = 256;
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "provide space needed: %d + %d\n", off, extra);
-
- /* alloc space for all providers + extra */
- whatprovidesdata = solv_calloc(off + extra, sizeof(Id));
- whatprovidesdata[2] = SYSTEMSOLVABLE;
-
- /* alloc aux vector */
- whatprovidesauxdata = 0;
- if (!pool->nowhatprovidesaux)
- {
- pool->whatprovidesaux = whatprovidesaux = solv_calloc(num, sizeof(Offset));
- pool->whatprovidesauxoff = num;
- pool->whatprovidesauxdataoff = off;
- pool->whatprovidesauxdata = whatprovidesauxdata = solv_calloc(pool->whatprovidesauxdataoff, sizeof(Id));
- }
-
- /* now fill data for all provides */
- for (i = pool->nsolvables - 1; i > 0; i--)
- {
- Id *pp;
- s = pool->solvables + i;
- if (!s->provides || !s->repo || s->repo->disabled)
- continue;
- if (s->repo != installed && !pool_installable(pool, s))
- continue;
-
- /* for all provides of this solvable */
- pp = s->repo->idarraydata + s->provides;
- while ((id = *pp++) != 0)
- {
- Id auxid = id;
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- id = rd->name;
- }
- dp = whatprovidesdata + whatprovides[id]; /* offset into whatprovidesdata */
- if (*dp != i) /* don't add same solvable twice */
- {
- dp[-1] = i;
- whatprovides[id]--;
- }
- else
- auxid = 1;
- if (whatprovidesauxdata)
- whatprovidesauxdata[whatprovides[id]] = auxid;
- }
- }
- if (pool->whatprovidesaux)
- memcpy(pool->whatprovidesaux, pool->whatprovides, num * sizeof(Id));
- pool->whatprovidesdata = whatprovidesdata;
- pool->whatprovidesdataoff = off;
- pool->whatprovidesdataleft = extra;
- pool_shrink_whatprovides(pool);
- if (pool->whatprovidesaux)
- pool_shrink_whatprovidesaux(pool);
- POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovides memory used: %d K id array, %d K data\n", (pool->ss.nstrings + pool->nrels + WHATPROVIDES_BLOCK) / (int)(1024/sizeof(Id)), (pool->whatprovidesdataoff + pool->whatprovidesdataleft) / (int)(1024/sizeof(Id)));
- if (pool->whatprovidesaux)
- POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovidesaux memory used: %d K id array, %d K data\n", pool->whatprovidesauxoff / (int)(1024/sizeof(Id)), pool->whatprovidesauxdataoff / (int)(1024/sizeof(Id)));
-
- queue_empty(&pool->lazywhatprovidesq);
- if ((!pool->addedfileprovides && pool->disttype == DISTTYPE_RPM) || pool->addedfileprovides == 1)
- {
- if (!pool->addedfileprovides)
- POOL_DEBUG(SOLV_DEBUG_STATS, "WARNING: pool_addfileprovides was not called, this may result in slow operation\n");
- /* lazyly add file provides */
- for (i = 1; i < num; i++)
- {
- const char *str = pool->ss.stringspace + pool->ss.strings[i];
- if (str[0] != '/')
- continue;
- if (pool->addedfileprovides == 1 && repodata_filelistfilter_matches(0, str))
- continue;
- /* setup lazy adding, but remember old value */
- if (pool->whatprovides[i] > 1)
- queue_push2(&pool->lazywhatprovidesq, i, pool->whatprovides[i]);
- pool->whatprovides[i] = 0;
- if (pool->whatprovidesaux)
- pool->whatprovidesaux[i] = 0; /* sorry */
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "lazywhatprovidesq size: %d entries\n", pool->lazywhatprovidesq.count / 2);
- }
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "createwhatprovides took %d ms\n", solv_timems(now));
-}
-
-/*
- * free all of our whatprovides data
- * be careful, everything internalized with pool_queuetowhatprovides is
- * gone, too
- */
-void
-pool_freewhatprovides(Pool *pool)
-{
- pool->whatprovides = solv_free(pool->whatprovides);
- pool->whatprovides_rel = solv_free(pool->whatprovides_rel);
- pool->whatprovidesdata = solv_free(pool->whatprovidesdata);
- pool->whatprovidesdataoff = 0;
- pool->whatprovidesdataleft = 0;
- pool->whatprovidesaux = solv_free(pool->whatprovidesaux);
- pool->whatprovidesauxdata = solv_free(pool->whatprovidesauxdata);
- pool->whatprovidesauxoff = 0;
- pool->whatprovidesauxdataoff = 0;
-}
-
-
-/******************************************************************************/
-
-/*
- * pool_queuetowhatprovides - add queue contents to whatprovidesdata
- *
- * used for whatprovides, jobs, learnt rules, selections
- * input: q: queue of Ids
- * returns: Offset into whatprovidesdata
- *
- */
-
-Id
-pool_ids2whatprovides(Pool *pool, Id *ids, int count)
-{
- Offset off;
-
- if (count == 0) /* queue empty -> 1 */
- return 1;
- if (count == 1 && *ids == SYSTEMSOLVABLE)
- return 2;
-
- /* extend whatprovidesdata if needed, +1 for 0-termination */
- if (pool->whatprovidesdataleft < count + 1)
- {
- POOL_DEBUG(SOLV_DEBUG_STATS, "growing provides hash data...\n");
- pool->whatprovidesdata = solv_realloc(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, ids, count * sizeof(Id));
-
- /* adapt count and 0-terminate */
- pool->whatprovidesdataoff += count;
- pool->whatprovidesdata[pool->whatprovidesdataoff++] = 0;
- pool->whatprovidesdataleft -= count + 1;
-
- return (Id)off;
-}
-
-Id
-pool_queuetowhatprovides(Pool *pool, Queue *q)
-{
- int count = q->count;
- if (count == 0) /* queue empty -> 1 */
- return 1;
- if (count == 1 && q->elements[0] == SYSTEMSOLVABLE)
- return 2;
- return pool_ids2whatprovides(pool, q->elements, count);
-}
-
-
-/*************************************************************************/
-
-#if defined(MULTI_SEMANTICS)
-# define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE)
-#elif defined(DEBIAN)
-# define EVRCMP_DEPCMP EVRCMP_COMPARE
-#else
-# define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE
-#endif
-
-/* check if a package's nevr matches a dependency */
-/* semi-private, called from public pool_match_nevr */
-
-int
-pool_match_nevr_rel(Pool *pool, Solvable *s, Id d)
-{
- Reldep *rd = GETRELDEP(pool, d);
- Id name = rd->name;
- Id evr = rd->evr;
- int flags = rd->flags;
-
- if (flags > 7)
- {
- switch (flags)
- {
- case REL_ARCH:
- if (s->arch != evr)
- {
- if (evr != ARCH_SRC || s->arch != ARCH_NOSRC)
- return 0;
- }
- return pool_match_nevr(pool, s, name);
- case REL_OR:
- if (pool_match_nevr(pool, s, name))
- return 1;
- return pool_match_nevr(pool, s, evr);
- case REL_AND:
- case REL_WITH:
- if (!pool_match_nevr(pool, s, name))
- return 0;
- return pool_match_nevr(pool, s, evr);
- case REL_MULTIARCH:
- if (evr != ARCH_ANY)
- return 0;
- /* XXX : need to check for Multi-Arch: allowed! */
- return pool_match_nevr(pool, s, name);
- default:
- return 0;
- }
- }
- if (!pool_match_nevr(pool, s, name))
- return 0;
- if (evr == s->evr)
- return (flags & REL_EQ) ? 1 : 0;
- if (!flags)
- return 0;
- if (flags == 7)
- return 1;
- switch (pool_evrcmp(pool, s->evr, evr, EVRCMP_DEPCMP))
- {
- case -2:
- return 1;
- case -1:
- return (flags & REL_LT) ? 1 : 0;
- case 0:
- return (flags & REL_EQ) ? 1 : 0;
- case 1:
- return (flags & REL_GT) ? 1 : 0;
- case 2:
- return (flags & REL_EQ) ? 1 : 0;
- default:
- break;
- }
- return 0;
-}
-
-#if defined(HAIKU) || defined(MULTI_SEMANTICS)
-/* forward declaration */
-static int pool_match_flags_evr_rel_compat(Pool *pool, Reldep *range, int flags, int evr);
-#endif
-
-/* match (flags, evr) against provider (pflags, pevr) */
-static inline int
-pool_match_flags_evr(Pool *pool, int pflags, Id pevr, int flags, int evr)
-{
- if (!pflags || !flags || pflags >= 8 || flags >= 8)
- return 0;
- if (flags == 7 || pflags == 7)
- return 1; /* rel provides every version */
- if ((pflags & flags & (REL_LT | REL_GT)) != 0)
- return 1; /* both rels show in the same direction */
- if (pevr == evr)
- return (flags & pflags & REL_EQ) ? 1 : 0;
-#if defined(HAIKU) || defined(MULTI_SEMANTICS)
- if (ISRELDEP(pevr))
- {
- Reldep *rd = GETRELDEP(pool, pevr);
- if (rd->flags == REL_COMPAT)
- return pool_match_flags_evr_rel_compat(pool, rd, flags, evr);
- }
-#endif
- switch (pool_evrcmp(pool, pevr, evr, EVRCMP_DEPCMP))
- {
- case -2:
- return (pflags & REL_EQ) ? 1 : 0;
- case -1:
- return (flags & REL_LT) || (pflags & REL_GT) ? 1 : 0;
- case 0:
- return (flags & pflags & REL_EQ) ? 1 : 0;
- case 1:
- return (flags & REL_GT) || (pflags & REL_LT) ? 1 : 0;
- case 2:
- return (flags & REL_EQ) ? 1 : 0;
- default:
- break;
- }
- return 0;
-}
-
-#if defined(HAIKU) || defined(MULTI_SEMANTICS)
-static int
-pool_match_flags_evr_rel_compat(Pool *pool, Reldep *range, int flags, int evr)
-{
- /* range->name is the actual version, range->evr the backwards compatibility
- version. If flags are '>=' or '>', we match the compatibility version
- as well, otherwise only the actual version. */
- if (!(flags & REL_GT) || (flags & REL_LT))
- return pool_match_flags_evr(pool, REL_EQ, range->name, flags, evr);
- return pool_match_flags_evr(pool, REL_LT | REL_EQ, range->name, flags, evr) &&
- pool_match_flags_evr(pool, REL_GT | REL_EQ, range->evr, REL_EQ, evr);
-}
-#endif
-
-/* public (i.e. not inlined) version of pool_match_flags_evr */
-int
-pool_intersect_evrs(Pool *pool, int pflags, Id pevr, int flags, int evr)
-{
- return pool_match_flags_evr(pool, pflags, pevr, flags, evr);
-}
-
-/* match two dependencies (d1 = provider) */
-
-int
-pool_match_dep(Pool *pool, Id d1, Id d2)
-{
- Reldep *rd1, *rd2;
-
- if (d1 == d2)
- return 1;
- if (!ISRELDEP(d1))
- {
- if (!ISRELDEP(d2))
- return 0;
- rd2 = GETRELDEP(pool, d2);
- return pool_match_dep(pool, d1, rd2->name);
- }
- rd1 = GETRELDEP(pool, d1);
- if (!ISRELDEP(d2))
- {
- return pool_match_dep(pool, rd1->name, d2);
- }
- rd2 = GETRELDEP(pool, d2);
- /* first match name */
- if (!pool_match_dep(pool, rd1->name, rd2->name))
- return 0;
- /* name matches, check flags and evr */
- return pool_intersect_evrs(pool, rd1->flags, rd1->evr, rd2->flags, rd2->evr);
-}
-
-Id
-pool_searchlazywhatprovidesq(Pool *pool, Id d)
-{
- int start = 0;
- int end = pool->lazywhatprovidesq.count;
- Id *elements;
- if (!end)
- return 0;
- elements = pool->lazywhatprovidesq.elements;
- while (end - start > 16)
- {
- int mid = (start + end) / 2 & ~1;
- if (elements[mid] == d)
- return elements[mid + 1];
- if (elements[mid] < d)
- start = mid + 2;
- else
- end = mid;
- }
- for (; start < end; start += 2)
- if (elements[start] == d)
- return elements[start + 1];
- return 0;
-}
-
-/*
- * addstdproviders
- *
- * lazy populating of the whatprovides array, non relation case
- */
-static Id
-pool_addstdproviders(Pool *pool, Id d)
-{
- const char *str;
- Queue q;
- Id qbuf[16];
- Dataiterator di;
- Id oldoffset;
-
- if (pool->addedfileprovides == 2)
- {
- pool->whatprovides[d] = 1;
- return 1;
- }
- str = pool->ss.stringspace + pool->ss.strings[d];
- if (*str != '/')
- {
- pool->whatprovides[d] = 1;
- return 1;
- }
- queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
- dataiterator_init(&di, pool, 0, 0, SOLVABLE_FILELIST, str, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
- for (; dataiterator_step(&di); dataiterator_skip_solvable(&di))
- {
- Solvable *s = pool->solvables + di.solvid;
- /* XXX: maybe should add a provides dependency to the solvables
- * OTOH this is only needed for rel deps that filter the provides,
- * and those should not use filelist entries */
- if (s->repo->disabled)
- continue;
- if (s->repo != pool->installed && !pool_installable(pool, s))
- continue;
- queue_push(&q, di.solvid);
- }
- dataiterator_free(&di);
- oldoffset = pool_searchlazywhatprovidesq(pool, d);
- if (!q.count)
- pool->whatprovides[d] = oldoffset ? oldoffset : 1;
- else
- {
- if (oldoffset)
- {
- Id *oo = pool->whatprovidesdata + oldoffset;
- int i;
- /* unify both queues. easy, as we know both are sorted */
- for (i = 0; i < q.count; i++)
- {
- if (*oo > q.elements[i])
- continue;
- if (*oo < q.elements[i])
- queue_insert(&q, i, *oo);
- oo++;
- if (!*oo)
- break;
- }
- while (*oo)
- queue_push(&q, *oo++);
- if (q.count == oo - (pool->whatprovidesdata + oldoffset))
- {
- /* end result has same size as oldoffset -> no new entries */
- queue_free(&q);
- pool->whatprovides[d] = oldoffset;
- return oldoffset;
- }
- }
- pool->whatprovides[d] = pool_queuetowhatprovides(pool, &q);
- }
- queue_free(&q);
- return pool->whatprovides[d];
-}
-
-
-static inline int
-pool_is_kind(Pool *pool, Id name, Id kind)
-{
- const char *n;
- if (!kind)
- return 1;
- n = pool_id2str(pool, name);
- if (kind != 1)
- {
- const char *kn = pool_id2str(pool, kind);
- int knl = strlen(kn);
- return !strncmp(n, kn, knl) && n[knl] == ':' ? 1 : 0;
- }
- else
- {
- if (*n == ':')
- return 1;
- while(*n >= 'a' && *n <= 'z')
- n++;
- return *n == ':' ? 0 : 1;
- }
-}
-
-/*
- * addrelproviders
- *
- * add packages fulfilling the relation to whatprovides array
- *
- * some words about REL_AND and REL_IF: we assume the best case
- * here, so that you get a "potential" result if you ask for a match.
- * E.g. if you ask for "whatrequires A" and package X contains
- * "Requires: A & B", you'll get "X" as an answer.
- */
-Id
-pool_addrelproviders(Pool *pool, Id d)
-{
- Reldep *rd;
- Reldep *prd;
- Queue plist;
- Id buf[16];
- Id name, evr, flags;
- Id pid, *pidp;
- Id p, *pp;
-
- if (!ISRELDEP(d))
- return pool_addstdproviders(pool, d);
- rd = GETRELDEP(pool, d);
- name = rd->name;
- evr = rd->evr;
- flags = rd->flags;
- d = GETRELID(d);
- queue_init_buffer(&plist, buf, sizeof(buf)/sizeof(*buf));
-
- if (flags >= 8)
- {
- /* special relation */
- Id wp = 0;
- Id *pp2, *pp3;
-
- switch (flags)
- {
- case REL_WITH:
- wp = pool_whatprovides(pool, name);
- pp2 = pool_whatprovides_ptr(pool, evr);
- pp = pool->whatprovidesdata + wp;
- while ((p = *pp++) != 0)
- {
- for (pp3 = pp2; *pp3; pp3++)
- if (*pp3 == p)
- break;
- if (*pp3)
- queue_push(&plist, p); /* found it */
- else
- wp = 0;
- }
- break;
-
- case REL_AND:
- case REL_OR:
- case REL_COND:
- if (flags == REL_COND)
- {
- if (ISRELDEP(evr))
- {
- Reldep *rd2 = GETRELDEP(pool, evr);
- evr = rd2->flags == REL_ELSE ? rd2->evr : 0;
- }
- else
- evr = 0; /* assume cond is true */
- }
- wp = pool_whatprovides(pool, name);
- if (!pool->whatprovidesdata[wp])
- wp = evr ? pool_whatprovides(pool, evr) : 1;
- else if (evr)
- {
- /* sorted merge */
- pp2 = pool_whatprovides_ptr(pool, evr);
- pp = pool->whatprovidesdata + wp;
- while (*pp && *pp2)
- {
- if (*pp < *pp2)
- queue_push(&plist, *pp++);
- else
- {
- if (*pp == *pp2)
- pp++;
- queue_push(&plist, *pp2++);
- }
- }
- while (*pp)
- queue_push(&plist, *pp++);
- while (*pp2)
- queue_push(&plist, *pp2++);
- /* if the number of elements did not change, we can reuse wp */
- if (pp - (pool->whatprovidesdata + wp) != plist.count)
- wp = 0;
- }
- break;
-
- case REL_NAMESPACE:
- if (name == NAMESPACE_OTHERPROVIDERS)
- {
- wp = pool_whatprovides(pool, evr);
- break;
- }
- if (pool->nscallback)
- {
- /* ask callback which packages provide the dependency
- * 0: none
- * 1: the system (aka SYSTEMSOLVABLE)
- * >1: set of packages, stored as offset on whatprovidesdata
- */
- p = pool->nscallback(pool, pool->nscallbackdata, name, evr);
- if (p > 1)
- wp = p;
- if (p == 1)
- queue_push(&plist, SYSTEMSOLVABLE);
- }
- break;
- case REL_ARCH:
- /* small hack: make it possible to match <pkg>.src
- * we have to iterate over the solvables as src packages do not
- * provide anything, thus they are not indexed in our
- * whatprovides hash */
- if (evr == ARCH_SRC || evr == ARCH_NOSRC)
- {
- Solvable *s;
- for (p = 1, s = pool->solvables + p; p < pool->nsolvables; p++, s++)
- {
- if (!s->repo)
- continue;
- if (s->arch != evr && s->arch != ARCH_NOSRC)
- continue;
- if (pool_disabled_solvable(pool, s))
- continue;
- if (!name || pool_match_nevr(pool, s, name))
- queue_push(&plist, p);
- }
- break;
- }
- if (!name)
- {
- FOR_POOL_SOLVABLES(p)
- {
- Solvable *s = pool->solvables + p;
- if (s->repo != pool->installed && !pool_installable(pool, s))
- continue;
- if (s->arch == evr)
- queue_push(&plist, p);
- }
- break;
- }
- wp = pool_whatprovides(pool, name);
- pp = pool->whatprovidesdata + wp;
- while ((p = *pp++) != 0)
- {
- Solvable *s = pool->solvables + p;
- if (s->arch == evr)
- queue_push(&plist, p);
- else
- wp = 0;
- }
- break;
- case REL_MULTIARCH:
- if (evr != ARCH_ANY)
- break;
- /* XXX : need to check for Multi-Arch: allowed! */
- wp = pool_whatprovides(pool, name);
- break;
- case REL_KIND:
- /* package kind filtering */
- if (!name)
- {
- FOR_POOL_SOLVABLES(p)
- {
- Solvable *s = pool->solvables + p;
- if (s->repo != pool->installed && !pool_installable(pool, s))
- continue;
- if (pool_is_kind(pool, s->name, evr))
- queue_push(&plist, p);
- }
- break;
- }
- wp = pool_whatprovides(pool, name);
- pp = pool->whatprovidesdata + wp;
- while ((p = *pp++) != 0)
- {
- Solvable *s = pool->solvables + p;
- if (pool_is_kind(pool, s->name, evr))
- queue_push(&plist, p);
- else
- wp = 0;
- }
- break;
- case REL_FILECONFLICT:
- pp = pool_whatprovides_ptr(pool, name);
- while ((p = *pp++) != 0)
- {
- Id origd = MAKERELDEP(d);
- Solvable *s = pool->solvables + p;
- if (!s->provides)
- continue;
- pidp = s->repo->idarraydata + s->provides;
- while ((pid = *pidp++) != 0)
- if (pid == origd)
- break;
- if (pid)
- queue_push(&plist, p);
- }
- break;
- default:
- break;
- }
- if (wp)
- {
- /* we can reuse an existing entry */
- queue_free(&plist);
- pool->whatprovides_rel[d] = wp;
- return wp;
- }
- }
- else if (flags)
- {
- Id *ppaux = 0;
- /* simple version comparison relation */
-#if 0
- POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: what provides %s?\n", pool_dep2str(pool, name));
-#endif
- pp = pool_whatprovides_ptr(pool, name);
- if (!ISRELDEP(name) && name < pool->whatprovidesauxoff)
- ppaux = pool->whatprovidesaux[name] ? pool->whatprovidesauxdata + pool->whatprovidesaux[name] : 0;
- while (ISRELDEP(name))
- {
- rd = GETRELDEP(pool, name);
- name = rd->name;
- }
- while ((p = *pp++) != 0)
- {
- Solvable *s = pool->solvables + p;
- if (ppaux)
- {
- pid = *ppaux++;
- if (pid && pid != 1)
- {
-#if 0
- POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: aux hit %d %s\n", p, pool_dep2str(pool, pid));
-#endif
- if (!ISRELDEP(pid))
- {
- if (pid != name)
- continue; /* wrong provides name */
- if (pool->disttype == DISTTYPE_DEB)
- continue; /* unversioned provides can never match versioned deps */
- }
- else
- {
- prd = GETRELDEP(pool, pid);
- if (prd->name != name)
- continue; /* wrong provides name */
- /* right package, both deps are rels. check flags/evr */
- if (!pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr))
- continue;
- }
- queue_push(&plist, p);
- continue;
- }
- }
- if (!s->provides)
- {
- /* no provides - check nevr */
- if (pool_match_nevr_rel(pool, s, MAKERELDEP(d)))
- queue_push(&plist, p);
- continue;
- }
- /* solvable p provides name in some rels */
- pidp = s->repo->idarraydata + s->provides;
- while ((pid = *pidp++) != 0)
- {
- if (!ISRELDEP(pid))
- {
- if (pid != name)
- continue; /* wrong provides name */
- if (pool->disttype == DISTTYPE_DEB)
- continue; /* unversioned provides can never match versioned deps */
- break;
- }
- prd = GETRELDEP(pool, pid);
- if (prd->name != name)
- continue; /* wrong provides name */
- /* right package, both deps are rels. check flags/evr */
- if (pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr))
- break; /* matches */
- }
- if (!pid)
- continue; /* none of the providers matched */
- queue_push(&plist, p);
- }
- /* make our system solvable provide all unknown rpmlib() stuff */
- if (plist.count == 0 && !strncmp(pool_id2str(pool, name), "rpmlib(", 7))
- queue_push(&plist, SYSTEMSOLVABLE);
- }
- /* add providers to whatprovides */
-#if 0
- POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: adding %d packages to %d\n", plist.count, d);
-#endif
- pool->whatprovides_rel[d] = pool_queuetowhatprovides(pool, &plist);
- queue_free(&plist);
-
- return pool->whatprovides_rel[d];
-}
-
-void
-pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr)
-{
- int nrels = pool->nrels;
- Id d;
- Reldep *rd;
-
- if (!pool->whatprovides_rel)
- return;
- for (d = 1, rd = pool->rels + d; d < nrels; d++, rd++)
- {
- if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS)
- continue;
- if (ns && rd->name != ns)
- continue;
- if (evr && rd->evr != evr)
- continue;
- pool->whatprovides_rel[d] = 0;
- }
-}
-
-/* intersect dependencies in keyname with dep, return list of matching packages */
-void
-pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker)
-{
- Id p;
-
- queue_empty(q);
- FOR_POOL_SOLVABLES(p)
- {
- Solvable *s = pool->solvables + p;
- if (s->repo->disabled)
- continue;
- if (s->repo != pool->installed && !pool_installable(pool, s))
- continue;
- if (solvable_matchesdep(s, keyname, dep, marker))
- queue_push(q, p);
- }
-}
-
-/*************************************************************************/
-
-void
-pool_debug(Pool *pool, int type, const char *format, ...)
-{
- va_list args;
- char buf[1024];
-
- if ((type & (SOLV_FATAL|SOLV_ERROR)) == 0)
- {
- if ((pool->debugmask & type) == 0)
- return;
- }
- va_start(args, format);
- if (!pool->debugcallback)
- {
- if ((type & (SOLV_FATAL|SOLV_ERROR)) == 0 && !(pool->debugmask & SOLV_DEBUG_TO_STDERR))
- vprintf(format, args);
- else
- vfprintf(stderr, format, args);
- return;
- }
- vsnprintf(buf, sizeof(buf), format, args);
- va_end(args);
- pool->debugcallback(pool, pool->debugcallbackdata, type, buf);
-}
-
-int
-pool_error(Pool *pool, int ret, const char *format, ...)
-{
- va_list args;
- int l;
- va_start(args, format);
- if (!pool->errstr)
- {
- pool->errstra = 1024;
- pool->errstr = solv_malloc(pool->errstra);
- }
- if (!*format)
- {
- *pool->errstr = 0;
- l = 0;
- }
- else
- l = vsnprintf(pool->errstr, pool->errstra, format, args);
- va_end(args);
- if (l >= 0 && l + 1 > pool->errstra)
- {
- pool->errstra = l + 256;
- pool->errstr = solv_realloc(pool->errstr, pool->errstra);
- va_start(args, format);
- l = vsnprintf(pool->errstr, pool->errstra, format, args);
- va_end(args);
- }
- if (l < 0)
- strcpy(pool->errstr, "unknown error");
- if (pool->debugmask & SOLV_ERROR)
- pool_debug(pool, SOLV_ERROR, "%s\n", pool->errstr);
- return ret;
-}
-
-char *
-pool_errstr(Pool *pool)
-{
- return pool->errstr ? pool->errstr : "no error";
-}
-
-void
-pool_setdebuglevel(Pool *pool, int level)
-{
- int mask = SOLV_DEBUG_RESULT;
- if (level > 0)
- mask |= SOLV_DEBUG_STATS|SOLV_DEBUG_ANALYZE|SOLV_DEBUG_UNSOLVABLE|SOLV_DEBUG_SOLVER|SOLV_DEBUG_TRANSACTION|SOLV_ERROR;
- if (level > 1)
- mask |= SOLV_DEBUG_JOB|SOLV_DEBUG_SOLUTIONS|SOLV_DEBUG_POLICY;
- if (level > 2)
- mask |= SOLV_DEBUG_PROPAGATE;
- if (level > 3)
- mask |= SOLV_DEBUG_RULE_CREATION;
- mask |= pool->debugmask & SOLV_DEBUG_TO_STDERR; /* keep bit */
- pool->debugmask = mask;
-}
-
-void pool_setdebugcallback(Pool *pool, void (*debugcallback)(struct _Pool *, void *data, int type, const char *str), void *debugcallbackdata)
-{
- pool->debugcallback = debugcallback;
- pool->debugcallbackdata = debugcallbackdata;
-}
-
-void pool_setdebugmask(Pool *pool, int mask)
-{
- pool->debugmask = mask;
-}
-
-void pool_setloadcallback(Pool *pool, int (*cb)(struct _Pool *, struct _Repodata *, void *), void *loadcbdata)
-{
- pool->loadcallback = cb;
- pool->loadcallbackdata = loadcbdata;
-}
-
-void pool_setnamespacecallback(Pool *pool, Id (*cb)(struct _Pool *, void *, Id, Id), void *nscbdata)
-{
- pool->nscallback = cb;
- pool->nscallbackdata = nscbdata;
-}
-
-/*************************************************************************/
-
-struct searchfiles {
- Id *ids;
- int nfiles;
- Map seen;
-};
-
-#define SEARCHFILES_BLOCK 127
-
-static void
-pool_addfileprovides_dep(Pool *pool, Id *ida, struct searchfiles *sf, struct searchfiles *isf)
-{
- Id dep, sid;
- const char *s;
- struct searchfiles *csf;
-
- while ((dep = *ida++) != 0)
- {
- csf = sf;
- while (ISRELDEP(dep))
- {
- Reldep *rd;
- sid = pool->ss.nstrings + GETRELID(dep);
- if (MAPTST(&csf->seen, sid))
- {
- dep = 0;
- break;
- }
- MAPSET(&csf->seen, sid);
- rd = GETRELDEP(pool, dep);
- if (rd->flags < 8)
- dep = rd->name;
- else if (rd->flags == REL_NAMESPACE)
- {
- if (rd->name == NAMESPACE_SPLITPROVIDES)
- {
- csf = isf;
- if (!csf || MAPTST(&csf->seen, sid))
- {
- dep = 0;
- break;
- }
- MAPSET(&csf->seen, sid);
- }
- dep = rd->evr;
- }
- else if (rd->flags == REL_FILECONFLICT)
- {
- dep = 0;
- break;
- }
- else
- {
- Id ids[2];
- ids[0] = rd->name;
- ids[1] = 0;
- pool_addfileprovides_dep(pool, ids, csf, isf);
- dep = rd->evr;
- }
- }
- if (!dep)
- continue;
- if (MAPTST(&csf->seen, dep))
- continue;
- MAPSET(&csf->seen, dep);
- s = pool_id2str(pool, dep);
- if (*s != '/')
- continue;
- if (csf != isf && pool->addedfileprovides == 1 && !repodata_filelistfilter_matches(0, s))
- continue; /* skip non-standard locations csf == isf: installed case */
- csf->ids = solv_extend(csf->ids, csf->nfiles, 1, sizeof(Id), SEARCHFILES_BLOCK);
- csf->ids[csf->nfiles++] = dep;
- }
-}
-
-struct addfileprovides_cbdata {
- int nfiles;
- Id *ids;
- char **dirs;
- char **names;
-
- Id *dids;
-
- Map providedids;
-
- Map useddirs;
-};
-
-static int
-addfileprovides_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
-{
- struct addfileprovides_cbdata *cbd = cbdata;
- int i;
-
- if (!cbd->useddirs.size)
- {
- map_init(&cbd->useddirs, data->dirpool.ndirs + 1);
- if (!cbd->dirs)
- {
- cbd->dirs = solv_malloc2(cbd->nfiles, sizeof(char *));
- cbd->names = solv_malloc2(cbd->nfiles, sizeof(char *));
- for (i = 0; i < cbd->nfiles; i++)
- {
- char *s = solv_strdup(pool_id2str(data->repo->pool, cbd->ids[i]));
- cbd->dirs[i] = s;
- s = strrchr(s, '/');
- *s = 0;
- cbd->names[i] = s + 1;
- }
- }
- for (i = 0; i < cbd->nfiles; i++)
- {
- Id did;
- if (MAPTST(&cbd->providedids, cbd->ids[i]))
- {
- cbd->dids[i] = 0;
- continue;
- }
- did = repodata_str2dir(data, cbd->dirs[i], 0);
- cbd->dids[i] = did;
- if (did)
- MAPSET(&cbd->useddirs, did);
- }
- repodata_free_dircache(data);
- }
- if (value->id >= data->dirpool.ndirs || !MAPTST(&cbd->useddirs, value->id))
- return 0;
- for (i = 0; i < cbd->nfiles; i++)
- if (cbd->dids[i] == value->id && !strcmp(cbd->names[i], value->str))
- s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER);
- return 0;
-}
-
-static void
-pool_addfileprovides_search(Pool *pool, struct addfileprovides_cbdata *cbd, struct searchfiles *sf, Repo *repoonly)
-{
- Id p;
- Repodata *data;
- Repo *repo;
- Queue fileprovidesq;
- int i, j, repoid, repodataid;
- int provstart, provend;
- Map donemap;
- int ndone, incomplete;
-
- if (!pool->urepos)
- return;
-
- cbd->nfiles = sf->nfiles;
- cbd->ids = sf->ids;
- cbd->dirs = 0;
- cbd->names = 0;
- cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id));
- map_init(&cbd->providedids, pool->ss.nstrings);
-
- repoid = 1;
- repo = repoonly ? repoonly : pool->repos[repoid];
- map_init(&donemap, pool->nsolvables);
- queue_init(&fileprovidesq);
- provstart = provend = 0;
- for (;;)
- {
- if (!repo || repo->disabled)
- {
- if (repoonly || ++repoid == pool->nrepos)
- break;
- repo = pool->repos[repoid];
- continue;
- }
- ndone = 0;
- FOR_REPODATAS(repo, repodataid, data)
- {
- if (ndone >= repo->nsolvables)
- break;
-
- if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
- {
- map_empty(&cbd->providedids);
- for (i = 0; i < fileprovidesq.count; i++)
- MAPSET(&cbd->providedids, fileprovidesq.elements[i]);
- provstart = data->start;
- provend = data->end;
- for (i = 0; i < cbd->nfiles; i++)
- if (!MAPTST(&cbd->providedids, cbd->ids[i]))
- break;
- if (i == cbd->nfiles)
- {
- /* great! no need to search files */
- for (p = data->start; p < data->end; p++)
- if (pool->solvables[p].repo == repo)
- {
- if (MAPTST(&donemap, p))
- continue;
- MAPSET(&donemap, p);
- ndone++;
- }
- continue;
- }
- }
-
- if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
- continue;
-
- if (data->start < provstart || data->end > provend)
- {
- map_empty(&cbd->providedids);
- provstart = provend = 0;
- }
-
- /* check if the data is incomplete */
- incomplete = 0;
- if (data->state == REPODATA_AVAILABLE)
- {
- for (j = 1; j < data->nkeys; j++)
- if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
- break;
- if (j < data->nkeys)
- {
-#if 0
- for (i = 0; i < cbd->nfiles; i++)
- if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
- printf("need complete filelist because of %s\n", pool_id2str(pool, cbd->ids[i]));
-#endif
- for (i = 0; i < cbd->nfiles; i++)
- if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
- break;
- if (i < cbd->nfiles)
- incomplete = 1;
- }
- }
-
- /* do the search */
- map_init(&cbd->useddirs, 0);
- for (p = data->start; p < data->end; p++)
- if (pool->solvables[p].repo == repo)
- {
- if (MAPTST(&donemap, p))
- continue;
- repodata_search(data, p, SOLVABLE_FILELIST, 0, addfileprovides_cb, cbd);
- if (!incomplete)
- {
- MAPSET(&donemap, p);
- ndone++;
- }
- }
- map_free(&cbd->useddirs);
- }
-
- if (repoonly || ++repoid == pool->nrepos)
- break;
- repo = pool->repos[repoid];
- }
- map_free(&donemap);
- queue_free(&fileprovidesq);
- map_free(&cbd->providedids);
- if (cbd->dirs)
- {
- for (i = 0; i < cbd->nfiles; i++)
- solv_free(cbd->dirs[i]);
- cbd->dirs = solv_free(cbd->dirs);
- cbd->names = solv_free(cbd->names);
- }
-}
-
-void
-pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst)
-{
- Solvable *s;
- Repo *installed, *repo;
- struct searchfiles sf, isf, *isfp;
- struct addfileprovides_cbdata cbd;
- int i;
- unsigned int now;
-
- installed = pool->installed;
- now = solv_timems(0);
- memset(&sf, 0, sizeof(sf));
- map_init(&sf.seen, pool->ss.nstrings + pool->nrels);
- memset(&isf, 0, sizeof(isf));
- map_init(&isf.seen, pool->ss.nstrings + pool->nrels);
- pool->addedfileprovides = pool->addfileprovidesfiltered ? 1 : 2;
-
- if (idq)
- queue_empty(idq);
- if (idqinst)
- queue_empty(idqinst);
- isfp = installed ? &isf : 0;
- for (i = 1, s = pool->solvables + i; i < pool->nsolvables; i++, s++)
- {
- repo = s->repo;
- if (!repo)
- continue;
- if (s->obsoletes)
- pool_addfileprovides_dep(pool, repo->idarraydata + s->obsoletes, &sf, isfp);
- if (s->conflicts)
- pool_addfileprovides_dep(pool, repo->idarraydata + s->conflicts, &sf, isfp);
- if (s->requires)
- pool_addfileprovides_dep(pool, repo->idarraydata + s->requires, &sf, isfp);
- if (s->recommends)
- pool_addfileprovides_dep(pool, repo->idarraydata + s->recommends, &sf, isfp);
- if (s->suggests)
- pool_addfileprovides_dep(pool, repo->idarraydata + s->suggests, &sf, isfp);
- if (s->supplements)
- pool_addfileprovides_dep(pool, repo->idarraydata + s->supplements, &sf, isfp);
- if (s->enhances)
- pool_addfileprovides_dep(pool, repo->idarraydata + s->enhances, &sf, isfp);
- }
- map_free(&sf.seen);
- map_free(&isf.seen);
- POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file dependencies, %d installed file dependencies\n", sf.nfiles, isf.nfiles);
- cbd.dids = 0;
- if (sf.nfiles)
- {
-#if 0
- for (i = 0; i < sf.nfiles; i++)
- POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in filelist\n", pool_id2str(pool, sf.ids[i]));
-#endif
- pool_addfileprovides_search(pool, &cbd, &sf, 0);
- if (idq)
- for (i = 0; i < sf.nfiles; i++)
- queue_push(idq, sf.ids[i]);
- if (idqinst)
- for (i = 0; i < sf.nfiles; i++)
- queue_push(idqinst, sf.ids[i]);
- solv_free(sf.ids);
- }
- if (isf.nfiles)
- {
-#if 0
- for (i = 0; i < isf.nfiles; i++)
- POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in installed filelist\n", pool_id2str(pool, isf.ids[i]));
-#endif
- if (installed)
- pool_addfileprovides_search(pool, &cbd, &isf, installed);
- if (installed && idqinst)
- for (i = 0; i < isf.nfiles; i++)
- queue_pushunique(idqinst, isf.ids[i]);
- solv_free(isf.ids);
- }
- solv_free(cbd.dids);
- pool_freewhatprovides(pool); /* as we have added provides */
- POOL_DEBUG(SOLV_DEBUG_STATS, "addfileprovides took %d ms\n", solv_timems(now));
-}
-
-void
-pool_addfileprovides(Pool *pool)
-{
- pool_addfileprovides_queue(pool, 0, 0);
-}
-
-void
-pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, struct _Repodata *data, struct _Repokey *key, struct _KeyValue *kv), void *cbdata)
-{
- if (p)
- {
- if (pool->solvables[p].repo)
- repo_search(pool->solvables[p].repo, p, key, match, flags, callback, cbdata);
- return;
- }
- /* FIXME: obey callback return value! */
- for (p = 1; p < pool->nsolvables; p++)
- if (pool->solvables[p].repo)
- repo_search(pool->solvables[p].repo, p, key, match, flags, callback, cbdata);
-}
-
-void
-pool_clear_pos(Pool *pool)
-{
- memset(&pool->pos, 0, sizeof(pool->pos));
-}
-
-
-void
-pool_set_languages(Pool *pool, const char **languages, int nlanguages)
-{
- int i;
-
- pool->languagecache = solv_free(pool->languagecache);
- pool->languagecacheother = 0;
- for (i = 0; i < pool->nlanguages; i++)
- free((char *)pool->languages[i]);
- pool->languages = solv_free((void *)pool->languages);
- pool->nlanguages = nlanguages;
- if (!nlanguages)
- return;
- pool->languages = solv_calloc(nlanguages, sizeof(const char **));
- for (i = 0; i < pool->nlanguages; i++)
- pool->languages[i] = solv_strdup(languages[i]);
-}
-
-Id
-pool_id2langid(Pool *pool, Id id, const char *lang, int create)
-{
- const char *n;
- char buf[256], *p;
- int l;
-
- if (!lang || !*lang)
- return id;
- n = pool_id2str(pool, id);
- l = strlen(n) + strlen(lang) + 2;
- if (l > sizeof(buf))
- p = solv_malloc(strlen(n) + strlen(lang) + 2);
- else
- p = buf;
- sprintf(p, "%s:%s", n, lang);
- id = pool_str2id(pool, p, create);
- if (p != buf)
- free(p);
- return id;
-}
-
-char *
-pool_alloctmpspace(Pool *pool, int len)
-{
- int n = pool->tmpspace.n;
- if (!len)
- return 0;
- if (len > pool->tmpspace.len[n])
- {
- pool->tmpspace.buf[n] = solv_realloc(pool->tmpspace.buf[n], len + 32);
- pool->tmpspace.len[n] = len + 32;
- }
- pool->tmpspace.n = (n + 1) % POOL_TMPSPACEBUF;
- return pool->tmpspace.buf[n];
-}
-
-static char *
-pool_alloctmpspace_free(Pool *pool, const char *space, int len)
-{
- if (space)
- {
- int n, oldn;
- n = oldn = pool->tmpspace.n;
- for (;;)
- {
- if (!n--)
- n = POOL_TMPSPACEBUF - 1;
- if (n == oldn)
- break;
- if (pool->tmpspace.buf[n] != space)
- continue;
- if (len > pool->tmpspace.len[n])
- {
- pool->tmpspace.buf[n] = solv_realloc(pool->tmpspace.buf[n], len + 32);
- pool->tmpspace.len[n] = len + 32;
- }
- return pool->tmpspace.buf[n];
- }
- }
- return 0;
-}
-
-void
-pool_freetmpspace(Pool *pool, const char *space)
-{
- int n = pool->tmpspace.n;
- if (!space)
- return;
- n = (n + (POOL_TMPSPACEBUF - 1)) % POOL_TMPSPACEBUF;
- if (pool->tmpspace.buf[n] == space)
- pool->tmpspace.n = n;
-}
-
-char *
-pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const char *str3)
-{
- int l1, l2, l3;
- char *s, *str;
- l1 = str1 ? strlen(str1) : 0;
- l2 = str2 ? strlen(str2) : 0;
- l3 = str3 ? strlen(str3) : 0;
- s = str = pool_alloctmpspace(pool, l1 + l2 + l3 + 1);
- if (l1)
- {
- strcpy(s, str1);
- s += l1;
- }
- if (l2)
- {
- strcpy(s, str2);
- s += l2;
- }
- if (l3)
- {
- strcpy(s, str3);
- s += l3;
- }
- *s = 0;
- return str;
-}
-
-char *
-pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3)
-{
- int l1, l2, l3;
- char *s, *str;
-
- l1 = str1 ? strlen(str1) : 0;
- l2 = str2 ? strlen(str2) : 0;
- l3 = str3 ? strlen(str3) : 0;
- str = pool_alloctmpspace_free(pool, str1, l1 + l2 + l3 + 1);
- if (str)
- str1 = str;
- else
- str = pool_alloctmpspace(pool, l1 + l2 + l3 + 1);
- s = str;
- if (l1)
- {
- if (s != str1)
- strcpy(s, str1);
- s += l1;
- }
- if (l2)
- {
- strcpy(s, str2);
- s += l2;
- }
- if (l3)
- {
- strcpy(s, str3);
- s += l3;
- }
- *s = 0;
- return str;
-}
-
-const char *
-pool_bin2hex(Pool *pool, const unsigned char *buf, int len)
-{
- char *s;
- if (!len)
- return "";
- s = pool_alloctmpspace(pool, 2 * len + 1);
- solv_bin2hex(buf, len, s);
- return s;
-}
-
-/*******************************************************************/
-
-struct mptree {
- Id sibling;
- Id child;
- const char *comp;
- int compl;
- Id mountpoint;
-};
-
-struct ducbdata {
- DUChanges *mps;
- struct mptree *mptree;
- int addsub;
- int hasdu;
-
- Id *dirmap;
- int nmap;
- Repodata *olddata;
-};
-
-
-static int
-solver_fill_DU_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
-{
- struct ducbdata *cbd = cbdata;
- Id mp;
-
- if (data != cbd->olddata)
- {
- Id dn, mp, comp, *dirmap, *dirs;
- int i, compl;
- const char *compstr;
- struct mptree *mptree;
-
- /* create map from dir to mptree */
- cbd->dirmap = solv_free(cbd->dirmap);
- cbd->nmap = 0;
- dirmap = solv_calloc(data->dirpool.ndirs, sizeof(Id));
- mptree = cbd->mptree;
- mp = 0;
- for (dn = 2, dirs = data->dirpool.dirs + dn; dn < data->dirpool.ndirs; dn++)
- {
- comp = *dirs++;
- if (comp <= 0)
- {
- mp = dirmap[-comp];
- continue;
- }
- if (mp < 0)
- {
- /* unconnected */
- dirmap[dn] = mp;
- continue;
- }
- if (!mptree[mp].child)
- {
- dirmap[dn] = -mp;
- continue;
- }
- if (data->localpool)
- compstr = stringpool_id2str(&data->spool, comp);
- else
- compstr = pool_id2str(data->repo->pool, comp);
- compl = strlen(compstr);
- for (i = mptree[mp].child; i; i = mptree[i].sibling)
- if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl))
- break;
- dirmap[dn] = i ? i : -mp;
- }
- /* change dirmap to point to mountpoint instead of mptree */
- for (dn = 0; dn < data->dirpool.ndirs; dn++)
- {
- mp = dirmap[dn];
- dirmap[dn] = mptree[mp > 0 ? mp : -mp].mountpoint;
- }
- cbd->dirmap = dirmap;
- cbd->nmap = data->dirpool.ndirs;
- cbd->olddata = data;
- }
- cbd->hasdu = 1;
- if (value->id < 0 || value->id >= cbd->nmap)
- return 0;
- mp = cbd->dirmap[value->id];
- if (mp < 0)
- return 0;
- if (cbd->addsub > 0)
- {
- cbd->mps[mp].kbytes += value->num;
- cbd->mps[mp].files += value->num2;
- }
- else if (!(cbd->mps[mp].flags & DUCHANGES_ONLYADD))
- {
- cbd->mps[mp].kbytes -= value->num;
- cbd->mps[mp].files -= value->num2;
- }
- return 0;
-}
-
-static void
-propagate_mountpoints(struct mptree *mptree, int pos, Id mountpoint)
-{
- int i;
- if (mptree[pos].mountpoint == -1)
- mptree[pos].mountpoint = mountpoint;
- else
- mountpoint = mptree[pos].mountpoint;
- for (i = mptree[pos].child; i; i = mptree[i].sibling)
- propagate_mountpoints(mptree, i, mountpoint);
-}
-
-#define MPTREE_BLOCK 15
-
-static struct mptree *
-create_mptree(DUChanges *mps, int nmps)
-{
- int i, nmptree;
- struct mptree *mptree;
- int pos, compl;
- int mp;
- const char *p, *path, *compstr;
-
- mptree = solv_extend_resize(0, 1, sizeof(struct mptree), MPTREE_BLOCK);
-
- /* our root node */
- mptree[0].sibling = 0;
- mptree[0].child = 0;
- mptree[0].comp = 0;
- mptree[0].compl = 0;
- mptree[0].mountpoint = -1;
- nmptree = 1;
-
- /* create component tree */
- for (mp = 0; mp < nmps; mp++)
- {
- mps[mp].kbytes = 0;
- mps[mp].files = 0;
- pos = 0;
- path = mps[mp].path;
- while(*path == '/')
- path++;
- while (*path)
- {
- if ((p = strchr(path, '/')) == 0)
- {
- compstr = path;
- compl = strlen(compstr);
- path += compl;
- }
- else
- {
- compstr = path;
- compl = p - path;
- path = p + 1;
- while(*path == '/')
- path++;
- }
- for (i = mptree[pos].child; i; i = mptree[i].sibling)
- if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl))
- break;
- if (!i)
- {
- /* create new node */
- mptree = solv_extend(mptree, nmptree, 1, sizeof(struct mptree), MPTREE_BLOCK);
- i = nmptree++;
- mptree[i].sibling = mptree[pos].child;
- mptree[i].child = 0;
- mptree[i].comp = compstr;
- mptree[i].compl = compl;
- mptree[i].mountpoint = -1;
- mptree[pos].child = i;
- }
- pos = i;
- }
- mptree[pos].mountpoint = mp;
- }
-
- propagate_mountpoints(mptree, 0, mptree[0].mountpoint);
-
-#if 0
- for (i = 0; i < nmptree; i++)
- {
- printf("#%d sibling: %d\n", i, mptree[i].sibling);
- printf("#%d child: %d\n", i, mptree[i].child);
- printf("#%d comp: %s\n", i, mptree[i].comp);
- printf("#%d compl: %d\n", i, mptree[i].compl);
- printf("#%d mountpont: %d\n", i, mptree[i].mountpoint);
- }
-#endif
-
- return mptree;
-}
-
-void
-pool_calc_duchanges(Pool *pool, Map *installedmap, DUChanges *mps, int nmps)
-{
- struct mptree *mptree;
- struct ducbdata cbd;
- Solvable *s;
- int i, sp;
- Map ignoredu;
- Repo *oldinstalled = pool->installed;
- int haveonlyadd = 0;
-
- map_init(&ignoredu, 0);
- mptree = create_mptree(mps, nmps);
-
- for (i = 0; i < nmps; i++)
- if ((mps[i].flags & DUCHANGES_ONLYADD) != 0)
- haveonlyadd = 1;
- cbd.mps = mps;
- cbd.dirmap = 0;
- cbd.nmap = 0;
- cbd.olddata = 0;
- cbd.mptree = mptree;
- cbd.addsub = 1;
- for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++)
- {
- if (!s->repo || (oldinstalled && s->repo == oldinstalled))
- continue;
- if (!MAPTST(installedmap, sp))
- continue;
- cbd.hasdu = 0;
- repo_search(s->repo, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
- if (!cbd.hasdu && oldinstalled)
- {
- Id op, opp;
- int didonlyadd = 0;
- /* no du data available, ignore data of all installed solvables we obsolete */
- if (!ignoredu.size)
- map_grow(&ignoredu, oldinstalled->end - oldinstalled->start);
- FOR_PROVIDES(op, opp, s->name)
- {
- Solvable *s2 = pool->solvables + op;
- if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
- continue;
- if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
- continue;
- if (op >= oldinstalled->start && op < oldinstalled->end)
- {
- MAPSET(&ignoredu, op - oldinstalled->start);
- if (haveonlyadd && pool->solvables[op].repo == oldinstalled && !didonlyadd)
- {
- repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
- cbd.addsub = -1;
- repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
- cbd.addsub = 1;
- didonlyadd = 1;
- }
- }
- }
- if (s->obsoletes)
- {
- Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- FOR_PROVIDES(op, opp, obs)
- {
- Solvable *s2 = pool->solvables + op;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, s2, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
- continue;
- if (op >= oldinstalled->start && op < oldinstalled->end)
- {
- MAPSET(&ignoredu, op - oldinstalled->start);
- if (haveonlyadd && pool->solvables[op].repo == oldinstalled && !didonlyadd)
- {
- repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
- cbd.addsub = -1;
- repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
- cbd.addsub = 1;
- didonlyadd = 1;
- }
- }
- }
- }
- }
- }
- cbd.addsub = -1;
- if (oldinstalled)
- {
- /* assumes we allways have du data for installed solvables */
- FOR_REPO_SOLVABLES(oldinstalled, sp, s)
- {
- if (MAPTST(installedmap, sp))
- continue;
- if (ignoredu.map && MAPTST(&ignoredu, sp - oldinstalled->start))
- continue;
- repo_search(oldinstalled, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
- }
- }
- map_free(&ignoredu);
- solv_free(cbd.dirmap);
- solv_free(mptree);
-}
-
-int
-pool_calc_installsizechange(Pool *pool, Map *installedmap)
-{
- Id sp;
- Solvable *s;
- int change = 0;
- Repo *oldinstalled = pool->installed;
-
- for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++)
- {
- if (!s->repo || (oldinstalled && s->repo == oldinstalled))
- continue;
- if (!MAPTST(installedmap, sp))
- continue;
- change += solvable_lookup_sizek(s, SOLVABLE_INSTALLSIZE, 0);
- }
- if (oldinstalled)
- {
- FOR_REPO_SOLVABLES(oldinstalled, sp, s)
- {
- if (MAPTST(installedmap, sp))
- continue;
- change -= solvable_lookup_sizek(s, SOLVABLE_INSTALLSIZE, 0);
- }
- }
- return change;
-}
-
-/* map:
- * 1: installed
- * 2: conflicts with installed
- * 8: interesting (only true if installed)
- * 16: undecided
- */
-
-static inline Id dep2name(Pool *pool, Id dep)
-{
- while (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- dep = rd->name;
- }
- return dep;
-}
-
-static int providedbyinstalled_multiversion(Pool *pool, unsigned char *map, Id n, Id con)
-{
- Id p, pp;
- Solvable *sn = pool->solvables + n;
-
- FOR_PROVIDES(p, pp, sn->name)
- {
- Solvable *s = pool->solvables + p;
- if (s->name != sn->name || s->arch != sn->arch)
- continue;
- if ((map[p] & 9) != 9)
- continue;
- if (pool_match_nevr(pool, pool->solvables + p, con))
- continue;
- return 1; /* found installed package that doesn't conflict */
- }
- return 0;
-}
-
-static inline int providedbyinstalled(Pool *pool, unsigned char *map, Id dep, int ispatch, Map *multiversionmap)
-{
- Id p, pp;
- int r = 0;
- FOR_PROVIDES(p, pp, dep)
- {
- if (p == SYSTEMSOLVABLE)
- return 1; /* always boring, as never constraining */
- if (ispatch && !pool_match_nevr(pool, pool->solvables + p, dep))
- continue;
- if (ispatch && multiversionmap && multiversionmap->size && MAPTST(multiversionmap, p) && ISRELDEP(dep))
- if (providedbyinstalled_multiversion(pool, map, p, dep))
- continue;
- if ((map[p] & 9) == 9)
- return 9;
- r |= map[p] & 17;
- }
- return r;
-}
-
-/*
- * pool_trivial_installable - calculate if a set of solvables is
- * trivial installable without any other installs/deinstalls of
- * packages not belonging to the set.
- *
- * the state is returned in the result queue:
- * 1: solvable is installable without any other package changes
- * 0: solvable is not installable
- * -1: solvable is installable, but doesn't constrain any installed packages
- */
-
-void
-pool_trivial_installable_multiversionmap(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res, Map *multiversionmap)
-{
- int i, r, m, did;
- Id p, *dp, con, *conp, req, *reqp;
- unsigned char *map;
- Solvable *s;
-
- map = solv_calloc(pool->nsolvables, 1);
- for (p = 1; p < pool->nsolvables; p++)
- {
- if (!MAPTST(installedmap, p))
- continue;
- map[p] |= 9;
- s = pool->solvables + p;
- if (!s->conflicts)
- continue;
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- dp = pool_whatprovides_ptr(pool, con);
- for (; *dp; dp++)
- map[p] |= 2; /* XXX: self conflict ? */
- }
- }
- for (i = 0; i < pkgs->count; i++)
- map[pkgs->elements[i]] = 16;
-
- for (i = 0, did = 0; did < pkgs->count; i++, did++)
- {
- if (i == pkgs->count)
- i = 0;
- p = pkgs->elements[i];
- if ((map[p] & 16) == 0)
- continue;
- if ((map[p] & 2) != 0)
- {
- map[p] = 2;
- continue;
- }
- s = pool->solvables + p;
- m = 1;
- if (s->requires)
- {
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- continue;
- r = providedbyinstalled(pool, map, req, 0, 0);
- if (!r)
- {
- /* decided and miss */
- map[p] = 2;
- did = 0;
- break;
- }
- if (r == 16)
- break; /* undecided */
- m |= r; /* 1 | 9 | 17 */
- }
- if (req)
- continue;
- if ((m & 9) == 9)
- m = 9;
- }
- if (s->conflicts)
- {
- int ispatch = 0; /* see solver.c patch handling */
-
- if (!strncmp("patch:", pool_id2str(pool, s->name), 6))
- ispatch = 1;
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- if ((providedbyinstalled(pool, map, con, ispatch, multiversionmap) & 1) != 0)
- {
- map[p] = 2;
- did = 0;
- break;
- }
- if ((m == 1 || m == 17) && ISRELDEP(con))
- {
- con = dep2name(pool, con);
- if ((providedbyinstalled(pool, map, con, ispatch, multiversionmap) & 1) != 0)
- m = 9;
- }
- }
- if (con)
- continue; /* found a conflict */
- }
-#if 0
- if (s->repo && s->repo != oldinstalled)
- {
- Id p2, obs, *obsp, *pp;
- Solvable *s2;
- if (s->obsoletes)
- {
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- if ((providedbyinstalled(pool, map, obs, 0, 0) & 1) != 0)
- {
- map[p] = 2;
- break;
- }
- }
- if (obs)
- continue;
- }
- FOR_PROVIDES(p2, pp, s->name)
- {
- s2 = pool->solvables + p2;
- if (s2->name == s->name && (map[p2] & 1) != 0)
- {
- map[p] = 2;
- break;
- }
- }
- if (p2)
- continue;
- }
-#endif
- if (m != map[p])
- {
- map[p] = m;
- did = 0;
- }
- }
- queue_free(res);
- queue_init_clone(res, pkgs);
- for (i = 0; i < pkgs->count; i++)
- {
- m = map[pkgs->elements[i]];
- if ((m & 9) == 9)
- r = 1;
- else if (m & 1)
- r = -1;
- else
- r = 0;
- res->elements[i] = r;
- }
- free(map);
-}
-
-void
-pool_trivial_installable(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res)
-{
- pool_trivial_installable_multiversionmap(pool, installedmap, pkgs, res, 0);
-}
-
-const char *
-pool_lookup_str(Pool *pool, Id entry, Id keyname)
-{
- if (entry == SOLVID_POS && pool->pos.repo)
- return repo_lookup_str(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname);
- if (entry <= 0)
- return 0;
- return solvable_lookup_str(pool->solvables + entry, keyname);
-}
-
-Id
-pool_lookup_id(Pool *pool, Id entry, Id keyname)
-{
- if (entry == SOLVID_POS && pool->pos.repo)
- return repo_lookup_id(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname);
- if (entry <= 0)
- return 0;
- return solvable_lookup_id(pool->solvables + entry, keyname);
-}
-
-unsigned long long
-pool_lookup_num(Pool *pool, Id entry, Id keyname, unsigned long long notfound)
-{
- if (entry == SOLVID_POS && pool->pos.repo)
- return repo_lookup_num(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname, notfound);
- if (entry <= 0)
- return notfound;
- return solvable_lookup_num(pool->solvables + entry, keyname, notfound);
-}
-
-int
-pool_lookup_void(Pool *pool, Id entry, Id keyname)
-{
- if (entry == SOLVID_POS && pool->pos.repo)
- return repo_lookup_void(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname);
- if (entry <= 0)
- return 0;
- return solvable_lookup_void(pool->solvables + entry, keyname);
-}
-
-const unsigned char *
-pool_lookup_bin_checksum(Pool *pool, Id entry, Id keyname, Id *typep)
-{
- if (entry == SOLVID_POS && pool->pos.repo)
- return repo_lookup_bin_checksum(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname, typep);
- if (entry <= 0)
- return 0;
- return solvable_lookup_bin_checksum(pool->solvables + entry, keyname, typep);
-}
-
-const char *
-pool_lookup_checksum(Pool *pool, Id entry, Id keyname, Id *typep)
-{
- if (entry == SOLVID_POS && pool->pos.repo)
- return repo_lookup_checksum(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname, typep);
- if (entry <= 0)
- return 0;
- return solvable_lookup_checksum(pool->solvables + entry, keyname, typep);
-}
-
-int
-pool_lookup_idarray(Pool *pool, Id entry, Id keyname, Queue *q)
-{
- if (entry == SOLVID_POS && pool->pos.repo)
- return repo_lookup_idarray(pool->pos.repo, pool->pos.repodataid ? entry : pool->pos.solvid, keyname, q);
- if (entry <= 0)
- return 0;
- return solvable_lookup_idarray(pool->solvables + entry, keyname, q);
-}
-
-const char *
-pool_lookup_deltalocation(Pool *pool, Id entry, unsigned int *medianrp)
-{
- const char *loc;
- if (medianrp)
- *medianrp = 0;
- if (entry != SOLVID_POS)
- return 0;
- loc = pool_lookup_str(pool, entry, DELTA_LOCATION_DIR);
- loc = pool_tmpjoin(pool, loc, loc ? "/" : 0, pool_lookup_str(pool, entry, DELTA_LOCATION_NAME));
- loc = pool_tmpappend(pool, loc, "-", pool_lookup_str(pool, entry, DELTA_LOCATION_EVR));
- loc = pool_tmpappend(pool, loc, ".", pool_lookup_str(pool, entry, DELTA_LOCATION_SUFFIX));
- return loc;
-}
-
-static void
-add_new_provider(Pool *pool, Id id, Id p)
-{
- Queue q;
- Id *pp;
-
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- id = rd->name;
- }
-
- queue_init(&q);
- for (pp = pool->whatprovidesdata + pool->whatprovides[id]; *pp; pp++)
- {
- if (*pp == p)
- {
- queue_free(&q);
- return;
- }
- if (*pp > p)
- {
- queue_push(&q, p);
- p = 0;
- }
- queue_push(&q, *pp);
- }
- if (p)
- queue_push(&q, p);
- pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
- if (id < pool->whatprovidesauxoff)
- pool->whatprovidesaux[id] = 0; /* sorry */
- queue_free(&q);
-}
-
-void
-pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts)
-{
- int hadhashes = pool->relhashtbl ? 1 : 0;
- Solvable *s;
- Id fn, p, q, md5;
- Id id;
- int i;
-
- if (!conflicts->count)
- return;
- for (i = 0; i < conflicts->count; i += 6)
- {
- fn = conflicts->elements[i];
- p = conflicts->elements[i + 1];
- md5 = conflicts->elements[i + 2];
- q = conflicts->elements[i + 4];
- id = pool_rel2id(pool, fn, md5, REL_FILECONFLICT, 1);
- s = pool->solvables + p;
- if (!s->repo)
- continue;
- s->provides = repo_addid_dep(s->repo, s->provides, id, SOLVABLE_FILEMARKER);
- if (pool->whatprovides)
- add_new_provider(pool, fn, p);
- if (pool->whatprovides_rel)
- pool->whatprovides_rel[GETRELID(id)] = 0; /* clear cache */
- s = pool->solvables + q;
- if (!s->repo)
- continue;
- s->conflicts = repo_addid_dep(s->repo, s->conflicts, id, 0);
- }
- if (!hadhashes)
- pool_freeidhashes(pool);
-}
-
-char *
-pool_prepend_rootdir(Pool *pool, const char *path)
-{
- if (!path)
- return 0;
- if (!pool->rootdir)
- return solv_strdup(path);
- return solv_dupjoin(pool->rootdir, "/", *path == '/' ? path + 1 : path);
-}
-
-const char *
-pool_prepend_rootdir_tmp(Pool *pool, const char *path)
-{
- if (!path)
- return 0;
- if (!pool->rootdir)
- return path;
- return pool_tmpjoin(pool, pool->rootdir, "/", *path == '/' ? path + 1 : path);
-}
-
-void
-pool_set_rootdir(Pool *pool, const char *rootdir)
-{
- solv_free(pool->rootdir);
- pool->rootdir = solv_strdup(rootdir);
-}
-
-const char *
-pool_get_rootdir(Pool *pool)
-{
- return pool->rootdir;
-}
-
-/* only used in libzypp */
-void
-pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(Pool *, Solvable *, Solvable *))
-{
- pool->custom_vendorcheck = vendorcheck;
-}
-
-/* EOF */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * pool.h
- *
- */
-
-#ifndef LIBSOLV_POOL_H
-#define LIBSOLV_POOL_H
-
-#include <stdio.h>
-
-#include "solvversion.h"
-#include "pooltypes.h"
-#include "poolid.h"
-#include "solvable.h"
-#include "bitmap.h"
-#include "queue.h"
-#include "strpool.h"
-
-/* well known ids */
-#include "knownid.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* well known solvable */
-#define SYSTEMSOLVABLE 1
-
-
-/* how many strings to maintain (round robin) */
-#define POOL_TMPSPACEBUF 16
-
-/*----------------------------------------------- */
-
-struct _Repo;
-struct _Repodata;
-struct _Repokey;
-struct _KeyValue;
-
-typedef struct _Datapos {
- struct _Repo *repo;
- Id solvid;
- Id repodataid;
- Id schema;
- Id dp;
-} Datapos;
-
-struct _Pool_tmpspace {
- char *buf[POOL_TMPSPACEBUF];
- int len[POOL_TMPSPACEBUF];
- int n;
-};
-
-struct _Pool {
- void *appdata; /* application private pointer */
-
- struct _Stringpool ss;
-
- Reldep *rels; /* table of rels: Id -> Reldep */
- int nrels; /* number of unique rels */
-
- struct _Repo **repos;
- int nrepos; /* repos allocated */
- int urepos; /* repos in use */
-
- struct _Repo *installed; /* packages considered installed */
-
- Solvable *solvables;
- int nsolvables; /* solvables allocated */
-
- const char **languages;
- int nlanguages;
-
- /* package manager type, deb/rpm */
- int disttype;
-
- Id *id2arch; /* map arch ids to scores */
- unsigned char *id2color; /* map arch ids to colors */
- Id lastarch; /* last valid entry in id2arch/id2color */
-
- Queue vendormap; /* map vendor to vendorclasses mask */
- const char **vendorclasses; /* vendor equivalence classes */
-
- /* providers data, as two-step indirect list
- * whatprovides[Id] -> Offset into whatprovidesdata for name
- * whatprovidesdata[Offset] -> 0-terminated list of solvables providing Id
- */
- Offset *whatprovides; /* Offset to providers of a specific name, Id -> Offset */
- Offset *whatprovides_rel; /* Offset to providers of a specific relation, Id -> Offset */
-
- Id *whatprovidesdata; /* Ids of solvable providing Id */
- Offset whatprovidesdataoff; /* next free slot within whatprovidesdata */
- int whatprovidesdataleft; /* number of 'free slots' within whatprovidesdata */
-
- /* If nonzero, then consider only the solvables with Ids set in this
- bitmap for solving. If zero, consider all solvables. */
- Map *considered;
-
- /* callback for REL_NAMESPACE dependencies handled by the application */
- Id (*nscallback)(struct _Pool *, void *data, Id name, Id evr);
- void *nscallbackdata;
-
- /* debug mask and callback */
- int debugmask;
- void (*debugcallback)(struct _Pool *, void *data, int type, const char *str);
- void *debugcallbackdata;
-
- /* load callback */
- int (*loadcallback)(struct _Pool *, struct _Repodata *, void *);
- void *loadcallbackdata;
-
- /* search position */
- Datapos pos;
-
- Queue pooljobs; /* fixed jobs, like USERINSTALLED/MULTIVERSION */
-
-#ifdef LIBSOLV_INTERNAL
- /* flags to tell the library how the installed package manager works */
- int promoteepoch; /* true: missing epoch is replaced by epoch of dependency */
- int havedistepoch; /* true: thr release part in the evr may contain a distepoch suffix */
- int obsoleteusesprovides; /* true: obsoletes are matched against provides, not names */
- int implicitobsoleteusesprovides; /* true: implicit obsoletes due to same name are matched against provides, not names */
- int obsoleteusescolors; /* true: obsoletes check arch color */
- int implicitobsoleteusescolors; /* true: implicit obsoletes check arch color */
- int noinstalledobsoletes; /* true: ignore obsoletes of installed packages */
- int forbidselfconflicts; /* true: packages which conflict with itself are not installable */
- int noobsoletesmultiversion; /* true: obsoletes are ignored for multiversion installs */
-
- Id noarchid; /* ARCH_NOARCH, ARCH_ALL, ARCH_ANY, ... */
-
- /* hash for rel unification */
- Hashtable relhashtbl; /* hashtable: (name,evr,op)Hash -> Id */
- Hashval relhashmask;
-
- Id *languagecache;
- int languagecacheother;
-
- /* our tmp space string space */
- struct _Pool_tmpspace tmpspace;
-
- char *errstr; /* last error string */
- int errstra; /* allocated space for errstr */
-
- char *rootdir;
-
- int (*custom_vendorcheck)(struct _Pool *, Solvable *, Solvable *);
-
- int addfileprovidesfiltered; /* 1: only use filtered file list for addfileprovides */
- int addedfileprovides; /* true: application called addfileprovides */
- Queue lazywhatprovidesq; /* queue to store old whatprovides offsets */
- int nowhatprovidesaux; /* don't allocate and use the whatprovides aux helper */
- Offset *whatprovidesaux;
- Offset whatprovidesauxoff;
- Id *whatprovidesauxdata;
- Offset whatprovidesauxdataoff;
-
-#endif
-};
-
-#define DISTTYPE_RPM 0
-#define DISTTYPE_DEB 1
-#define DISTTYPE_ARCH 2
-#define DISTTYPE_HAIKU 3
-
-#define SOLV_FATAL (1<<0)
-#define SOLV_ERROR (1<<1)
-#define SOLV_WARN (1<<2)
-#define SOLV_DEBUG_STATS (1<<3)
-#define SOLV_DEBUG_RULE_CREATION (1<<4)
-#define SOLV_DEBUG_PROPAGATE (1<<5)
-#define SOLV_DEBUG_ANALYZE (1<<6)
-#define SOLV_DEBUG_UNSOLVABLE (1<<7)
-#define SOLV_DEBUG_SOLUTIONS (1<<8)
-#define SOLV_DEBUG_POLICY (1<<9)
-#define SOLV_DEBUG_RESULT (1<<10)
-#define SOLV_DEBUG_JOB (1<<11)
-#define SOLV_DEBUG_SOLVER (1<<12)
-#define SOLV_DEBUG_TRANSACTION (1<<13)
-
-#define SOLV_DEBUG_TO_STDERR (1<<30)
-
-#define POOL_FLAG_PROMOTEEPOCH 1
-#define POOL_FLAG_FORBIDSELFCONFLICTS 2
-#define POOL_FLAG_OBSOLETEUSESPROVIDES 3
-#define POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES 4
-#define POOL_FLAG_OBSOLETEUSESCOLORS 5
-#define POOL_FLAG_NOINSTALLEDOBSOLETES 6
-#define POOL_FLAG_HAVEDISTEPOCH 7
-#define POOL_FLAG_NOOBSOLETESMULTIVERSION 8
-#define POOL_FLAG_ADDFILEPROVIDESFILTERED 9
-#define POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS 10
-#define POOL_FLAG_NOWHATPROVIDESAUX 11
-
-/* ----------------------------------------------- */
-
-
-/* mark dependencies with relation by setting bit31 */
-
-#define MAKERELDEP(id) ((id) | 0x80000000)
-#define ISRELDEP(id) (((id) & 0x80000000) != 0)
-#define GETRELID(id) ((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
-
-#define REL_AND 16
-#define REL_OR 17
-#define REL_WITH 18
-#define REL_NAMESPACE 19
-#define REL_ARCH 20
-#define REL_FILECONFLICT 21
-#define REL_COND 22
-#define REL_COMPAT 23
-#define REL_KIND 24 /* for filters only */
-#define REL_MULTIARCH 25 /* debian multiarch annotation */
-#define REL_ELSE 26 /* only as evr part of REL_COND */
-
-#if !defined(__GNUC__) && !defined(__attribute__)
-# define __attribute__(x)
-#endif
-
-extern Pool *pool_create(void);
-extern void pool_free(Pool *pool);
-extern void pool_freeallrepos(Pool *pool, int reuseids);
-
-extern void pool_setdebuglevel(Pool *pool, int level);
-extern int pool_setdisttype(Pool *pool, int disttype);
-extern int pool_set_flag(Pool *pool, int flag, int value);
-extern int pool_get_flag(Pool *pool, int flag);
-
-extern void pool_debug(Pool *pool, int type, const char *format, ...) __attribute__((format(printf, 3, 4)));
-extern void pool_setdebugcallback(Pool *pool, void (*debugcallback)(struct _Pool *, void *data, int type, const char *str), void *debugcallbackdata);
-extern void pool_setdebugmask(Pool *pool, int mask);
-extern void pool_setloadcallback(Pool *pool, int (*cb)(struct _Pool *, struct _Repodata *, void *), void *loadcbdata);
-extern void pool_setnamespacecallback(Pool *pool, Id (*cb)(struct _Pool *, void *, Id, Id), void *nscbdata);
-extern void pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr);
-
-extern void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(struct _Pool *, Solvable *, Solvable *));
-
-
-extern char *pool_alloctmpspace(Pool *pool, int len);
-extern void pool_freetmpspace(Pool *pool, const char *space);
-extern char *pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const char *str3);
-extern char *pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3);
-extern const char *pool_bin2hex(Pool *pool, const unsigned char *buf, int len);
-
-extern void pool_set_installed(Pool *pool, struct _Repo *repo);
-
-extern int pool_error(Pool *pool, int ret, const char *format, ...) __attribute__((format(printf, 3, 4)));
-extern char *pool_errstr(Pool *pool);
-
-extern void pool_set_rootdir(Pool *pool, const char *rootdir);
-extern const char *pool_get_rootdir(Pool *pool);
-extern char *pool_prepend_rootdir(Pool *pool, const char *dir);
-extern const char *pool_prepend_rootdir_tmp(Pool *pool, const char *dir);
-
-/**
- * Solvable management
- */
-extern Id pool_add_solvable(Pool *pool);
-extern Id pool_add_solvable_block(Pool *pool, int count);
-
-extern void pool_free_solvable_block(Pool *pool, Id start, int count, int reuseids);
-static inline Solvable *pool_id2solvable(const Pool *pool, Id p)
-{
- return pool->solvables + p;
-}
-
-extern const char *pool_solvable2str(Pool *pool, Solvable *s);
-static inline const char *pool_solvid2str(Pool *pool, Id p)
-{
- return pool_solvable2str(pool, pool->solvables + p);
-}
-
-void pool_set_languages(Pool *pool, const char **languages, int nlanguages);
-Id pool_id2langid(Pool *pool, Id id, const char *lang, int create);
-
-int solvable_trivial_installable_map(Solvable *s, Map *installedmap, Map *conflictsmap, Map *multiversionmap);
-int solvable_trivial_installable_repo(Solvable *s, struct _Repo *installed, Map *multiversionmap);
-int solvable_trivial_installable_queue(Solvable *s, Queue *installed, Map *multiversionmap);
-int solvable_is_irrelevant_patch(Solvable *s, Map *installedmap);
-
-void pool_create_state_maps(Pool *pool, Queue *installed, Map *installedmap, Map *conflictsmap);
-
-int pool_intersect_evrs(Pool *pool, int pflags, Id pevr, int flags, int evr);
-int pool_match_dep(Pool *pool, Id d1, Id d2);
-
-/* semi private, used in pool_match_nevr */
-int pool_match_nevr_rel(Pool *pool, Solvable *s, Id d);
-
-static inline int pool_match_nevr(Pool *pool, Solvable *s, Id d)
-{
- if (!ISRELDEP(d))
- return d == s->name;
- else
- return pool_match_nevr_rel(pool, s, d);
-}
-
-
-/**
- * Prepares a pool for solving
- */
-extern void pool_createwhatprovides(Pool *pool);
-extern void pool_addfileprovides(Pool *pool);
-extern void pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst);
-extern void pool_freewhatprovides(Pool *pool);
-extern Id pool_queuetowhatprovides(Pool *pool, Queue *q);
-extern Id pool_ids2whatprovides(Pool *pool, Id *ids, int count);
-extern Id pool_searchlazywhatprovidesq(Pool *pool, Id d);
-
-extern Id pool_addrelproviders(Pool *pool, Id d);
-
-static inline Id pool_whatprovides(Pool *pool, Id d)
-{
- if (!ISRELDEP(d))
- {
- if (pool->whatprovides[d])
- return pool->whatprovides[d];
- }
- else
- {
- Id v = GETRELID(d);
- if (pool->whatprovides_rel[v])
- return pool->whatprovides_rel[v];
- }
- return pool_addrelproviders(pool, d);
-}
-
-static inline Id *pool_whatprovides_ptr(Pool *pool, Id d)
-{
- Id off = pool_whatprovides(pool, d);
- return pool->whatprovidesdata + off;
-}
-
-void pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker);
-
-/* search the pool. the following filters are available:
- * p - search just this solvable
- * key - search only this key
- * match - key must match this string
- */
-void pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, struct _Repodata *data, struct _Repokey *key, struct _KeyValue *kv), void *cbdata);
-
-void pool_clear_pos(Pool *pool);
-
-
-#define DUCHANGES_ONLYADD 1
-
-typedef struct _DUChanges {
- const char *path;
- int kbytes;
- int files;
- int flags;
-} DUChanges;
-
-void pool_calc_duchanges(Pool *pool, Map *installedmap, DUChanges *mps, int nmps);
-int pool_calc_installsizechange(Pool *pool, Map *installedmap);
-void pool_trivial_installable(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res);
-void pool_trivial_installable_multiversionmap(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res, Map *multiversionmap);
-
-const char *pool_lookup_str(Pool *pool, Id entry, Id keyname);
-Id pool_lookup_id(Pool *pool, Id entry, Id keyname);
-unsigned long long pool_lookup_num(Pool *pool, Id entry, Id keyname, unsigned long long notfound);
-int pool_lookup_void(Pool *pool, Id entry, Id keyname);
-const unsigned char *pool_lookup_bin_checksum(Pool *pool, Id entry, Id keyname, Id *typep);
-int pool_lookup_idarray(Pool *pool, Id entry, Id keyname, Queue *q);
-const char *pool_lookup_checksum(Pool *pool, Id entry, Id keyname, Id *typep);
-const char *pool_lookup_deltalocation(Pool *pool, Id entry, unsigned int *medianrp);
-
-void pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts);
-
-
-
-/* loop over all providers of d */
-#define FOR_PROVIDES(v, vp, d) \
- for (vp = pool_whatprovides(pool, d) ; (v = pool->whatprovidesdata[vp++]) != 0; )
-
-/* loop over all repositories */
-#define FOR_REPOS(repoid, r) \
- for (repoid = 1; repoid < pool->nrepos; repoid++) \
- if ((r = pool->repos[repoid]) == 0) \
- continue; \
- else
-
-#define FOR_POOL_SOLVABLES(p) \
- for (p = 2; p < pool->nsolvables; p++) \
- if (pool->solvables[p].repo == 0) \
- continue; \
- else
-
-#ifdef ENABLE_COMPS
-#define ISCONDDEP(id) (ISRELDEP(id) && (GETRELDEP(pool, id))->flags == REL_COND)
-#define MODIFYCONDDEP(id, tst) do { Reldep *condrd = GETRELDEP(pool, id); Id condp, condpp; FOR_PROVIDES(condrd->evr, condp, condpp) if (tst) break; id = condp ? condrd->name : 0;} while(0)
-#endif
-
-#define POOL_DEBUG(type, ...) do {if ((pool->debugmask & (type)) != 0) pool_debug(pool, (type), __VA_ARGS__);} while (0)
-#define IF_POOLDEBUG(type) if ((pool->debugmask & (type)) != 0)
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* LIBSOLV_POOL_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * poolarch.c
- *
- * create architecture policies
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "pool.h"
-#include "poolid.h"
-#include "poolarch.h"
-#include "util.h"
-
-static const char *archpolicies[] = {
-#if defined(FEDORA) || defined(MAGEIA)
- "x86_64", "x86_64:athlon:i686:i586:i486:i386",
-#else
- "x86_64", "x86_64:i686:i586:i486:i386",
-#endif
- "i686", "i686:i586:i486:i386",
- "i586", "i586:i486:i386",
- "i486", "i486:i386",
- "i386", "i386",
- "s390x", "s390x:s390",
- "s390", "s390",
- "ppc64le", "ppc64le",
- "ppc64", "ppc64:ppc",
- "ppc", "ppc",
- "ppc64p7", "ppc64p7:ppc64:ppc",
- "ia64", "ia64:i686:i586:i486:i386",
- "aarch64", "aarch64",
- "armv6hl", "armv6hl",
- "armv7hnl", "armv7hnl:armv7hl:armv6hl",
- "armv7hl", "armv7hl:armv6hl",
- "armv7l", "armv7l:armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
- "armv6l", "armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
- "armv5tejl", "armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
- "armv5tel", "armv5tel:armv5l:armv4tl:armv4l:armv3l",
- "armv5tl", "armv5l:armv4tl:armv4l:armv3l",
- "armv5l", "armv5l:armv4tl:armv4l:armv3l",
- "armv4tl", "armv4tl:armv4l:armv3l",
- "armv4l", "armv4l:armv3l",
- "armv3l", "armv3l",
- "sh3", "sh3",
- "sh4", "sh4",
- "sh4a", "sh4a:sh4",
- "sparc64v", "sparc64v:sparc64:sparcv9v:sparcv9:sparcv8:sparc",
- "sparc64", "sparc64:sparcv9:sparcv8:sparc",
- "sparcv9v", "sparcv9v:sparcv9:sparcv8:sparc",
- "sparcv9", "sparcv9:sparcv8:sparc",
- "sparcv8", "sparcv8:sparc",
- "sparc", "sparc",
- "mips", "mips",
- "mipsel", "mipsel",
- "mips64", "mips64",
- "mips64el", "mips64el",
- "m68k", "m68k",
-#if defined(FEDORA) || defined(MAGEIA)
- "ia32e", "ia32e:x86_64:athlon:i686:i586:i486:i386",
- "athlon", "athlon:i686:i586:i486:i386",
- "amd64", "amd64:x86_64:athlon:i686:i586:i486:i386",
- "geode", "geode:i586:i486:i386",
- "ppc64iseries", "ppc64iseries:ppc64:ppc",
- "ppc64pseries", "ppc64pseries:ppc64:ppc",
-#endif
- 0
-};
-
-void
-pool_setarch(Pool *pool, const char *arch)
-{
- if (arch)
- {
- int i;
-
- /* convert arch to known policy */
- for (i = 0; archpolicies[i]; i += 2)
- if (!strcmp(archpolicies[i], arch))
- break;
- if (archpolicies[i])
- arch = archpolicies[i + 1];
- else
- arch = "";
- }
- pool_setarchpolicy(pool, arch);
-}
-
-/*
- * we support three relations:
- *
- * a = b both architectures a and b are treated as equivalent
- * a > b a is considered a "better" architecture, the solver
- * should change from a to b, but must not change from b to a
- * a : b a is considered a "better" architecture, the solver
- * must not change the architecture from a to b or b to a
- */
-void
-pool_setarchpolicy(Pool *pool, const char *arch)
-{
- unsigned int score = 0x10001;
- size_t l;
- char d;
- Id *id2arch;
- Id id, lastarch;
-
- pool->id2arch = solv_free(pool->id2arch);
- pool->id2color = solv_free(pool->id2color);
- if (!arch)
- {
- pool->lastarch = 0;
- return;
- }
- id = pool->noarchid;
- lastarch = id + 255;
- id2arch = solv_calloc(lastarch + 1, sizeof(Id));
- id2arch[id] = 1; /* the "noarch" class */
-
- d = 0;
- while (*arch)
- {
- l = strcspn(arch, ":=>");
- if (l)
- {
- id = pool_strn2id(pool, arch, l, 1);
- if (id > lastarch)
- {
- id2arch = solv_realloc2(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;
- }
- }
- arch += l;
- if ((d = *arch++) == 0)
- break;
- }
- pool->id2arch = id2arch;
- pool->lastarch = lastarch;
-}
-
-unsigned char
-pool_arch2color_slow(Pool *pool, Id arch)
-{
- const char *s;
- unsigned char color;
-
- if (arch > pool->lastarch)
- return ARCHCOLOR_ALL;
- if (!pool->id2color)
- pool->id2color = solv_calloc(pool->lastarch + 1, 1);
- s = pool_id2str(pool, arch);
- if (arch == ARCH_NOARCH || arch == ARCH_ALL || arch == ARCH_ANY)
- color = ARCHCOLOR_ALL;
- else if (!strcmp(s, "s390x") || strstr(s, "64"))
- color = ARCHCOLOR_64;
- else
- color = ARCHCOLOR_32;
- pool->id2color[arch] = color;
- return color;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef LIBSOLV_POOLARCH_H
-#define LIBSOLV_POOLARCH_H
-
-#include "pool.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern void pool_setarch(Pool *, const char *);
-extern void pool_setarchpolicy(Pool *, const char *);
-extern unsigned char pool_arch2color_slow(Pool *pool, Id arch);
-
-#define ARCHCOLOR_32 1
-#define ARCHCOLOR_64 2
-#define ARCHCOLOR_ALL 255
-
-static inline unsigned char pool_arch2color(Pool *pool, Id arch)
-{
- if (arch > pool->lastarch)
- return ARCHCOLOR_ALL;
- if (pool->id2color && pool->id2color[arch])
- return pool->id2color[arch];
- return pool_arch2color_slow(pool, arch);
-}
-
-static inline int pool_colormatch(Pool *pool, Solvable *s1, Solvable *s2)
-{
- if (s1->arch == s2->arch)
- return 1;
- if ((pool_arch2color(pool, s1->arch) & pool_arch2color(pool, s2->arch)) != 0)
- return 1;
- return 0;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_POOLARCH_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * poolid.c
- *
- * Id management
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "pool.h"
-#include "poolid.h"
-#include "poolid_private.h"
-#include "util.h"
-
-
-/* intern string into pool, return id */
-
-Id
-pool_str2id(Pool *pool, const char *str, int create)
-{
- int oldnstrings = pool->ss.nstrings;
- Id id = stringpool_str2id(&pool->ss, str, create);
- if (create && pool->whatprovides && oldnstrings != pool->ss.nstrings && (id & WHATPROVIDES_BLOCK) == 0)
- {
- /* grow whatprovides array */
- pool->whatprovides = solv_realloc(pool->whatprovides, (id + (WHATPROVIDES_BLOCK + 1)) * sizeof(Offset));
- memset(pool->whatprovides + id, 0, (WHATPROVIDES_BLOCK + 1) * sizeof(Offset));
- }
- return id;
-}
-
-Id
-pool_strn2id(Pool *pool, const char *str, unsigned int len, int create)
-{
- int oldnstrings = pool->ss.nstrings;
- Id id = stringpool_strn2id(&pool->ss, str, len, create);
- if (create && pool->whatprovides && oldnstrings != pool->ss.nstrings && (id & WHATPROVIDES_BLOCK) == 0)
- {
- /* grow whatprovides array */
- pool->whatprovides = solv_realloc(pool->whatprovides, (id + (WHATPROVIDES_BLOCK + 1)) * sizeof(Offset));
- memset(pool->whatprovides + id, 0, (WHATPROVIDES_BLOCK + 1) * sizeof(Offset));
- }
- return id;
-}
-
-Id
-pool_rel2id(Pool *pool, Id name, Id evr, int flags, int create)
-{
- Hashval h, hh, hashmask;
- int i;
- Id id;
- Hashtable hashtbl;
- Reldep *ran;
-
- hashmask = pool->relhashmask;
- hashtbl = pool->relhashtbl;
- ran = pool->rels;
-
- /* extend hashtable if needed */
- if ((Hashval)pool->nrels * 2 > hashmask)
- {
- solv_free(pool->relhashtbl);
- pool->relhashmask = hashmask = mkmask(pool->nrels + REL_BLOCK);
- pool->relhashtbl = hashtbl = solv_calloc(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;
-
- id = pool->nrels++;
- /* extend rel space if needed */
- pool->rels = solv_extend(pool->rels, id, 1, sizeof(Reldep), REL_BLOCK);
- hashtbl[h] = id;
- ran = pool->rels + id;
- ran->name = name;
- ran->evr = evr;
- ran->flags = flags;
-
- /* extend whatprovides_rel if needed */
- if (pool->whatprovides_rel && (id & WHATPROVIDES_BLOCK) == 0)
- {
- pool->whatprovides_rel = solv_realloc2(pool->whatprovides_rel, id + (WHATPROVIDES_BLOCK + 1), sizeof(Offset));
- memset(pool->whatprovides_rel + id, 0, (WHATPROVIDES_BLOCK + 1) * sizeof(Offset));
- }
- return MAKERELDEP(id);
-}
-
-
-/* Id -> String
- * for rels (returns name only) and strings
- */
-const char *
-pool_id2str(const Pool *pool, Id id)
-{
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- id = rd->name;
- }
- return pool->ss.stringspace + pool->ss.strings[id];
-}
-
-static const char *rels[] = {
- " ! ",
- " > ",
- " = ",
- " >= ",
- " < ",
- " <> ",
- " <= ",
- " <=> "
-};
-
-
-/* get operator for RelId */
-const char *
-pool_id2rel(const Pool *pool, Id id)
-{
- Reldep *rd;
- if (!ISRELDEP(id))
- return "";
- rd = GETRELDEP(pool, id);
- switch (rd->flags)
- {
- /* debian special cases < and > */
- /* haiku special cases <> (maybe we should use != for the others as well */
- case 0: case REL_EQ: case REL_GT | REL_EQ:
- case REL_LT | REL_EQ: case REL_LT | REL_EQ | REL_GT:
-#if !defined(DEBIAN) && !defined(MULTI_SEMANTICS)
- case REL_LT: case REL_GT:
-#endif
-#if !defined(HAIKU) && !defined(MULTI_SEMANTICS)
- case REL_LT | REL_GT:
-#endif
- return rels[rd->flags];
-#if defined(DEBIAN) || defined(MULTI_SEMANTICS)
- case REL_GT:
- return pool->disttype == DISTTYPE_DEB ? " >> " : rels[rd->flags];
- case REL_LT:
- return pool->disttype == DISTTYPE_DEB ? " << " : rels[rd->flags];
-#endif
-#if defined(HAIKU) || defined(MULTI_SEMANTICS)
- case REL_LT | REL_GT:
- return pool->disttype == DISTTYPE_HAIKU ? " != " : rels[rd->flags];
-#endif
- case REL_AND:
- return " & ";
- case REL_OR:
- return " | ";
- case REL_WITH:
- return " + ";
- case REL_NAMESPACE:
- return " NAMESPACE "; /* actually not used in dep2str */
- case REL_ARCH:
- return ".";
- case REL_MULTIARCH:
- return ":";
- case REL_FILECONFLICT:
- return " FILECONFLICT ";
- case REL_COND:
- return " IF ";
- case REL_COMPAT:
- return " compat >= ";
- case REL_KIND:
- return " KIND ";
- case REL_ELSE:
- return " ELSE ";
- default:
- break;
- }
- return " ??? ";
-}
-
-
-/* get e:v.r for Id */
-const char *
-pool_id2evr(const Pool *pool, Id id)
-{
- Reldep *rd;
- if (!ISRELDEP(id))
- return "";
- rd = GETRELDEP(pool, id);
- if (ISRELDEP(rd->evr))
- return "(REL)";
- return pool->ss.stringspace + pool->ss.strings[rd->evr];
-}
-
-static int
-dep2strlen(const Pool *pool, Id id)
-{
- int l = 0;
-
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- /* add 2 for parens */
- l += 2 + dep2strlen(pool, rd->name) + strlen(pool_id2rel(pool, id));
- id = rd->evr;
- }
- return l + strlen(pool->ss.stringspace + pool->ss.strings[id]);
-}
-
-static void
-dep2strcpy(const Pool *pool, char *p, Id id, int oldrel)
-{
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- if (oldrel == REL_AND || oldrel == REL_OR || oldrel == REL_WITH)
- if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH)
- if (oldrel != rd->flags)
- {
- *p++ = '(';
- dep2strcpy(pool, p, rd->name, rd->flags);
- p += strlen(p);
- strcpy(p, pool_id2rel(pool, id));
- p += strlen(p);
- dep2strcpy(pool, p, rd->evr, rd->flags);
- strcat(p, ")");
- return;
- }
- if (rd->flags == REL_KIND)
- {
- dep2strcpy(pool, p, rd->evr, rd->flags);
- p += strlen(p);
- *p++ = ':';
- id = rd->name;
- oldrel = rd->flags;
- continue;
- }
- dep2strcpy(pool, p, rd->name, rd->flags);
- p += strlen(p);
- if (rd->flags == REL_NAMESPACE)
- {
- *p++ = '(';
- dep2strcpy(pool, p, rd->evr, rd->flags);
- strcat(p, ")");
- return;
- }
- if (rd->flags == REL_FILECONFLICT)
- {
- *p = 0;
- return;
- }
- strcpy(p, pool_id2rel(pool, id));
- p += strlen(p);
- id = rd->evr;
- oldrel = rd->flags;
- }
- strcpy(p, pool->ss.stringspace + pool->ss.strings[id]);
-}
-
-const char *
-pool_dep2str(Pool *pool, Id id)
-{
- char *p;
- if (!ISRELDEP(id))
- return pool->ss.stringspace + pool->ss.strings[id];
- p = pool_alloctmpspace(pool, dep2strlen(pool, id) + 1);
- dep2strcpy(pool, p, id, 0);
- return p;
-}
-
-void
-pool_shrink_strings(Pool *pool)
-{
- stringpool_shrink(&pool->ss);
-}
-
-void
-pool_shrink_rels(Pool *pool)
-{
- pool->rels = solv_extend_resize(pool->rels, pool->nrels, sizeof(Reldep), REL_BLOCK);
-}
-
-/* free all hash tables */
-void
-pool_freeidhashes(Pool *pool)
-{
- stringpool_freehash(&pool->ss);
- pool->relhashtbl = solv_free(pool->relhashtbl);
- pool->relhashmask = 0;
-}
-
-/* EOF */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * poolid.h
- *
- */
-
-#ifndef LIBSOLV_POOLID_H
-#define LIBSOLV_POOLID_H
-
-#include "pooltypes.h"
-#include "hash.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*-----------------------------------------------
- * Ids with relation
- */
-
-typedef struct _Reldep {
- Id name; /* "package" */
- Id evr; /* "0:42-3" */
- int flags; /* operation/relation, see REL_x in pool.h */
-} Reldep;
-
-extern Id pool_str2id(Pool *pool, const char *, int);
-extern Id pool_strn2id(Pool *pool, const char *, unsigned int, int);
-extern Id pool_rel2id(Pool *pool, Id, Id, int, int);
-extern const char *pool_id2str(const Pool *pool, Id);
-extern const char *pool_id2rel(const Pool *pool, Id);
-extern const char *pool_id2evr(const Pool *pool, Id);
-extern const char *pool_dep2str(Pool *pool, Id); /* might alloc tmpspace */
-
-extern void pool_shrink_strings(Pool *pool);
-extern void pool_shrink_rels(Pool *pool);
-extern void pool_freeidhashes(Pool *pool);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_POOLID_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * poolid_private.h
- *
- */
-
-#ifndef LIBSOLV_POOLID_PRIVATE_H
-#define LIBSOLV_POOLID_PRIVATE_H
-
-/* the size of all buffers is incremented in blocks
- * these are the block values (increment values) for the
- * rel hashtable
- */
-#define REL_BLOCK 1023 /* hashtable for relations */
-#define WHATPROVIDES_BLOCK 1023
-
-#endif /* LIBSOLV_POOLID_PRIVATE_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * pooltypes.h
- *
- */
-
-#ifndef LIBSOLV_POOLTYPES_H
-#define LIBSOLV_POOLTYPES_H
-
-/* format version number for .solv files */
-#define SOLV_VERSION_0 0
-#define SOLV_VERSION_1 1
-#define SOLV_VERSION_2 2
-#define SOLV_VERSION_3 3
-#define SOLV_VERSION_4 4
-#define SOLV_VERSION_5 5
-#define SOLV_VERSION_6 6
-#define SOLV_VERSION_7 7
-#define SOLV_VERSION_8 8
-
-#define SOLV_FLAG_PREFIX_POOL 4
-#define SOLV_FLAG_SIZE_BYTES 8
-
-struct _Stringpool;
-typedef struct _Stringpool Stringpool;
-
-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 /* LIBSOLV_POOLTYPES_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* we need FNM_CASEFOLD */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <fnmatch.h>
-
-#include "pool.h"
-#include "poolid.h"
-#include "poolvendor.h"
-#include "util.h"
-
-/*
- * const char *vendorsclasses[] = {
- * "!openSUSE Build Service*",
- * "SUSE*",
- * "openSUSE*",
- * "SGI*",
- * "Novell*",
- * "Silicon Graphics*",
- * "Jpackage Project*",
- * "ATI Technologies Inc.*",
- * "Nvidia*",
- * 0,
- * 0,
- * };
- */
-
-/* allows for 32 different vendor classes */
-
-Id pool_vendor2mask(Pool *pool, Id vendor)
-{
- const char *vstr;
- int i;
- Id mask, m;
- const char **v, *vs;
-
- if (vendor == 0 || !pool->vendorclasses)
- return 0;
- for (i = 0; i < pool->vendormap.count; i += 2)
- if (pool->vendormap.elements[i] == vendor)
- return pool->vendormap.elements[i + 1];
- vstr = pool_id2str(pool, vendor);
- m = 1;
- mask = 0;
- for (v = pool->vendorclasses; ; v++)
- {
- vs = *v;
- if (vs == 0) /* end of block? */
- {
- v++;
- if (*v == 0)
- break;
- if (m == (1 << 31))
- break; /* sorry, out of bits */
- m <<= 1; /* next vendor equivalence class */
- }
- if (fnmatch(*vs == '!' ? vs + 1 : vs, vstr, FNM_CASEFOLD) == 0)
- {
- if (*vs != '!')
- mask |= m;
- while (v[1]) /* forward to next block */
- v++;
- }
- }
- queue_push(&pool->vendormap, vendor);
- queue_push(&pool->vendormap, mask);
- return mask;
-}
-
-void
-pool_setvendorclasses(Pool *pool, const char **vendorclasses)
-{
- int i;
- const char **v;
-
- if (pool->vendorclasses)
- {
- for (v = pool->vendorclasses; v[0] || v[1]; v++)
- solv_free((void *)*v);
- pool->vendorclasses = solv_free((void *)pool->vendorclasses);
- }
- if (!vendorclasses || !vendorclasses[0])
- return;
- for (v = vendorclasses; v[0] || v[1]; v++)
- ;
- pool->vendorclasses = solv_calloc(v - vendorclasses + 2, sizeof(const char *));
- for (v = vendorclasses, i = 0; v[0] || v[1]; v++, i++)
- pool->vendorclasses[i] = *v ? solv_strdup(*v) : 0;
- pool->vendorclasses[i++] = 0;
- pool->vendorclasses[i] = 0;
- queue_empty(&pool->vendormap);
-}
-
-void
-pool_addvendorclass(Pool *pool, const char **vendorclass)
-{
- int i, j;
-
- if (!vendorclass || !vendorclass[0])
- return;
- for (j = 1; vendorclass[j]; j++)
- ;
- i = 0;
- if (pool->vendorclasses)
- {
- for (i = 0; pool->vendorclasses[i] || pool->vendorclasses[i + 1]; i++)
- ;
- if (i)
- i++;
- }
- pool->vendorclasses = solv_realloc2((void *)pool->vendorclasses, i + j + 2, sizeof(const char *));
- for (j = 0; vendorclass[j]; j++)
- pool->vendorclasses[i++] = solv_strdup(vendorclass[j]);
- pool->vendorclasses[i++] = 0;
- pool->vendorclasses[i] = 0;
- queue_empty(&pool->vendormap);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef LIBSOLV_POOLVENDOR_H
-#define LIBSOLV_POOLVENDOR_H
-
-#include "pool.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-Id pool_vendor2mask(Pool *pool, Id vendor);
-void pool_setvendorclasses(Pool *pool, const char **vendorclasses);
-void pool_addvendorclass(Pool *pool, const char **vendorclass);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_POOLVENDOR_H */
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * problems.c
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-
-#include "solver.h"
-#include "solver_private.h"
-#include "bitmap.h"
-#include "pool.h"
-#include "util.h"
-#include "evr.h"
-#include "solverdebug.h"
-
-
-/**********************************************************************************/
-
-/* a problem is an item on the solver's problem list. It can either be >0, in that
- * case it is a update/infarch/dup rule, or it can be <0, which makes it refer to a job
- * consisting of multiple job rules.
- */
-
-void
-solver_disableproblem(Solver *solv, Id v)
-{
- Rule *r;
- int i;
- Id *jp;
-
- if (v > 0)
- {
- if (v >= solv->infarchrules && v < solv->infarchrules_end)
- {
- Pool *pool = solv->pool;
- Id name = pool->solvables[-solv->rules[v].p].name;
- while (v > solv->infarchrules && pool->solvables[-solv->rules[v - 1].p].name == name)
- v--;
- for (; v < solv->infarchrules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
- solver_disablerule(solv, solv->rules + v);
- return;
- }
- if (v >= solv->duprules && v < solv->duprules_end)
- {
- Pool *pool = solv->pool;
- Id name = pool->solvables[-solv->rules[v].p].name;
- while (v > solv->duprules && pool->solvables[-solv->rules[v - 1].p].name == name)
- v--;
- for (; v < solv->duprules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
- solver_disablerule(solv, solv->rules + v);
- return;
- }
- solver_disablerule(solv, solv->rules + v);
-#if 0
- /* XXX: doesn't work */
- if (v >= solv->updaterules && v < solv->updaterules_end)
- {
- /* enable feature rule if we disabled the update rule */
- r = solv->rules + (v - solv->updaterules + solv->featurerules);
- if (r->p)
- solver_enablerule(solv, r);
- }
-#endif
- return;
- }
- v = -(v + 1);
- jp = solv->ruletojob.elements;
- for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
- if (*jp == v)
- solver_disablerule(solv, r);
-}
-
-/*-------------------------------------------------------------------
- * enableproblem
- */
-
-void
-solver_enableproblem(Solver *solv, Id v)
-{
- Rule *r;
- int i;
- Id *jp;
-
- if (v > 0)
- {
- if (v >= solv->infarchrules && v < solv->infarchrules_end)
- {
- Pool *pool = solv->pool;
- Id name = pool->solvables[-solv->rules[v].p].name;
- while (v > solv->infarchrules && pool->solvables[-solv->rules[v - 1].p].name == name)
- v--;
- for (; v < solv->infarchrules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
- solver_enablerule(solv, solv->rules + v);
- return;
- }
- if (v >= solv->duprules && v < solv->duprules_end)
- {
- Pool *pool = solv->pool;
- Id name = pool->solvables[-solv->rules[v].p].name;
- while (v > solv->duprules && pool->solvables[-solv->rules[v - 1].p].name == name)
- v--;
- for (; v < solv->duprules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
- solver_enablerule(solv, solv->rules + v);
- return;
- }
- if (v >= solv->featurerules && v < solv->featurerules_end)
- {
- /* do not enable feature rule if update rule is enabled */
- r = solv->rules + (v - solv->featurerules + solv->updaterules);
- if (r->d >= 0)
- return;
- }
- solver_enablerule(solv, solv->rules + v);
- if (v >= solv->updaterules && v < solv->updaterules_end)
- {
- /* disable feature rule when enabling update rule */
- r = solv->rules + (v - solv->updaterules + solv->featurerules);
- if (r->p)
- solver_disablerule(solv, r);
- }
- return;
- }
- v = -(v + 1);
- jp = solv->ruletojob.elements;
- for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
- if (*jp == v)
- solver_enablerule(solv, r);
-}
-
-
-/*-------------------------------------------------------------------
- * enable weak rules
- *
- * Reenable all disabled weak rules (marked in weakrulemap)
- *
- */
-
-static void
-enableweakrules(Solver *solv)
-{
- int i;
- Rule *r;
-
- if (!solv->weakrulemap.size)
- return;
- for (i = 1, r = solv->rules + i; i < solv->learntrules; i++, r++)
- {
- if (r->d >= 0) /* already enabled? */
- continue;
- if (!MAPTST(&solv->weakrulemap, i))
- continue;
- solver_enablerule(solv, r);
- }
- /* make sure broken orphan rules stay disabled */
- if (solv->brokenorphanrules)
- for (i = 0; i < solv->brokenorphanrules->count; i++)
- solver_disablerule(solv, solv->rules + solv->brokenorphanrules->elements[i]);
-}
-
-
-/*-------------------------------------------------------------------
- *
- * refine_suggestion
- *
- * at this point, all rules that led to conflicts are disabled.
- * we re-enable all rules of a problem set but rule "sug", then
- * continue to disable more rules until there as again a solution.
- */
-
-/* FIXME: think about conflicting assertions */
-
-static void
-refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essentialok)
-{
- Pool *pool = solv->pool;
- int i, j;
- Id v;
- Queue disabled;
- int disabledcnt;
-
- IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "refine_suggestion start\n");
- for (i = 0; problem[i]; i++)
- {
- if (problem[i] == sug)
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "=> ");
- solver_printproblem(solv, problem[i]);
- }
- }
- queue_empty(refined);
- if (!essentialok && sug < 0 && (solv->job.elements[-sug - 1] & SOLVER_ESSENTIAL) != 0)
- return;
- queue_init(&disabled);
- queue_push(refined, sug);
-
- /* re-enable all problem rules with the exception of "sug"(gestion) */
- solver_reset(solv);
-
- for (i = 0; problem[i]; i++)
- if (problem[i] != sug)
- solver_enableproblem(solv, problem[i]);
-
- if (sug < 0)
- solver_reenablepolicyrules(solv, -sug);
- else if (sug >= solv->updaterules && sug < solv->updaterules_end)
- {
- /* enable feature rule */
- Rule *r = solv->rules + solv->featurerules + (sug - solv->updaterules);
- if (r->p)
- solver_enablerule(solv, r);
- }
-
- enableweakrules(solv);
-
- for (;;)
- {
- int njob, nfeature, nupdate, pass;
- queue_empty(&solv->problems);
- solver_reset(solv);
- solver_run_sat(solv, 0, 0);
-
- if (!solv->problems.count)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "no more problems!\n");
- break; /* great, no more problems */
- }
- disabledcnt = disabled.count;
- /* start with 1 to skip over proof index */
- njob = nfeature = nupdate = 0;
- for (pass = 0; pass < 2; pass++)
- {
- for (i = 1; i < solv->problems.count - 1; i++)
- {
- /* ignore solutions in refined */
- v = solv->problems.elements[i];
- if (v == 0)
- break; /* end of problem reached */
- if (sug != v)
- {
- /* check if v is in the given problems list
- * we allow disabling all problem rules *after* sug in
- * pass 2, to prevent getting the same solution twice */
- for (j = 0; problem[j]; j++)
- if (problem[j] == v || (pass && problem[j] == sug))
- break;
- if (problem[j] == v)
- continue;
- }
- if (v >= solv->featurerules && v < solv->featurerules_end)
- nfeature++;
- else if (v > 0)
- nupdate++;
- else
- {
- if (!essentialok && (solv->job.elements[-v - 1] & SOLVER_ESSENTIAL) != 0)
- continue; /* not that one! */
- njob++;
- }
- queue_push(&disabled, v);
- }
- if (disabled.count != disabledcnt)
- break;
- }
- if (disabled.count == disabledcnt)
- {
- /* no solution found, this was an invalid suggestion! */
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "no solution found!\n");
- refined->count = 0;
- break;
- }
- if (!njob && nupdate && nfeature)
- {
- /* got only update rules, filter out feature rules */
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "throwing away feature rules\n");
- for (i = j = disabledcnt; i < disabled.count; i++)
- {
- v = disabled.elements[i];
- if (v < solv->featurerules || v >= solv->featurerules_end)
- disabled.elements[j++] = v;
- }
- disabled.count = j;
- nfeature = 0;
- }
- if (disabled.count == disabledcnt + 1)
- {
- /* just one suggestion, add it to refined list */
- v = disabled.elements[disabledcnt];
- if (!nfeature && v != sug)
- queue_push(refined, v); /* do not record feature rules */
- solver_disableproblem(solv, v);
- if (v >= solv->updaterules && v < solv->updaterules_end)
- {
- Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
- if (r->p)
- solver_enablerule(solv, r); /* enable corresponding feature rule */
- }
- if (v < 0)
- solver_reenablepolicyrules(solv, -v);
- }
- else
- {
- /* more than one solution, disable all */
- /* do not push anything on refine list, as we do not know which solution to choose */
- /* thus, the user will get another problem if he selects this solution, where he
- * can choose the right one */
- IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "more than one solution found:\n");
- for (i = disabledcnt; i < disabled.count; i++)
- solver_printproblem(solv, disabled.elements[i]);
- }
- for (i = disabledcnt; i < disabled.count; i++)
- {
- v = disabled.elements[i];
- solver_disableproblem(solv, v);
- if (v >= solv->updaterules && v < solv->updaterules_end)
- {
- Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
- if (r->p)
- solver_enablerule(solv, r);
- }
- }
- }
- }
- /* all done, get us back into the same state as before */
- /* enable refined rules again */
- for (i = 0; i < disabled.count; i++)
- solver_enableproblem(solv, disabled.elements[i]);
- queue_free(&disabled);
- /* reset policy rules */
- for (i = 0; problem[i]; i++)
- solver_enableproblem(solv, problem[i]);
- solver_disablepolicyrules(solv);
- /* disable problem rules again */
- for (i = 0; problem[i]; i++)
- solver_disableproblem(solv, problem[i]);
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "refine_suggestion end\n");
-}
-
-
-/*-------------------------------------------------------------------
- * sorting helper for problems
- *
- * bring update rules before job rules
- * make essential job rules last
- */
-
-static int
-problems_sortcmp(const void *ap, const void *bp, void *dp)
-{
- Queue *job = dp;
- Id a = *(Id *)ap, b = *(Id *)bp;
- if (a < 0 && b > 0)
- return 1;
- if (a > 0 && b < 0)
- return -1;
- if (a < 0 && b < 0)
- {
- int af = job->elements[-a - 1] & SOLVER_ESSENTIAL;
- int bf = job->elements[-b - 1] & SOLVER_ESSENTIAL;
- int x = af - bf;
- if (x)
- return x;
- }
- return a - b;
-}
-
-/*
- * convert a solution rule into a job modifier
- */
-static void
-convertsolution(Solver *solv, Id why, Queue *solutionq)
-{
- Pool *pool = solv->pool;
- if (why < 0)
- {
- why = -why;
- if (why < solv->pooljobcnt)
- {
- queue_push(solutionq, SOLVER_SOLUTION_POOLJOB);
- queue_push(solutionq, why);
- }
- else
- {
- queue_push(solutionq, SOLVER_SOLUTION_JOB);
- queue_push(solutionq, why - solv->pooljobcnt);
- }
- return;
- }
- if (why >= solv->infarchrules && why < solv->infarchrules_end)
- {
- Id p, name;
- /* infarch rule, find replacement */
- assert(solv->rules[why].p < 0);
- name = pool->solvables[-solv->rules[why].p].name;
- while (why > solv->infarchrules && pool->solvables[-solv->rules[why - 1].p].name == name)
- why--;
- p = 0;
- for (; why < solv->infarchrules_end && pool->solvables[-solv->rules[why].p].name == name; why++)
- if (solv->decisionmap[-solv->rules[why].p] > 0)
- {
- p = -solv->rules[why].p;
- break;
- }
- if (!p)
- return; /* false alarm */
- queue_push(solutionq, SOLVER_SOLUTION_INFARCH);
- queue_push(solutionq, p);
- return;
- }
- if (why >= solv->duprules && why < solv->duprules_end)
- {
- Id p, name;
- /* dist upgrade rule, find replacement */
- assert(solv->rules[why].p < 0);
- name = pool->solvables[-solv->rules[why].p].name;
- while (why > solv->duprules && pool->solvables[-solv->rules[why - 1].p].name == name)
- why--;
- p = 0;
- for (; why < solv->duprules_end && pool->solvables[-solv->rules[why].p].name == name; why++)
- if (solv->decisionmap[-solv->rules[why].p] > 0)
- {
- p = -solv->rules[why].p;
- break;
- }
- if (!p)
- return; /* false alarm */
- queue_push(solutionq, SOLVER_SOLUTION_DISTUPGRADE);
- queue_push(solutionq, p);
- return;
- }
- if (why >= solv->updaterules && why < solv->updaterules_end)
- {
- /* update rule, find replacement package */
- Id p, pp, rp = 0;
- Rule *rr;
-
- /* check if this is a false positive, i.e. the update rule is fulfilled */
- rr = solv->rules + why;
- FOR_RULELITERALS(p, pp, rr)
- if (p > 0 && solv->decisionmap[p] > 0)
- return; /* false alarm */
-
- p = solv->installed->start + (why - solv->updaterules);
- if (solv->dupmap_all && solv->rules[why].p != p && solv->decisionmap[p] > 0)
- {
- /* distupgrade case, allow to keep old package */
- queue_push(solutionq, SOLVER_SOLUTION_DISTUPGRADE);
- queue_push(solutionq, p);
- return;
- }
- if (solv->decisionmap[p] > 0)
- return; /* false alarm, turned out we can keep the package */
- rr = solv->rules + solv->featurerules + (why - solv->updaterules);
- if (!rr->p)
- rr = solv->rules + why;
- if (rr->w2)
- {
- int mvrp = 0; /* multi-version replacement */
- FOR_RULELITERALS(rp, pp, rr)
- {
- if (rp > 0 && solv->decisionmap[rp] > 0 && pool->solvables[rp].repo != solv->installed)
- {
- mvrp = rp;
- if (!(solv->multiversion.size && MAPTST(&solv->multiversion, rp)))
- break;
- }
- }
- if (!rp && mvrp)
- {
- /* found only multi-version replacements */
- /* have to split solution into two parts */
- queue_push(solutionq, p);
- queue_push(solutionq, mvrp);
- }
- }
- queue_push(solutionq, p);
- queue_push(solutionq, rp);
- return;
- }
- if (why >= solv->bestrules && why < solv->bestrules_end)
- {
- int mvrp;
- Id p, pp, rp = 0;
- Rule *rr;
- /* check false positive */
- rr = solv->rules + why;
- FOR_RULELITERALS(p, pp, rr)
- if (p > 0 && solv->decisionmap[p] > 0)
- return; /* false alarm */
- /* check update/feature rule */
- p = solv->bestrules_pkg[why - solv->bestrules];
- if (p < 0)
- {
- /* install job */
- queue_push(solutionq, 0);
- queue_push(solutionq, solv->ruletojob.elements[-p - solv->jobrules] + 1);
- return;
- }
- if (solv->decisionmap[p] > 0)
- {
- /* disable best rule by keeping the old package */
- queue_push(solutionq, SOLVER_SOLUTION_BEST);
- queue_push(solutionq, p);
- return;
- }
- rr = solv->rules + solv->featurerules + (p - solv->installed->start);
- if (!rr->p)
- rr = solv->rules + solv->updaterules + (p - solv->installed->start);
- mvrp = 0; /* multi-version replacement */
- FOR_RULELITERALS(rp, pp, rr)
- if (rp > 0 && solv->decisionmap[rp] > 0 && pool->solvables[rp].repo != solv->installed)
- {
- mvrp = rp;
- if (!(solv->multiversion.size && MAPTST(&solv->multiversion, rp)))
- break;
- }
- if (!rp && mvrp)
- {
- queue_push(solutionq, SOLVER_SOLUTION_BEST); /* split, see above */
- queue_push(solutionq, mvrp);
- queue_push(solutionq, p);
- queue_push(solutionq, 0);
- return;
- }
- if (rp)
- {
- queue_push(solutionq, SOLVER_SOLUTION_BEST);
- queue_push(solutionq, rp);
- }
- return;
- }
-}
-
-/*
- * convert problem data into a form usable for refining.
- * Returns the number of problems.
- */
-int
-solver_prepare_solutions(Solver *solv)
-{
- int i, j = 1, idx;
-
- if (!solv->problems.count)
- return 0;
- queue_empty(&solv->solutions);
- queue_push(&solv->solutions, 0); /* dummy so idx is always nonzero */
- idx = solv->solutions.count;
- queue_push(&solv->solutions, -1); /* unrefined */
- /* proofidx stays in position, thus we start with 1 */
- for (i = 1; i < solv->problems.count; i++)
- {
- Id p = solv->problems.elements[i];
- queue_push(&solv->solutions, p);
- if (p)
- continue;
- /* end of problem reached */
- solv->problems.elements[j++] = idx;
- if (i + 1 >= solv->problems.count)
- break;
- /* start another problem */
- solv->problems.elements[j++] = solv->problems.elements[++i]; /* copy proofidx */
- idx = solv->solutions.count;
- queue_push(&solv->solutions, -1); /* unrefined */
- }
- solv->problems.count = j;
- return j / 2;
-}
-
-/*
- * refine the simple solution rule list provided by
- * the solver into multiple lists of job modifiers.
- */
-static void
-create_solutions(Solver *solv, int probnr, int solidx)
-{
- Pool *pool = solv->pool;
- Queue redoq;
- Queue problem, solution, problems_save, branches_save;
- int i, j, nsol;
- int essentialok;
- unsigned int now;
- int oldmistakes = solv->cleandeps_mistakes ? solv->cleandeps_mistakes->count : 0;
- Id extraflags = -1;
- int decisioncnt_update;
- int decisioncnt_keep;
- int decisioncnt_resolve;
- int decisioncnt_weak;
- int decisioncnt_orphan;
-
- now = solv_timems(0);
- queue_init(&redoq);
- /* save decisionq, decisionq_why, decisionmap, and decisioncnt */
- for (i = 0; i < solv->decisionq.count; i++)
- {
- Id p = solv->decisionq.elements[i];
- queue_push(&redoq, p);
- queue_push(&redoq, solv->decisionq_why.elements[i]);
- queue_push(&redoq, solv->decisionmap[p > 0 ? p : -p]);
- }
- decisioncnt_update = solv->decisioncnt_update;
- decisioncnt_keep = solv->decisioncnt_keep;
- decisioncnt_resolve = solv->decisioncnt_resolve;
- decisioncnt_weak = solv->decisioncnt_weak;
- decisioncnt_orphan = solv->decisioncnt_orphan;
-
- /* save problems queue */
- problems_save = solv->problems;
- memset(&solv->problems, 0, sizeof(solv->problems));
-
- /* save branches queue */
- branches_save = solv->problems;
- memset(&solv->branches, 0, sizeof(solv->branches));
-
- /* extract problem from queue */
- queue_init(&problem);
- for (i = solidx + 1; i < solv->solutions.count; i++)
- {
- Id v = solv->solutions.elements[i];
- if (!v)
- break;
- queue_push(&problem, v);
- if (v < 0)
- extraflags &= solv->job.elements[-v - 1];
- }
- if (extraflags == -1)
- extraflags = 0;
- if (problem.count > 1)
- solv_sort(problem.elements, problem.count, sizeof(Id), problems_sortcmp, &solv->job);
- queue_push(&problem, 0); /* mark end for refine_suggestion */
- problem.count--;
-#if 0
- for (i = 0; i < problem.count; i++)
- printf("PP %d %d\n", i, problem.elements[i]);
-#endif
-
- /* refine each solution element */
- nsol = 0;
- essentialok = 0;
- queue_init(&solution);
- for (i = 0; i < problem.count; i++)
- {
- int solstart = solv->solutions.count;
- refine_suggestion(solv, problem.elements, problem.elements[i], &solution, essentialok);
- queue_push(&solv->solutions, 0); /* reserve room for number of elements */
- for (j = 0; j < solution.count; j++)
- convertsolution(solv, solution.elements[j], &solv->solutions);
- if (solv->solutions.count == solstart + 1)
- {
- solv->solutions.count--; /* this one did not work out */
- if (nsol || i + 1 < problem.count)
- continue; /* got one or still hope */
- if (!essentialok)
- {
- /* nothing found, start over */
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "nothing found, re-run with essentialok = 1\n");
- essentialok = 1;
- i = -1;
- continue;
- }
- /* this is bad, we found no solution */
- /* for now just offer a rule */
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "nothing found, already did essentialok, fake it\n");
- queue_push(&solv->solutions, 0);
- for (j = 0; j < problem.count; j++)
- {
- convertsolution(solv, problem.elements[j], &solv->solutions);
- if (solv->solutions.count > solstart + 1)
- break;
- }
- if (solv->solutions.count == solstart + 1)
- {
- solv->solutions.count--;
- continue; /* sorry */
- }
- }
- /* patch in number of solution elements */
- solv->solutions.elements[solstart] = (solv->solutions.count - (solstart + 1)) / 2;
- queue_push(&solv->solutions, 0); /* add end marker */
- queue_push(&solv->solutions, 0); /* add end marker */
- queue_push(&solv->solutions, problem.elements[i]); /* just for bookkeeping */
- queue_push(&solv->solutions, extraflags & SOLVER_CLEANDEPS); /* our extraflags */
- solv->solutions.elements[solidx + 1 + nsol++] = solstart;
- }
- solv->solutions.elements[solidx + 1 + nsol] = 0; /* end marker */
- solv->solutions.elements[solidx] = nsol;
- queue_free(&problem);
- queue_free(&solution);
-
- /* restore decisions */
- memset(solv->decisionmap, 0, pool->nsolvables * sizeof(Id));
- queue_empty(&solv->decisionq);
- queue_empty(&solv->decisionq_why);
- for (i = 0; i < redoq.count; i += 3)
- {
- Id p = redoq.elements[i];
- queue_push(&solv->decisionq, p);
- queue_push(&solv->decisionq_why, redoq.elements[i + 1]);
- solv->decisionmap[p > 0 ? p : -p] = redoq.elements[i + 2];
- }
- queue_free(&redoq);
- solv->decisioncnt_update = decisioncnt_update;
- solv->decisioncnt_keep = decisioncnt_keep;
- solv->decisioncnt_resolve = decisioncnt_resolve;
- solv->decisioncnt_weak = decisioncnt_weak;
- solv->decisioncnt_orphan = decisioncnt_orphan;
-
- /* restore problems */
- queue_free(&solv->problems);
- solv->problems = problems_save;
-
- /* restore branches */
- queue_free(&solv->branches);
- solv->branches = branches_save;
-
- if (solv->cleandeps_mistakes)
- {
- if (oldmistakes)
- queue_truncate(solv->cleandeps_mistakes, oldmistakes);
- else
- {
- queue_free(solv->cleandeps_mistakes);
- solv->cleandeps_mistakes = solv_free(solv->cleandeps_mistakes);
- }
- }
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "create_solutions for problem #%d took %d ms\n", probnr, solv_timems(now));
-}
-
-
-/**************************************************************************/
-
-unsigned int
-solver_problem_count(Solver *solv)
-{
- return solv->problems.count / 2;
-}
-
-Id
-solver_next_problem(Solver *solv, Id problem)
-{
- if (!problem)
- return solv->problems.count ? 1 : 0;
- return (problem + 1) * 2 - 1 < solv->problems.count ? problem + 1 : 0;
-}
-
-unsigned int
-solver_solution_count(Solver *solv, Id problem)
-{
- Id solidx = solv->problems.elements[problem * 2 - 1];
- if (solv->solutions.elements[solidx] < 0)
- create_solutions(solv, problem, solidx);
- return solv->solutions.elements[solidx];
-}
-
-Id
-solver_next_solution(Solver *solv, Id problem, Id solution)
-{
- Id solidx = solv->problems.elements[problem * 2 - 1];
- if (solv->solutions.elements[solidx] < 0)
- create_solutions(solv, problem, solidx);
- return solv->solutions.elements[solidx + solution + 1] ? solution + 1 : 0;
-}
-
-unsigned int
-solver_solutionelement_count(Solver *solv, Id problem, Id solution)
-{
- Id solidx = solv->problems.elements[problem * 2 - 1];
- solidx = solv->solutions.elements[solidx + solution];
- return solv->solutions.elements[solidx];
-}
-
-Id
-solver_solutionelement_internalid(Solver *solv, Id problem, Id solution)
-{
- Id solidx = solv->problems.elements[problem * 2 - 1];
- solidx = solv->solutions.elements[solidx + solution];
- return solv->solutions.elements[solidx + 2 * solv->solutions.elements[solidx] + 3];
-}
-
-Id
-solver_solutionelement_extrajobflags(Solver *solv, Id problem, Id solution)
-{
- Id solidx = solv->problems.elements[problem * 2 - 1];
- solidx = solv->solutions.elements[solidx + solution];
- return solv->solutions.elements[solidx + 2 * solv->solutions.elements[solidx] + 4];
-}
-
-
-/*
- * return the next item of the proposed solution
- * here are the possibilities for p / rp and what
- * the solver expects the application to do:
- * p rp
- * -------------------------------------------------------
- * SOLVER_SOLUTION_INFARCH pkgid
- * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
- * SOLVER_SOLUTION_DISTUPGRADE pkgid
- * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
- * SOLVER_SOLUTION_BEST pkgid
- * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
- * SOLVER_SOLUTION_JOB jobidx
- * -> remove job (jobidx - 1, jobidx) from job queue
- * SOLVER_SOLUTION_POOLJOB jobidx
- * -> remove job (jobidx - 1, jobidx) from pool job queue
- * pkgid (> 0) 0
- * -> add (SOLVER_ERASE|SOLVER_SOLVABLE, p) to the job
- * pkgid (> 0) pkgid (> 0)
- * -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
- * (this will replace package p)
- *
- * Thus, the solver will either ask the application to remove
- * a specific job from the job queue, or ask to add an install/erase
- * job to it.
- *
- */
-
-Id
-solver_next_solutionelement(Solver *solv, Id problem, Id solution, Id element, Id *p, Id *rp)
-{
- Id solidx = solv->problems.elements[problem * 2 - 1];
- solidx = solv->solutions.elements[solidx + solution];
- if (!solidx)
- return 0;
- solidx += 1 + element * 2;
- if (!solv->solutions.elements[solidx] && !solv->solutions.elements[solidx + 1])
- return 0;
- *p = solv->solutions.elements[solidx];
- *rp = solv->solutions.elements[solidx + 1];
- return element + 1;
-}
-
-void
-solver_take_solutionelement(Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job)
-{
- int i;
-
- if (p == SOLVER_SOLUTION_POOLJOB)
- {
- solv->pool->pooljobs.elements[rp - 1] = SOLVER_NOOP;
- solv->pool->pooljobs.elements[rp] = 0;
- return;
- }
- if (p == SOLVER_SOLUTION_JOB)
- {
- job->elements[rp - 1] = SOLVER_NOOP;
- job->elements[rp] = 0;
- return;
- }
- if (rp <= 0 && p <= 0)
- return; /* just in case */
- if (rp > 0)
- p = SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_NOTBYUSER|extrajobflags;
- else
- {
- rp = p;
- p = SOLVER_ERASE|SOLVER_SOLVABLE|extrajobflags;
- }
- for (i = 0; i < job->count; i += 2)
- if (job->elements[i] == p && job->elements[i + 1] == rp)
- return;
- queue_push2(job, p, rp);
-}
-
-void
-solver_take_solution(Solver *solv, Id problem, Id solution, Queue *job)
-{
- Id p, rp, element = 0;
- Id extrajobflags = solver_solutionelement_extrajobflags(solv, problem, solution);
- while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
- solver_take_solutionelement(solv, p, rp, extrajobflags, job);
-}
-
-
-/*-------------------------------------------------------------------
- *
- * find problem rule
- */
-
-static void
-findproblemrule_internal(Solver *solv, Id idx, Id *reqrp, Id *conrp, Id *sysrp, Id *jobrp, Map *rseen)
-{
- Id rid, d;
- Id lreqr, lconr, lsysr, ljobr;
- Rule *r;
- Id jobassert = 0;
- int i, reqset = 0; /* 0: unset, 1: installed, 2: jobassert, 3: assert */
- int conset = 0; /* 0: unset, 1: installed */
-
- /* find us a jobassert rule */
- for (i = idx; (rid = solv->learnt_pool.elements[i]) != 0; i++)
- {
- if (rid < solv->jobrules || rid >= solv->jobrules_end)
- continue;
- r = solv->rules + rid;
- d = r->d < 0 ? -r->d - 1 : r->d;
- if (!d && r->w2 == 0 && r->p > 0)
- {
- jobassert = r->p;
- break;
- }
- }
-
- /* the problem rules are somewhat ordered from "near to the problem" to
- * "near to the job" */
- lreqr = lconr = lsysr = ljobr = 0;
- while ((rid = solv->learnt_pool.elements[idx++]) != 0)
- {
- assert(rid > 0);
- if (rid >= solv->learntrules)
- {
- if (MAPTST(rseen, rid - solv->learntrules))
- continue;
- MAPSET(rseen, rid - solv->learntrules);
- findproblemrule_internal(solv, solv->learnt_why.elements[rid - solv->learntrules], &lreqr, &lconr, &lsysr, &ljobr, rseen);
- }
- else if ((rid >= solv->jobrules && rid < solv->jobrules_end) || (rid >= solv->infarchrules && rid < solv->infarchrules_end) || (rid >= solv->duprules && rid < solv->duprules_end) || (rid >= solv->bestrules && rid < solv->bestrules_end) || (rid >= solv->yumobsrules && rid <= solv->yumobsrules_end))
- {
- if (!*jobrp)
- *jobrp = rid;
- }
- else if (rid >= solv->updaterules && rid < solv->updaterules_end)
- {
- if (!*sysrp)
- *sysrp = rid;
- }
- else
- {
- assert(rid < solv->pkgrules_end);
- r = solv->rules + rid;
- d = r->d < 0 ? -r->d - 1 : r->d;
- if (!d && r->w2 < 0)
- {
- /* prefer conflicts of installed packages */
- if (solv->installed && !conset)
- {
- if (r->p < 0 && (solv->pool->solvables[-r->p].repo == solv->installed ||
- solv->pool->solvables[-r->w2].repo == solv->installed))
- {
- *conrp = rid;
- conset = 1;
- }
- }
- if (!*conrp)
- *conrp = rid;
- }
- else
- {
- if (!d && r->w2 == 0 && reqset < 3)
- {
- if (*reqrp > 0 && r->p < -1)
- {
- Id op = -solv->rules[*reqrp].p;
- if (op > 1 && solv->pool->solvables[op].arch != solv->pool->solvables[-r->p].arch)
- continue; /* different arch, skip */
- }
- /* prefer assertions */
- *reqrp = rid;
- reqset = 3;
- }
- else if (jobassert && r->p == -jobassert)
- {
- /* prefer rules of job assertions */
- *reqrp = rid;
- reqset = 2;
- }
- else if (solv->installed && r->p < 0 && solv->pool->solvables[-r->p].repo == solv->installed && reqset <= 1)
- {
- /* prefer rules of job installed package so that the user doesn't get confused by strange packages */
- *reqrp = rid;
- reqset = 1;
- }
- else if (!*reqrp)
- *reqrp = rid;
- }
- }
- }
- if (!*reqrp && lreqr)
- *reqrp = lreqr;
- if (!*conrp && lconr)
- *conrp = lconr;
- if (!*jobrp && ljobr)
- *jobrp = ljobr;
- if (!*sysrp && lsysr)
- *sysrp = lsysr;
-}
-
-/*
- * find problem rule
- *
- * search for a rule that describes the problem to the
- * user. Actually a pretty hopeless task that may leave the user
- * puzzled. To get all of the needed information use
- * solver_findallproblemrules() instead.
- */
-
-Id
-solver_findproblemrule(Solver *solv, Id problem)
-{
- Id reqr, conr, sysr, jobr;
- Id idx = solv->problems.elements[2 * problem - 2];
- Map rseen;
- reqr = conr = sysr = jobr = 0;
- map_init(&rseen, solv->learntrules ? solv->nrules - solv->learntrules : 0);
- findproblemrule_internal(solv, idx, &reqr, &conr, &sysr, &jobr, &rseen);
- map_free(&rseen);
- /* check if the request is about a not-installed package requiring a installed
- * package conflicting with the non-installed package. In that case return the conflict */
- if (reqr && conr && solv->installed && solv->rules[reqr].p < 0 && solv->rules[conr].p < 0 && solv->rules[conr].w2 < 0)
- {
- Pool *pool = solv->pool;
- Solvable *s = pool->solvables - solv->rules[reqr].p;
- Solvable *s1 = pool->solvables - solv->rules[conr].p;
- Solvable *s2 = pool->solvables - solv->rules[conr].w2;
- Id cp = 0;
- if (s == s1 && s2->repo == solv->installed)
- cp = -solv->rules[conr].w2;
- else if (s == s2 && s1->repo == solv->installed)
- cp = -solv->rules[conr].p;
- if (cp && s1->name != s2->name && s->repo != solv->installed)
- {
- Id p, pp;
- Rule *r = solv->rules + reqr;
- FOR_RULELITERALS(p, pp, r)
- if (p == cp)
- return conr;
- }
- }
- if (reqr)
- return reqr; /* some requires */
- if (conr)
- return conr; /* some conflict */
- if (sysr)
- return sysr; /* an update rule */
- if (jobr)
- return jobr; /* a user request */
- assert(0);
- return 0;
-}
-
-/*-------------------------------------------------------------------*/
-
-static void
-findallproblemrules_internal(Solver *solv, Id idx, Queue *rules, Map *rseen)
-{
- Id rid;
- while ((rid = solv->learnt_pool.elements[idx++]) != 0)
- {
- if (rid >= solv->learntrules)
- {
- if (MAPTST(rseen, rid - solv->learntrules))
- continue;
- MAPSET(rseen, rid - solv->learntrules);
- findallproblemrules_internal(solv, solv->learnt_why.elements[rid - solv->learntrules], rules, rseen);
- continue;
- }
- queue_pushunique(rules, rid);
- }
-}
-
-/*
- * find all problem rule
- *
- * return all rules that lead to the problem. This gives the user
- * all of the information to understand the problem, but the result
- * can be a large number of rules.
- */
-
-void
-solver_findallproblemrules(Solver *solv, Id problem, Queue *rules)
-{
- Map rseen;
- queue_empty(rules);
- map_init(&rseen, solv->learntrules ? solv->nrules - solv->learntrules : 0);
- findallproblemrules_internal(solv, solv->problems.elements[2 * problem - 2], rules, &rseen);
- map_free(&rseen);
-}
-
-const char *
-solver_problemruleinfo2str(Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep)
-{
- Pool *pool = solv->pool;
- char *s;
- switch (type)
- {
- case SOLVER_RULE_DISTUPGRADE:
- return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not belong to a distupgrade repository", 0);
- case SOLVER_RULE_INFARCH:
- return pool_tmpjoin(pool, pool_solvid2str(pool, source), " has inferior architecture", 0);
- case SOLVER_RULE_UPDATE:
- return pool_tmpjoin(pool, "problem with installed package ", pool_solvid2str(pool, source), 0);
- case SOLVER_RULE_JOB:
- return "conflicting requests";
- case SOLVER_RULE_JOB_UNSUPPORTED:
- return "unsupported request";
- case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
- return pool_tmpjoin(pool, "nothing provides requested ", pool_dep2str(pool, dep), 0);
- case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
- return pool_tmpjoin(pool, "package ", pool_dep2str(pool, dep), " does not exist");
- case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
- return pool_tmpjoin(pool, pool_dep2str(pool, dep), " is provided by the system", 0);
- case SOLVER_RULE_PKG:
- return "some dependency problem";
- case SOLVER_RULE_BEST:
- if (source > 0)
- return pool_tmpjoin(pool, "cannot install the best update candidate for package ", pool_solvid2str(pool, source), 0);
- return "cannot install the best candidate for the job";
- case SOLVER_RULE_PKG_NOT_INSTALLABLE:
- return pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " is not installable");
- case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
- s = pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0);
- return pool_tmpappend(pool, s, " needed by ", pool_solvid2str(pool, source));
- case SOLVER_RULE_PKG_SAME_NAME:
- s = pool_tmpjoin(pool, "cannot install both ", pool_solvid2str(pool, source), 0);
- return pool_tmpappend(pool, s, " and ", pool_solvid2str(pool, target));
- case SOLVER_RULE_PKG_CONFLICTS:
- s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
- s = pool_tmpappend(pool, s, " conflicts with ", pool_dep2str(pool, dep));
- return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
- case SOLVER_RULE_PKG_OBSOLETES:
- s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
- s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep));
- return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
- case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
- s = pool_tmpjoin(pool, "installed package ", pool_solvid2str(pool, source), 0);
- s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep));
- return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
- case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
- s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
- s = pool_tmpappend(pool, s, " implicitly obsoletes ", pool_dep2str(pool, dep));
- return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
- case SOLVER_RULE_PKG_REQUIRES:
- s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " requires ");
- return pool_tmpappend(pool, s, pool_dep2str(pool, dep), ", but none of the providers can be installed");
- case SOLVER_RULE_PKG_SELF_CONFLICT:
- s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " conflicts with ");
- return pool_tmpappend(pool, s, pool_dep2str(pool, dep), " provided by itself");
- case SOLVER_RULE_YUMOBS:
- s = pool_tmpjoin(pool, "both package ", pool_solvid2str(pool, source), " and ");
- s = pool_tmpjoin(pool, s, pool_solvid2str(pool, target), " obsolete ");
- return pool_tmpappend(pool, s, pool_dep2str(pool, dep), 0);
- default:
- return "bad problem rule type";
- }
-}
-
-/* convenience function */
-const char *
-solver_problem2str(Solver *solv, Id problem)
-{
- Id type, source, target, dep;
- Id r = solver_findproblemrule(solv, problem);
- if (!r)
- return "no problem rule?";
- type = solver_ruleinfo(solv, r, &source, &target, &dep);
- return solver_problemruleinfo2str(solv, type, source, target, dep);
-}
-
-const char *
-solver_solutionelement2str(Solver *solv, Id p, Id rp)
-{
- Pool *pool = solv->pool;
- if (p == SOLVER_SOLUTION_JOB || p == SOLVER_SOLUTION_POOLJOB)
- {
- Id how, what;
- if (p == SOLVER_SOLUTION_JOB)
- rp += solv->pooljobcnt;
- how = solv->job.elements[rp - 1];
- what = solv->job.elements[rp];
- return pool_tmpjoin(pool, "do not ask to ", pool_job2str(pool, how, what, 0), 0);
- }
- else if (p == SOLVER_SOLUTION_INFARCH)
- {
- Solvable *s = pool->solvables + rp;
- if (solv->installed && s->repo == solv->installed)
- return pool_tmpjoin(pool, "keep ", pool_solvable2str(pool, s), " despite the inferior architecture");
- else
- return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the inferior architecture");
- }
- else if (p == SOLVER_SOLUTION_DISTUPGRADE)
- {
- Solvable *s = pool->solvables + rp;
- if (solv->installed && s->repo == solv->installed)
- return pool_tmpjoin(pool, "keep obsolete ", pool_solvable2str(pool, s), 0);
- else
- return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " from excluded repository");
- }
- else if (p == SOLVER_SOLUTION_BEST)
- {
- Solvable *s = pool->solvables + rp;
- if (solv->installed && s->repo == solv->installed)
- return pool_tmpjoin(pool, "keep old ", pool_solvable2str(pool, s), 0);
- else
- return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the old version");
- }
- else if (p > 0 && rp == 0)
- return pool_tmpjoin(pool, "allow deinstallation of ", pool_solvid2str(pool, p), 0);
- else if (p > 0 && rp > 0)
- {
- const char *sp = pool_solvid2str(pool, p);
- const char *srp = pool_solvid2str(pool, rp);
- const char *str = pool_tmpjoin(pool, "allow replacement of ", sp, 0);
- return pool_tmpappend(pool, str, " with ", srp);
- }
- else
- return "bad solution element";
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * problems.h
- *
- */
-
-#ifndef LIBSOLV_PROBLEMS_H
-#define LIBSOLV_PROBLEMS_H
-
-#include "rules.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-struct _Solver;
-
-#define SOLVER_SOLUTION_JOB (0)
-#define SOLVER_SOLUTION_DISTUPGRADE (-1)
-#define SOLVER_SOLUTION_INFARCH (-2)
-#define SOLVER_SOLUTION_BEST (-3)
-#define SOLVER_SOLUTION_POOLJOB (-4)
-
-void solver_disableproblem(struct _Solver *solv, Id v);
-void solver_enableproblem(struct _Solver *solv, Id v);
-int solver_prepare_solutions(struct _Solver *solv);
-
-unsigned int solver_problem_count(struct _Solver *solv);
-Id solver_next_problem(struct _Solver *solv, Id problem);
-unsigned int solver_solution_count(struct _Solver *solv, Id problem);
-Id solver_next_solution(struct _Solver *solv, Id problem, Id solution);
-unsigned int solver_solutionelement_count(struct _Solver *solv, Id problem, Id solution);
-Id solver_solutionelement_internalid(struct _Solver *solv, Id problem, Id solution);
-Id solver_solutionelement_extrajobflags(struct _Solver *solv, Id problem, Id solution);
-Id solver_next_solutionelement(struct _Solver *solv, Id problem, Id solution, Id element, Id *p, Id *rp);
-
-void solver_take_solutionelement(struct _Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job);
-void solver_take_solution(struct _Solver *solv, Id problem, Id solution, Queue *job);
-
-Id solver_findproblemrule(struct _Solver *solv, Id problem);
-void solver_findallproblemrules(struct _Solver *solv, Id problem, Queue *rules);
-
-extern const char *solver_problemruleinfo2str(struct _Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep);
-extern const char *solver_problem2str(struct _Solver *solv, Id problem);
-extern const char *solver_solutionelement2str(struct _Solver *solv, Id p, Id rp);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- * qsort taken from FreeBSD, slightly modified to match glibc's
- * argument ordering
- */
-
-/* FIXME: should use mergesort instead */
-
-/*-
- * Copyright (c) 1992, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-
-/* $FreeBSD: src/lib/libc/stdlib/qsort.c,v 1.13.2.1.8.1 2010/12/21 17:10:29 kensmith Exp $ */
-
-#include <stdlib.h>
-
-typedef int cmp_t(const void *, const void *, void *);
-static inline char *med3(char *, char *, char *, cmp_t *, void *);
-static inline void swapfunc(char *, char *, int, int);
-
-#ifndef min
-#define min(a, b) (a) < (b) ? a : b
-#endif
-
-/*
- * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
- */
-#define swapcode(TYPE, parmi, parmj, n) { \
- long i = (n) / sizeof (TYPE); \
- TYPE *pi = (TYPE *) (parmi); \
- TYPE *pj = (TYPE *) (parmj); \
- do { \
- TYPE t = *pi; \
- *pi++ = *pj; \
- *pj++ = t; \
- } while (--i > 0); \
-}
-
-#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
- es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
-
-static inline void
-swapfunc(a, b, n, swaptype)
- char *a, *b;
- int n, swaptype;
-{
- if(swaptype <= 1)
- swapcode(long, a, b, n)
- else
- swapcode(char, a, b, n)
-}
-
-#define swap(a, b) \
- if (swaptype == 0) { \
- long t = *(long *)(a); \
- *(long *)(a) = *(long *)(b); \
- *(long *)(b) = t; \
- } else \
- swapfunc(a, b, es, swaptype)
-
-#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
-
-#define CMP(t, x, y) (cmp((x), (y), (t)))
-
-static inline char *
-med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk)
-{
- return CMP(thunk, a, b) < 0 ?
- (CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a ))
- :(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c ));
-}
-
-void
-solv_sort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk)
-{
- char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
- size_t d, r;
- int cmp_result;
- int swaptype, swap_cnt;
-
-loop: SWAPINIT(a, es);
- swap_cnt = 0;
- if (n < 7) {
- for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
- for (pl = pm;
- pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
- pl -= es)
- swap(pl, pl - es);
- return;
- }
- pm = (char *)a + (n / 2) * es;
- if (n > 7) {
- pl = a;
- pn = (char *)a + (n - 1) * es;
- if (n > 40) {
- d = (n / 8) * es;
- pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk);
- pm = med3(pm - d, pm, pm + d, cmp, thunk);
- pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk);
- }
- pm = med3(pl, pm, pn, cmp, thunk);
- }
- swap(a, pm);
- pa = pb = (char *)a + es;
-
- pc = pd = (char *)a + (n - 1) * es;
- for (;;) {
- while (pb <= pc && (cmp_result = CMP(thunk, pb, a)) <= 0) {
- if (cmp_result == 0) {
- swap_cnt = 1;
- swap(pa, pb);
- pa += es;
- }
- pb += es;
- }
- while (pb <= pc && (cmp_result = CMP(thunk, pc, a)) >= 0) {
- if (cmp_result == 0) {
- swap_cnt = 1;
- swap(pc, pd);
- pd -= es;
- }
- pc -= es;
- }
- if (pb > pc)
- break;
- swap(pb, pc);
- swap_cnt = 1;
- pb += es;
- pc -= es;
- }
- if (swap_cnt == 0) { /* Switch to insertion sort */
- for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
- for (pl = pm;
- pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
- pl -= es)
- swap(pl, pl - es);
- return;
- }
-
- pn = (char *)a + n * es;
- r = min(pa - (char *)a, pb - pa);
- vecswap(a, pb - r, r);
- r = min(pd - pc, pn - pd - es);
- vecswap(pb, pn - r, r);
- if ((r = pb - pa) > es)
- solv_sort(a, r / es, es, cmp, thunk);
- if ((r = pd - pc) > es) {
- /* Iterate rather than recurse to save stack space */
- a = pn - r;
- n = r / es;
- goto loop;
- }
-/* qsort(pn - r, r / es, es, cmp);*/
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * queue.c
- *
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "queue.h"
-#include "util.h"
-
-#define EXTRA_SPACE 8
-#define EXTRA_SPACE_HEAD 8
-
-void
-queue_init(Queue *q)
-{
- q->alloc = q->elements = 0;
- q->count = q->left = 0;
-}
-
-void
-queue_init_clone(Queue *t, Queue *s)
-{
- if (!s->elements)
- {
- t->alloc = t->elements = 0;
- t->count = t->left = 0;
- return;
- }
- t->alloc = t->elements = solv_malloc2(s->count + EXTRA_SPACE, sizeof(Id));
- if (s->count)
- memcpy(t->alloc, s->elements, s->count * sizeof(Id));
- t->count = s->count;
- t->left = EXTRA_SPACE;
-}
-
-void
-queue_init_buffer(Queue *q, Id *buf, int size)
-{
- q->alloc = 0;
- q->elements = buf;
- q->count = 0;
- q->left = size;
-}
-
-void
-queue_free(Queue *q)
-{
- if (q->alloc)
- solv_free(q->alloc);
- q->alloc = q->elements = 0;
- q->count = q->left = 0;
-}
-
-void
-queue_alloc_one(Queue *q)
-{
- if (!q->alloc)
- {
- q->alloc = solv_malloc2(q->count + EXTRA_SPACE, sizeof(Id));
- if (q->count)
- memcpy(q->alloc, q->elements, q->count * sizeof(Id));
- q->elements = q->alloc;
- q->left = EXTRA_SPACE;
- }
- else if (q->alloc != q->elements)
- {
- int l = q->elements - q->alloc;
- if (q->count)
- memmove(q->alloc, q->elements, q->count * sizeof(Id));
- q->elements -= l;
- q->left += l;
- }
- else
- {
- q->elements = q->alloc = solv_realloc2(q->alloc, q->count + EXTRA_SPACE, sizeof(Id));
- q->left = EXTRA_SPACE;
- }
-}
-
-/* make room for an element in front of queue */
-void
-queue_alloc_one_head(Queue *q)
-{
- int l;
- if (!q->alloc || !q->left)
- queue_alloc_one(q);
- l = q->left > EXTRA_SPACE_HEAD ? EXTRA_SPACE_HEAD : q->left;
- if (q->count)
- memmove(q->elements + l, q->elements, q->count * sizeof(Id));
- q->elements += l;
- q->left -= l;
-}
-
-void
-queue_insert(Queue *q, int pos, Id id)
-{
- queue_push(q, id); /* make room */
- if (pos < q->count - 1)
- {
- memmove(q->elements + pos + 1, q->elements + pos, (q->count - 1 - pos) * sizeof(Id));
- q->elements[pos] = id;
- }
-}
-
-void
-queue_delete(Queue *q, int pos)
-{
- if (pos >= q->count)
- return;
- if (pos < q->count - 1)
- memmove(q->elements + pos, q->elements + pos + 1, (q->count - 1 - pos) * sizeof(Id));
- q->left++;
- q->count--;
-}
-
-void
-queue_insert2(Queue *q, int pos, Id id1, Id id2)
-{
- queue_push(q, id1); /* make room */
- queue_push(q, id2); /* make room */
- if (pos < q->count - 2)
- {
- memmove(q->elements + pos + 2, q->elements + pos, (q->count - 2 - pos) * sizeof(Id));
- q->elements[pos] = id1;
- q->elements[pos + 1] = id2;
- }
-}
-
-void
-queue_delete2(Queue *q, int pos)
-{
- if (pos >= q->count)
- return;
- if (pos == q->count - 1)
- {
- q->left++;
- q->count--;
- return;
- }
- if (pos < q->count - 2)
- memmove(q->elements + pos, q->elements + pos + 2, (q->count - 2 - pos) * sizeof(Id));
- q->left += 2;
- q->count -= 2;
-}
-
-void
-queue_insertn(Queue *q, int pos, int n, Id *elements)
-{
- if (n <= 0)
- return;
- if (pos > q->count)
- pos = q->count;
- if (q->left < n)
- {
- int off;
- if (!q->alloc)
- queue_alloc_one(q);
- off = q->elements - q->alloc;
- q->alloc = solv_realloc2(q->alloc, off + q->count + n + EXTRA_SPACE, sizeof(Id));
- q->elements = q->alloc + off;
- q->left = n + EXTRA_SPACE;
- }
- if (pos < q->count)
- memmove(q->elements + pos + n, q->elements + pos, (q->count - pos) * sizeof(Id));
- if (elements)
- memcpy(q->elements + pos, elements, n * sizeof(Id));
- else
- memset(q->elements + pos, 0, n * sizeof(Id));
- q->left -= n;
- q->count += n;
-}
-
-void
-queue_deleten(Queue *q, int pos, int n)
-{
- if (n <= 0 || pos >= q->count)
- return;
- if (pos + n >= q->count)
- n = q->count - pos;
- else
- memmove(q->elements + pos, q->elements + pos + n, (q->count - n - pos) * sizeof(Id));
- q->left += n;
- q->count -= n;
-}
-
-/* allocate room for n more elements */
-void
-queue_prealloc(Queue *q, int n)
-{
- int off;
- if (n <= 0 || q->left >= n)
- return;
- if (!q->alloc)
- queue_alloc_one(q);
- off = q->elements - q->alloc;
- q->alloc = solv_realloc2(q->alloc, off + q->count + n + EXTRA_SPACE, sizeof(Id));
- q->elements = q->alloc + off;
- q->left = n + EXTRA_SPACE;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * queue.h
- *
- */
-
-#ifndef LIBSOLV_QUEUE_H
-#define LIBSOLV_QUEUE_H
-
-#include "pooltypes.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct _Queue {
- Id *elements; /* pointer to elements */
- int count; /* current number of elements in queue */
- Id *alloc; /* this is whats actually allocated, elements > alloc if shifted */
- int left; /* space left in alloc *after* elements+count */
-} Queue;
-
-
-extern void queue_alloc_one(Queue *q); /* internal */
-extern void queue_alloc_one_head(Queue *q); /* internal */
-
-/* clear queue */
-static inline void
-queue_empty(Queue *q)
-{
- if (q->alloc)
- {
- q->left += (q->elements - q->alloc) + q->count;
- q->elements = q->alloc;
- }
- else
- q->left += q->count;
- q->count = 0;
-}
-
-static inline Id
-queue_shift(Queue *q)
-{
- if (!q->count)
- return 0;
- q->count--;
- return *q->elements++;
-}
-
-static inline Id
-queue_pop(Queue *q)
-{
- if (!q->count)
- return 0;
- q->left++;
- return q->elements[--q->count];
-}
-
-static inline void
-queue_unshift(Queue *q, Id id)
-{
- if (!q->alloc || q->alloc == q->elements)
- queue_alloc_one_head(q);
- *--q->elements = id;
- q->count++;
-}
-
-static inline void
-queue_push(Queue *q, Id id)
-{
- if (!q->left)
- queue_alloc_one(q);
- q->elements[q->count++] = id;
- q->left--;
-}
-
-static inline void
-queue_pushunique(Queue *q, Id id)
-{
- int i;
- for (i = q->count; i > 0; )
- if (q->elements[--i] == id)
- return;
- queue_push(q, id);
-}
-
-static inline void
-queue_push2(Queue *q, Id id1, Id id2)
-{
- queue_push(q, id1);
- queue_push(q, id2);
-}
-
-static inline void
-queue_truncate(Queue *q, int n)
-{
- if (q->count > n)
- {
- q->left += q->count - n;
- q->count = n;
- }
-}
-
-extern void queue_init(Queue *q);
-extern void queue_init_buffer(Queue *q, Id *buf, int size);
-extern void queue_init_clone(Queue *t, Queue *s);
-extern void queue_free(Queue *q);
-
-extern void queue_insert(Queue *q, int pos, Id id);
-extern void queue_insert2(Queue *q, int pos, Id id1, Id id2);
-extern void queue_insertn(Queue *q, int pos, int n, Id *elements);
-extern void queue_delete(Queue *q, int pos);
-extern void queue_delete2(Queue *q, int pos);
-extern void queue_deleten(Queue *q, int pos, int n);
-extern void queue_prealloc(Queue *q, int n);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_QUEUE_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo.c
- *
- * Manage metadata coming from one repository
- *
- */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <fnmatch.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-
-
-#include "repo.h"
-#include "pool.h"
-#include "poolid_private.h"
-#include "util.h"
-#include "chksum.h"
-
-#define IDARRAY_BLOCK 4095
-
-
-/*
- * create empty repo
- * and add to pool
- */
-
-Repo *
-repo_create(Pool *pool, const char *name)
-{
- Repo *repo;
-
- pool_freewhatprovides(pool);
- repo = (Repo *)solv_calloc(1, sizeof(*repo));
- if (!pool->nrepos)
- {
- pool->nrepos = 1; /* start with repoid 1 */
- pool->repos = (Repo **)solv_calloc(2, sizeof(Repo *));
- }
- else
- pool->repos = (Repo **)solv_realloc2(pool->repos, pool->nrepos + 1, sizeof(Repo *));
- pool->repos[pool->nrepos] = repo;
- pool->urepos++;
- repo->repoid = pool->nrepos++;
- repo->name = name ? solv_strdup(name) : 0;
- repo->pool = pool;
- repo->start = pool->nsolvables;
- repo->end = pool->nsolvables;
- repo->nsolvables = 0;
- return repo;
-}
-
-void
-repo_freedata(Repo *repo)
-{
- int i;
- for (i = 1; i < repo->nrepodata; i++)
- repodata_freedata(repo->repodata + i);
- solv_free(repo->repodata);
- solv_free(repo->idarraydata);
- solv_free(repo->rpmdbid);
- solv_free(repo->lastidhash);
- solv_free((char *)repo->name);
- solv_free(repo);
-}
-
-/* delete all solvables and repodata blocks from this repo */
-
-void
-repo_empty(Repo *repo, int reuseids)
-{
- Pool *pool = repo->pool;
- Solvable *s;
- int i;
-
- pool_freewhatprovides(pool);
- if (reuseids && repo->end == pool->nsolvables)
- {
- /* it's ok to reuse the ids. As this is the last repo, we can
- just shrink the solvable array */
- for (i = repo->end - 1, s = pool->solvables + i; i >= repo->start; i--, s--)
- if (s->repo != repo)
- break;
- pool_free_solvable_block(pool, i + 1, repo->end - (i + 1), reuseids);
- repo->end = i + 1;
- }
- /* zero out (i.e. free) solvables belonging to this repo */
- for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
- if (s->repo == repo)
- memset(s, 0, sizeof(*s));
- repo->end = repo->start;
- repo->nsolvables = 0;
-
- /* free all data belonging to this repo */
- repo->idarraydata = solv_free(repo->idarraydata);
- repo->idarraysize = 0;
- repo->lastoff = 0;
- repo->rpmdbid = solv_free(repo->rpmdbid);
- for (i = 1; i < repo->nrepodata; i++)
- repodata_freedata(repo->repodata + i);
- solv_free(repo->repodata);
- repo->repodata = 0;
- repo->nrepodata = 0;
-}
-
-/*
- * remove repo from pool, delete solvables
- *
- */
-
-void
-repo_free(Repo *repo, int reuseids)
-{
- Pool *pool = repo->pool;
- int i;
-
- if (repo == pool->installed)
- pool->installed = 0;
- repo_empty(repo, reuseids);
- for (i = 1; i < pool->nrepos; i++) /* find repo in pool */
- if (pool->repos[i] == repo)
- break;
- if (i == pool->nrepos) /* repo not in pool, return */
- return;
- if (i == pool->nrepos - 1 && reuseids)
- pool->nrepos--;
- else
- pool->repos[i] = 0;
- pool->urepos--;
- repo_freedata(repo);
-}
-
-Id
-repo_add_solvable(Repo *repo)
-{
- Id p = pool_add_solvable(repo->pool);
- if (!repo->start || repo->start == repo->end)
- repo->start = repo->end = p;
- /* warning: sidedata must be extended before adapting start/end */
- if (repo->rpmdbid)
- repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, 1);
- if (p < repo->start)
- repo->start = p;
- if (p + 1 > repo->end)
- repo->end = p + 1;
- repo->nsolvables++;
- repo->pool->solvables[p].repo = repo;
- return p;
-}
-
-Id
-repo_add_solvable_block(Repo *repo, int count)
-{
- Id p;
- Solvable *s;
- if (!count)
- return 0;
- p = pool_add_solvable_block(repo->pool, count);
- if (!repo->start || repo->start == repo->end)
- repo->start = repo->end = p;
- /* warning: sidedata must be extended before adapting start/end */
- if (repo->rpmdbid)
- repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, count);
- if (p < repo->start)
- repo->start = p;
- if (p + count > repo->end)
- repo->end = p + count;
- repo->nsolvables += count;
- for (s = repo->pool->solvables + p; count--; s++)
- s->repo = repo;
- return p;
-}
-
-void
-repo_free_solvable(Repo *repo, Id p, int reuseids)
-{
- repo_free_solvable_block(repo, p, 1, reuseids);
-}
-
-void
-repo_free_solvable_block(Repo *repo, Id start, int count, int reuseids)
-{
- Solvable *s;
- Repodata *data;
- int i;
- if (start + count == repo->end)
- repo->end -= count;
- repo->nsolvables -= count;
- for (s = repo->pool->solvables + start, i = count; i--; s++)
- s->repo = 0;
- pool_free_solvable_block(repo->pool, start, count, reuseids);
- FOR_REPODATAS(repo, i, data)
- {
- int dstart, dend;
- if (data->end > repo->end)
- repodata_shrink(data, repo->end);
- dstart = data->start > start ? data->start : start;
- dend = data->end < start + count ? data->end : start + count;
- if (dstart < dend)
- {
- if (data->attrs)
- {
- int j;
- for (j = dstart; j < dend; j++)
- data->attrs[j - data->start] = solv_free(data->attrs[j - data->start]);
- }
- if (data->incoreoffset)
- memset(data->incoreoffset + (dstart - data->start), 0, (dend - dstart) * sizeof(Id));
- }
- }
-}
-
-/* specialized version of repo_add_solvable_block that inserts the new solvable
- * block before the indicated repo, which gets relocated.
- * used in repo_add_rpmdb
- */
-Id
-repo_add_solvable_block_before(Repo *repo, int count, Repo *beforerepo)
-{
- Pool *pool = repo->pool;
- Id p;
- Solvable *s;
- Repodata *data;
- int i;
-
- if (!count || !beforerepo || beforerepo->end != pool->nsolvables || beforerepo->start == beforerepo->end)
- return repo_add_solvable_block(repo, count);
- p = beforerepo->start;
- /* make sure all solvables belong to beforerepo */
- for (i = p, s = pool->solvables + i; i < beforerepo->end; i++, s++)
- if (s->repo && s->repo != beforerepo)
- return repo_add_solvable_block(repo, count);
- /* now move beforerepo to back */
- pool_add_solvable_block(pool, count); /* must return beforerepo->end! */
- memmove(pool->solvables + p + count, pool->solvables + p, (beforerepo->end - p) * sizeof(Solvable));
- memset(pool->solvables + p, 0, sizeof(Solvable) * count);
- /* adapt repodata */
- FOR_REPODATAS(beforerepo, i, data)
- {
- if (data->start < p)
- continue;
- data->start += count;
- data->end += count;
- }
- beforerepo->start += count;
- beforerepo->end += count;
- /* we now have count free solvables at id p */
- /* warning: sidedata must be extended before adapting start/end */
- if (repo->rpmdbid)
- repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, count);
- if (p < repo->start)
- repo->start = p;
- if (p + count > repo->end)
- repo->end = p + count;
- repo->nsolvables += count;
- for (s = pool->solvables + p; count--; s++)
- s->repo = repo;
- return p;
-}
-
-
-/* repository sidedata is solvable data allocated on demand.
- * It is used for data that is normally not present
- * in the solvable like the rpmdbid.
- * The solvable allocation funcions need to make sure that
- * the sidedata gets extended if new solvables get added.
- */
-
-#define REPO_SIDEDATA_BLOCK 63
-
-void *
-repo_sidedata_create(Repo *repo, size_t size)
-{
- return solv_calloc_block(repo->end - repo->start, size, REPO_SIDEDATA_BLOCK);
-}
-
-void *
-repo_sidedata_extend(Repo *repo, void *b, size_t size, Id p, int count)
-{
- int n = repo->end - repo->start;
- if (p < repo->start)
- {
- int d = repo->start - p;
- b = solv_extend(b, n, d, size, REPO_SIDEDATA_BLOCK);
- memmove((char *)b + d * size, b, n * size);
- memset(b, 0, d * size);
- n += d;
- }
- if (p + count > repo->end)
- {
- int d = p + count - repo->end;
- b = solv_extend(b, n, d, size, REPO_SIDEDATA_BLOCK);
- memset((char *)b + n * size, 0, d * size);
- }
- return b;
-}
-
-/*
- * add Id to idarraydata used to store dependencies
- * olddeps: old array offset to extend
- * returns new array offset
- */
-
-Offset
-repo_addid(Repo *repo, Offset olddeps, Id id)
-{
- Id *idarray;
- int idarraysize;
- int i;
-
- idarray = repo->idarraydata;
- idarraysize = repo->idarraysize;
-
- if (!idarray) /* alloc idarray if not done yet */
- {
- idarraysize = 1;
- idarray = solv_extend_resize(0, 1, sizeof(Id), IDARRAY_BLOCK);
- idarray[0] = 0;
- repo->lastoff = 0;
- }
-
- if (!olddeps) /* no deps yet */
- {
- olddeps = idarraysize;
- idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
- }
- else if (olddeps == repo->lastoff) /* extend at end */
- idarraysize--;
- else /* can't extend, copy old */
- {
- i = olddeps;
- olddeps = idarraysize;
- for (; idarray[i]; i++)
- {
- idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
- idarray[idarraysize++] = idarray[i];
- }
- idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
- }
-
- idarray[idarraysize++] = id; /* insert Id into array */
- idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
- idarray[idarraysize++] = 0; /* ensure NULL termination */
-
- repo->idarraydata = idarray;
- repo->idarraysize = idarraysize;
- repo->lastoff = olddeps;
-
- return olddeps;
-}
-
-#define REPO_ADDID_DEP_HASHTHRES 64
-#define REPO_ADDID_DEP_HASHMIN 128
-
-/*
- * Optimization for packages with an excessive amount of provides/requires:
- * if the number of deps exceed a threshold, we build a hash of the already
- * seen ids.
- */
-static Offset
-repo_addid_dep_hash(Repo *repo, Offset olddeps, Id id, Id marker, int size)
-{
- Id oid, *oidp;
- int before;
- Hashval h, hh;
- Id hid;
-
- before = 0;
- if (marker)
- {
- if (marker < 0)
- {
- marker = -marker;
- before = 1;
- }
- if (marker == id)
- marker = 0;
- }
-
- /* maintain hash and lastmarkerpos */
- if (repo->lastidhash_idarraysize != repo->idarraysize || (Hashval)size * 2 > repo->lastidhash_mask || repo->lastmarker != marker)
- {
- repo->lastmarkerpos = 0;
- if (size * 2 > (Hashval)repo->lastidhash_mask)
- {
- repo->lastidhash_mask = mkmask(size < REPO_ADDID_DEP_HASHMIN ? REPO_ADDID_DEP_HASHMIN : size);
- repo->lastidhash = solv_realloc2(repo->lastidhash, repo->lastidhash_mask + 1, sizeof(Id));
- }
- memset(repo->lastidhash, 0, (repo->lastidhash_mask + 1) * sizeof(Id));
- for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
- {
- h = oid & repo->lastidhash_mask;
- hh = HASHCHAIN_START;
- while (repo->lastidhash[h] != 0)
- h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
- repo->lastidhash[h] = oid;
- if (marker && oid == marker)
- repo->lastmarkerpos = oidp - repo->idarraydata;
- }
- repo->lastmarker = marker;
- repo->lastidhash_idarraysize = repo->idarraysize;
- }
-
- /* check the hash! */
- h = id & repo->lastidhash_mask;
- hh = HASHCHAIN_START;
- while ((hid = repo->lastidhash[h]) != 0 && hid != id)
- h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
- /* put new element in hash */
- if (!hid)
- repo->lastidhash[h] = id;
- else if (marker == SOLVABLE_FILEMARKER && (!before || !repo->lastmarkerpos))
- return olddeps;
- if (marker && !before && !repo->lastmarkerpos)
- {
- /* we have to add the marker first */
- repo->lastmarkerpos = repo->idarraysize - 1;
- olddeps = repo_addid(repo, olddeps, marker);
- /* now put marker in hash */
- h = marker & repo->lastidhash_mask;
- hh = HASHCHAIN_START;
- while (repo->lastidhash[h] != 0)
- h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
- repo->lastidhash[h] = marker;
- repo->lastidhash_idarraysize = repo->idarraysize;
- }
- if (!hid)
- {
- /* new entry, insert in correct position */
- if (marker && before && repo->lastmarkerpos)
- {
- /* need to add it before the marker */
- olddeps = repo_addid(repo, olddeps, id); /* dummy to make room */
- memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (repo->idarraysize - repo->lastmarkerpos - 2) * sizeof(Id));
- repo->idarraydata[repo->lastmarkerpos++] = id;
- }
- else
- {
- /* just append it to the end */
- olddeps = repo_addid(repo, olddeps, id);
- }
- repo->lastidhash_idarraysize = repo->idarraysize;
- return olddeps;
- }
- /* we already have it in the hash */
- if (!marker)
- return olddeps;
- if (marker == SOLVABLE_FILEMARKER)
- {
- /* check if it is in the wrong half */
- /* (we already made sure that "before" and "lastmarkerpos" are set, see above) */
- for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++)
- if (oid == id)
- break;
- if (!oid)
- return olddeps;
- /* yes, wrong half. copy it over */
- memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (oidp - (repo->idarraydata + repo->lastmarkerpos)) * sizeof(Id));
- repo->idarraydata[repo->lastmarkerpos++] = id;
- return olddeps;
- }
- if (before)
- return olddeps;
- /* check if it is in the correct half */
- for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++)
- if (oid == id)
- return olddeps;
- /* nope, copy it over */
- for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
- if (oid == id)
- break;
- if (!oid)
- return olddeps; /* should not happen */
- memmove(oidp, oidp + 1, (repo->idarraydata + repo->idarraysize - oidp - 2) * sizeof(Id));
- repo->idarraydata[repo->idarraysize - 2] = id;
- repo->lastmarkerpos--; /* marker has been moved */
- return olddeps;
-}
-
-/*
- * add dependency (as Id) to repo, also unifies dependencies
- * olddeps = offset into idarraydata
- * marker= 0 for normal dep
- * marker > 0 add dep after marker
- * marker < 0 add dep before -marker
- * returns new start of dependency array
- */
-Offset
-repo_addid_dep(Repo *repo, Offset olddeps, Id id, Id marker)
-{
- Id oid, *oidp, *markerp;
- int before;
-
- if (!olddeps)
- {
- if (marker > 0)
- olddeps = repo_addid(repo, olddeps, marker);
- return repo_addid(repo, olddeps, id);
- }
-
- /* check if we should use the hash optimization */
- if (olddeps == repo->lastoff)
- {
- int size = repo->idarraysize - 1 - repo->lastoff;
- if (size >= REPO_ADDID_DEP_HASHTHRES)
- return repo_addid_dep_hash(repo, olddeps, id, marker, size);
- }
-
- before = 0;
- if (marker)
- {
- if (marker < 0)
- {
- marker = -marker;
- before = 1;
- }
- if (marker == id)
- marker = 0;
- }
-
- if (!marker)
- {
- for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
- if (oid == id)
- return olddeps;
- return repo_addid(repo, olddeps, id);
- }
-
- markerp = 0;
- for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
- {
- if (oid == marker)
- markerp = oidp;
- else if (oid == id)
- break;
- }
-
- if (oid)
- {
- if (marker == SOLVABLE_FILEMARKER)
- {
- if (!markerp || !before)
- return olddeps;
- /* we found it, but in the second half */
- memmove(markerp + 1, markerp, (oidp - markerp) * sizeof(Id));
- *markerp = id;
- return olddeps;
- }
- if (markerp || before)
- return olddeps;
- /* we found it, but in the first half */
- markerp = oidp++;
- for (; (oid = *oidp) != 0; oidp++)
- if (oid == marker)
- break;
- if (!oid)
- {
- /* no marker in array yet */
- oidp--;
- if (markerp < oidp)
- memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
- *oidp = marker;
- return repo_addid(repo, olddeps, id);
- }
- while (oidp[1])
- oidp++;
- memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
- *oidp = id;
- return olddeps;
- }
- /* id not yet in array */
- if (!before && !markerp)
- olddeps = repo_addid(repo, olddeps, marker);
- else if (before && markerp)
- {
- *markerp++ = id;
- id = *--oidp;
- if (markerp < oidp)
- memmove(markerp + 1, markerp, (oidp - markerp) * sizeof(Id));
- *markerp = marker;
- }
- return repo_addid(repo, olddeps, id);
-}
-
-/* return standard marker for the keyname dependency.
- * 1: return positive marker, -1: return negative marker
- */
-Id
-solv_depmarker(Id keyname, Id marker)
-{
- if (marker != 1 && marker != -1)
- return marker;
- if (keyname == SOLVABLE_PROVIDES)
- return marker < 0 ? -SOLVABLE_FILEMARKER : SOLVABLE_FILEMARKER;
- if (keyname == SOLVABLE_REQUIRES)
- return marker < 0 ? -SOLVABLE_PREREQMARKER : SOLVABLE_PREREQMARKER;
- return 0;
-}
-
-/*
- * reserve Ids
- * make space for 'num' more dependencies
- * returns new start of dependency array
- *
- * reserved ids will always begin at offset idarraysize
- */
-Offset
-repo_reserve_ids(Repo *repo, Offset olddeps, int num)
-{
- num++; /* room for trailing ID_NULL */
-
- if (!repo->idarraysize) /* ensure buffer space */
- {
- repo->idarraysize = 1;
- repo->idarraydata = solv_extend_resize(0, 1 + num, sizeof(Id), IDARRAY_BLOCK);
- repo->idarraydata[0] = 0;
- repo->lastoff = 1;
- return 1;
- }
-
- if (olddeps && olddeps != repo->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 = repo->idarraydata + olddeps; *idend++; ) /* find end */
- ;
- count = idend - idstart - 1 + num; /* new size */
-
- repo->idarraydata = solv_extend(repo->idarraydata, repo->idarraysize, count, sizeof(Id), IDARRAY_BLOCK);
- /* move old deps to end */
- olddeps = repo->lastoff = repo->idarraysize;
- memcpy(repo->idarraydata + olddeps, idstart, count - num);
- repo->idarraysize = olddeps + count - num;
-
- return olddeps;
- }
-
- if (olddeps) /* appending */
- repo->idarraysize--;
-
- /* make room*/
- repo->idarraydata = solv_extend(repo->idarraydata, repo->idarraysize, num, sizeof(Id), IDARRAY_BLOCK);
-
- /* appending or new */
- repo->lastoff = olddeps ? olddeps : repo->idarraysize;
-
- return repo->lastoff;
-}
-
-
-/***********************************************************************/
-
-/*
- * some SUSE specific fixups, should go into a separate file
- */
-
-Offset
-repo_fix_supplements(Repo *repo, Offset provides, Offset supplements, Offset freshens)
-{
- Pool *pool = repo->pool;
- Id id, idp, idl;
- char buf[1024], *p, *dep;
- int i, l;
-
- if (provides)
- {
- for (i = provides; repo->idarraydata[i]; i++)
- {
- id = repo->idarraydata[i];
- if (ISRELDEP(id))
- continue;
- dep = (char *)pool_id2str(pool, id);
- if (!strncmp(dep, "locale(", 7) && strlen(dep) < sizeof(buf) - 2)
- {
- idp = 0;
- strcpy(buf + 2, dep);
- dep = buf + 2 + 7;
- if ((p = strchr(dep, ':')) != 0 && p != dep)
- {
- *p++ = 0;
- idp = pool_str2id(pool, dep, 1);
- dep = p;
- }
- id = 0;
- while ((p = strchr(dep, ';')) != 0)
- {
- if (p == dep)
- {
- dep = p + 1;
- continue;
- }
- *p++ = 0;
- idl = pool_str2id(pool, dep, 1);
- idl = pool_rel2id(pool, NAMESPACE_LANGUAGE, idl, REL_NAMESPACE, 1);
- if (id)
- id = pool_rel2id(pool, id, idl, REL_OR, 1);
- else
- id = idl;
- dep = p;
- }
- if (dep[0] && dep[1])
- {
- for (p = dep; *p && *p != ')'; p++)
- ;
- *p = 0;
- idl = pool_str2id(pool, dep, 1);
- idl = pool_rel2id(pool, NAMESPACE_LANGUAGE, idl, REL_NAMESPACE, 1);
- if (id)
- id = pool_rel2id(pool, id, idl, REL_OR, 1);
- else
- id = idl;
- }
- if (idp)
- id = pool_rel2id(pool, idp, id, REL_AND, 1);
- if (id)
- supplements = repo_addid_dep(repo, supplements, id, 0);
- }
- else if ((p = strchr(dep, ':')) != 0 && p != dep && p[1] == '/' && strlen(dep) < sizeof(buf))
- {
- strcpy(buf, dep);
- p = buf + (p - dep);
- *p++ = 0;
- idp = pool_str2id(pool, buf, 1);
- /* strip trailing slashes */
- l = strlen(p);
- while (l > 1 && p[l - 1] == '/')
- p[--l] = 0;
- id = pool_str2id(pool, p, 1);
- id = pool_rel2id(pool, idp, id, REL_WITH, 1);
- id = pool_rel2id(pool, NAMESPACE_SPLITPROVIDES, id, REL_NAMESPACE, 1);
- supplements = repo_addid_dep(repo, supplements, id, 0);
- }
- }
- }
- if (supplements)
- {
- for (i = supplements; repo->idarraydata[i]; i++)
- {
- id = repo->idarraydata[i];
- if (ISRELDEP(id))
- continue;
- dep = (char *)pool_id2str(pool, id);
- if (!strncmp(dep, "system:modalias(", 16))
- dep += 7;
- if (!strncmp(dep, "modalias(", 9) && dep[9] && dep[10] && strlen(dep) < sizeof(buf))
- {
- strcpy(buf, dep);
- p = strchr(buf + 9, ':');
- if (p && p != buf + 9 && strchr(p + 1, ':'))
- {
- *p++ = 0;
- idp = pool_str2id(pool, buf + 9, 1);
- p[strlen(p) - 1] = 0;
- id = pool_str2id(pool, p, 1);
- id = pool_rel2id(pool, NAMESPACE_MODALIAS, id, REL_NAMESPACE, 1);
- id = pool_rel2id(pool, idp, id, REL_AND, 1);
- }
- else
- {
- p = buf + 9;
- p[strlen(p) - 1] = 0;
- id = pool_str2id(pool, p, 1);
- id = pool_rel2id(pool, NAMESPACE_MODALIAS, id, REL_NAMESPACE, 1);
- }
- if (id)
- repo->idarraydata[i] = id;
- }
- else if (!strncmp(dep, "packageand(", 11) && strlen(dep) < sizeof(buf))
- {
- strcpy(buf, dep);
- id = 0;
- dep = buf + 11;
- while ((p = strchr(dep, ':')) != 0)
- {
- if (p == dep)
- {
- dep = p + 1;
- continue;
- }
- /* argh, allow pattern: prefix. sigh */
- if (p - dep == 7 && !strncmp(dep, "pattern", 7))
- {
- p = strchr(p + 1, ':');
- if (!p)
- break;
- }
- *p++ = 0;
- idp = pool_str2id(pool, dep, 1);
- if (id)
- id = pool_rel2id(pool, id, idp, REL_AND, 1);
- else
- id = idp;
- dep = p;
- }
- if (dep[0] && dep[1])
- {
- dep[strlen(dep) - 1] = 0;
- idp = pool_str2id(pool, dep, 1);
- if (id)
- id = pool_rel2id(pool, id, idp, REL_AND, 1);
- else
- id = idp;
- }
- if (id)
- repo->idarraydata[i] = id;
- }
- else if (!strncmp(dep, "filesystem(", 11) && strlen(dep) < sizeof(buf))
- {
- strcpy(buf, dep + 11);
- if ((p = strrchr(buf, ')')) != 0)
- *p = 0;
- id = pool_str2id(pool, buf, 1);
- id = pool_rel2id(pool, NAMESPACE_FILESYSTEM, id, REL_NAMESPACE, 1);
- repo->idarraydata[i] = id;
- }
- }
- }
- if (freshens && repo->idarraydata[freshens])
- {
- Id idsupp = 0, idfresh = 0;
- if (!supplements || !repo->idarraydata[supplements])
- return freshens;
- for (i = supplements; repo->idarraydata[i]; i++)
- {
- if (!idsupp)
- idsupp = repo->idarraydata[i];
- else
- idsupp = pool_rel2id(pool, idsupp, repo->idarraydata[i], REL_OR, 1);
- }
- for (i = freshens; repo->idarraydata[i]; i++)
- {
- if (!idfresh)
- idfresh = repo->idarraydata[i];
- else
- idfresh = pool_rel2id(pool, idfresh, repo->idarraydata[i], REL_OR, 1);
- }
- if (!idsupp)
- idsupp = idfresh;
- else
- idsupp = pool_rel2id(pool, idsupp, idfresh, REL_AND, 1);
- supplements = repo_addid_dep(repo, 0, idsupp, 0);
- }
- return supplements;
-}
-
-Offset
-repo_fix_conflicts(Repo *repo, Offset conflicts)
-{
- char buf[1024], *p, *dep;
- Pool *pool = repo->pool;
- Id id;
- int i;
-
- if (!conflicts)
- return conflicts;
- for (i = conflicts; repo->idarraydata[i]; i++)
- {
- id = repo->idarraydata[i];
- if (ISRELDEP(id))
- continue;
- dep = (char *)pool_id2str(pool, id);
- if (!strncmp(dep, "otherproviders(", 15) && strlen(dep) < sizeof(buf) - 2)
- {
- strcpy(buf, dep + 15);
- if ((p = strchr(buf, ')')) != 0)
- *p = 0;
- id = pool_str2id(pool, buf, 1);
- id = pool_rel2id(pool, NAMESPACE_OTHERPROVIDERS, id, REL_NAMESPACE, 1);
- repo->idarraydata[i] = id;
- }
- }
- return conflicts;
-}
-
-/***********************************************************************/
-
-struct matchdata
-{
- Pool *pool;
- int flags;
- Datamatcher matcher;
- int stop;
- int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv);
- void *callback_data;
-};
-
-int
-repo_matchvalue(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
-{
- struct matchdata *md = cbdata;
-
- if (md->matcher.match)
- {
- const char *str;
- if (key->name == SOLVABLE_FILELIST && key->type == REPOKEY_TYPE_DIRSTRARRAY && (md->matcher.flags & SEARCH_FILES) != 0)
- if (!datamatcher_checkbasename(&md->matcher, kv->str))
- return 0;
- if (!(str = repodata_stringify(md->pool, data, key, kv, md->flags)))
- return 0;
- if (!datamatcher_match(&md->matcher, str))
- return 0;
- }
- md->stop = md->callback(md->callback_data, s, data, key, kv);
- return md->stop;
-}
-
-
-/* list of all keys we store in the solvable */
-/* also used in the dataiterator code in repodata.c */
-Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
- { SOLVABLE_NAME, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_ARCH, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_EVR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_VENDOR, REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_PROVIDES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_OBSOLETES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_CONFLICTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_REQUIRES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_RECOMMENDS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_SUGGESTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
- { SOLVABLE_ENHANCES, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
- { RPM_RPMDBID, REPOKEY_TYPE_NUM, 0, KEY_STORAGE_SOLVABLE },
-};
-
-static void
-domatch_idarray(Solvable *s, Id keyname, struct matchdata *md, Id *ida)
-{
- KeyValue kv;
- kv.entry = 0;
- kv.parent = 0;
- for (; *ida && !md->stop; ida++)
- {
- kv.id = *ida;
- kv.eof = ida[1] ? 0 : 1;
- repo_matchvalue(md, s, 0, repo_solvablekeys + (keyname - SOLVABLE_NAME), &kv);
- kv.entry++;
- }
-}
-
-static void
-repo_search_md(Repo *repo, Id p, Id keyname, struct matchdata *md)
-{
- KeyValue kv;
- Pool *pool = repo->pool;
- Repodata *data;
- int i, j, flags;
- Solvable *s;
-
- kv.parent = 0;
- md->stop = 0;
- if (!p)
- {
- for (p = repo->start, s = repo->pool->solvables + p; p < repo->end; p++, s++)
- {
- if (s->repo == repo)
- repo_search_md(repo, p, keyname, md);
- if (md->stop > SEARCH_NEXT_SOLVABLE)
- break;
- }
- return;
- }
- else if (p < 0)
- /* The callback only supports solvables, so we can't iterate over the
- extra things. */
- return;
- flags = md->flags;
- if (!(flags & SEARCH_NO_STORAGE_SOLVABLE))
- {
- s = pool->solvables + p;
- switch(keyname)
- {
- case 0:
- case SOLVABLE_NAME:
- if (s->name)
- {
- kv.id = s->name;
- repo_matchvalue(md, s, 0, repo_solvablekeys + 0, &kv);
- }
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_ARCH:
- if (s->arch)
- {
- kv.id = s->arch;
- repo_matchvalue(md, s, 0, repo_solvablekeys + 1, &kv);
- }
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_EVR:
- if (s->evr)
- {
- kv.id = s->evr;
- repo_matchvalue(md, s, 0, repo_solvablekeys + 2, &kv);
- }
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_VENDOR:
- if (s->vendor)
- {
- kv.id = s->vendor;
- repo_matchvalue(md, s, 0, repo_solvablekeys + 3, &kv);
- }
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_PROVIDES:
- if (s->provides)
- domatch_idarray(s, SOLVABLE_PROVIDES, md, repo->idarraydata + s->provides);
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_OBSOLETES:
- if (s->obsoletes)
- domatch_idarray(s, SOLVABLE_OBSOLETES, md, repo->idarraydata + s->obsoletes);
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_CONFLICTS:
- if (s->conflicts)
- domatch_idarray(s, SOLVABLE_CONFLICTS, md, repo->idarraydata + s->conflicts);
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_REQUIRES:
- if (s->requires)
- domatch_idarray(s, SOLVABLE_REQUIRES, md, repo->idarraydata + s->requires);
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_RECOMMENDS:
- if (s->recommends)
- domatch_idarray(s, SOLVABLE_RECOMMENDS, md, repo->idarraydata + s->recommends);
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_SUPPLEMENTS:
- if (s->supplements)
- domatch_idarray(s, SOLVABLE_SUPPLEMENTS, md, repo->idarraydata + s->supplements);
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_SUGGESTS:
- if (s->suggests)
- domatch_idarray(s, SOLVABLE_SUGGESTS, md, repo->idarraydata + s->suggests);
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case SOLVABLE_ENHANCES:
- if (s->enhances)
- domatch_idarray(s, SOLVABLE_ENHANCES, md, repo->idarraydata + s->enhances);
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- case RPM_RPMDBID:
- if (repo->rpmdbid)
- {
- kv.num = repo->rpmdbid[p - repo->start];
- kv.num2 = 0;
- repo_matchvalue(md, s, 0, repo_solvablekeys + (RPM_RPMDBID - SOLVABLE_NAME), &kv);
- }
- if (keyname || md->stop > SEARCH_NEXT_KEY)
- return;
- break;
- default:
- break;
- }
- }
-
- FOR_REPODATAS(repo, i, data)
- {
- if (p < data->start || p >= data->end)
- continue;
- if (keyname && !repodata_precheck_keyname(data, keyname))
- continue;
- if (keyname == SOLVABLE_FILELIST && !(md->flags & SEARCH_COMPLETE_FILELIST))
- {
- /* do not search filelist extensions */
- if (data->state != REPODATA_AVAILABLE)
- continue;
- for (j = 1; j < data->nkeys; j++)
- if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
- break;
- if (j == data->nkeys)
- continue;
- }
- if (data->state == REPODATA_STUB)
- {
- if (keyname)
- {
- for (j = 1; j < data->nkeys; j++)
- if (keyname == data->keys[j].name)
- break;
- if (j == data->nkeys)
- continue;
- }
- /* load it */
- if (data->loadcallback)
- data->loadcallback(data);
- else
- data->state = REPODATA_ERROR;
- }
- if (data->state == REPODATA_ERROR)
- continue;
- repodata_search(data, p, keyname, md->flags, repo_matchvalue, md);
- if (md->stop > SEARCH_NEXT_KEY)
- break;
- }
-}
-
-void
-repo_search(Repo *repo, Id p, Id keyname, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
-{
- struct matchdata md;
-
- if (repo->disabled && !(flags & SEARCH_DISABLED_REPOS))
- return;
- memset(&md, 0, sizeof(md));
- md.pool = repo->pool;
- md.flags = flags;
- md.callback = callback;
- md.callback_data = cbdata;
- if (match)
- datamatcher_init(&md.matcher, match, flags);
- repo_search_md(repo, p, keyname, &md);
- if (match)
- datamatcher_free(&md.matcher);
-}
-
-const char *
-repo_lookup_str(Repo *repo, Id entry, Id keyname)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i;
- const char *str;
-
- if (entry >= 0)
- {
- switch (keyname)
- {
- case SOLVABLE_NAME:
- return pool_id2str(pool, pool->solvables[entry].name);
- case SOLVABLE_ARCH:
- return pool_id2str(pool, pool->solvables[entry].arch);
- case SOLVABLE_EVR:
- return pool_id2str(pool, pool->solvables[entry].evr);
- case SOLVABLE_VENDOR:
- return pool_id2str(pool, pool->solvables[entry].vendor);
- }
- }
- else if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
- return repodata_lookup_str(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname);
- FOR_REPODATAS(repo, i, data)
- {
- if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
- continue;
- if (!repodata_precheck_keyname(data, keyname))
- continue;
- str = repodata_lookup_str(data, entry, keyname);
- if (str)
- return str;
- if (repodata_lookup_type(data, entry, keyname))
- return 0;
- }
- return 0;
-}
-
-
-unsigned long long
-repo_lookup_num(Repo *repo, Id entry, Id keyname, unsigned long long notfound)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i;
- unsigned long long value;
-
- if (entry >= 0)
- {
- if (keyname == RPM_RPMDBID)
- {
- if (repo->rpmdbid && entry >= repo->start && entry < repo->end)
- return repo->rpmdbid[entry - repo->start];
- return notfound;
- }
- }
- else if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
- return repodata_lookup_num(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname, &value) ? value : notfound;
- FOR_REPODATAS(repo, i, data)
- {
- if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
- continue;
- if (!repodata_precheck_keyname(data, keyname))
- continue;
- if (repodata_lookup_num(data, entry, keyname, &value))
- return value;
- if (repodata_lookup_type(data, entry, keyname))
- return notfound;
- }
- return notfound;
-}
-
-Id
-repo_lookup_id(Repo *repo, Id entry, Id keyname)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i;
- Id id;
-
- if (entry >= 0)
- {
- switch (keyname)
- {
- case SOLVABLE_NAME:
- return repo->pool->solvables[entry].name;
- case SOLVABLE_ARCH:
- return repo->pool->solvables[entry].arch;
- case SOLVABLE_EVR:
- return repo->pool->solvables[entry].evr;
- case SOLVABLE_VENDOR:
- return repo->pool->solvables[entry].vendor;
- }
- }
- else if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
- {
- Repodata *data = pool->pos.repo->repodata + pool->pos.repodataid;
- Id id = repodata_lookup_id(data, entry, keyname);
- return data->localpool ? repodata_globalize_id(data, id, 1) : id;
- }
- FOR_REPODATAS(repo, i, data)
- {
- if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
- continue;
- if (!repodata_precheck_keyname(data, keyname))
- continue;
- id = repodata_lookup_id(data, entry, keyname);
- if (id)
- return data->localpool ? repodata_globalize_id(data, id, 1) : id;
- if (repodata_lookup_type(data, entry, keyname))
- return 0;
- }
- return 0;
-}
-
-static int
-lookup_idarray_solvable(Repo *repo, Offset off, Queue *q)
-{
- Id *p;
-
- queue_empty(q);
- if (off)
- for (p = repo->idarraydata + off; *p; p++)
- queue_push(q, *p);
- return 1;
-}
-
-int
-repo_lookup_idarray(Repo *repo, Id entry, Id keyname, Queue *q)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i;
- if (entry >= 0)
- {
- switch (keyname)
- {
- case SOLVABLE_PROVIDES:
- return lookup_idarray_solvable(repo, repo->pool->solvables[entry].provides, q);
- case SOLVABLE_OBSOLETES:
- return lookup_idarray_solvable(repo, repo->pool->solvables[entry].obsoletes, q);
- case SOLVABLE_CONFLICTS:
- return lookup_idarray_solvable(repo, repo->pool->solvables[entry].conflicts, q);
- case SOLVABLE_REQUIRES:
- return lookup_idarray_solvable(repo, repo->pool->solvables[entry].requires, q);
- case SOLVABLE_RECOMMENDS:
- return lookup_idarray_solvable(repo, repo->pool->solvables[entry].recommends, q);
- case SOLVABLE_SUGGESTS:
- return lookup_idarray_solvable(repo, repo->pool->solvables[entry].suggests, q);
- case SOLVABLE_SUPPLEMENTS:
- return lookup_idarray_solvable(repo, repo->pool->solvables[entry].supplements, q);
- case SOLVABLE_ENHANCES:
- return lookup_idarray_solvable(repo, repo->pool->solvables[entry].enhances, q);
- }
- }
- else if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
- {
- Repodata *data = pool->pos.repo->repodata + pool->pos.repodataid;
- if (repodata_lookup_idarray(data, entry, keyname, q))
- {
- if (data->localpool)
- {
- for (i = 0; i < q->count; i++)
- q->elements[i] = repodata_globalize_id(data, q->elements[i], 1);
- }
- return 1;
- }
- }
- FOR_REPODATAS(repo, i, data)
- {
- if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
- continue;
- if (!repodata_precheck_keyname(data, keyname))
- continue;
- if (repodata_lookup_idarray(data, entry, keyname, q))
- {
- if (data->localpool)
- {
- for (i = 0; i < q->count; i++)
- q->elements[i] = repodata_globalize_id(data, q->elements[i], 1);
- }
- return 1;
- }
- if (repodata_lookup_type(data, entry, keyname))
- break;
- }
- queue_empty(q);
- return 0;
-}
-
-int
-repo_lookup_deparray(Repo *repo, Id entry, Id keyname, Queue *q, Id marker)
-{
- int r = repo_lookup_idarray(repo, entry, keyname, q);
- if (!r)
- return 0;
- if (marker == -1 || marker == 1)
- marker = solv_depmarker(keyname, marker);
- if (marker && q->count)
- {
- int i;
- if (marker < 0)
- {
- marker = -marker;
- for (i = 0; i < q->count; i++)
- if (q->elements[i] == marker)
- {
- queue_truncate(q, i);
- return r;
- }
- }
- else
- {
- for (i = 0; i < q->count; i++)
- if (q->elements[i] == marker)
- {
- queue_deleten(q, 0, i + 1);
- return r;
- }
- queue_empty(q);
- }
- }
- return r;
-}
-
-const unsigned char *
-repo_lookup_bin_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i;
- const unsigned char *chk;
-
- if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
- return repodata_lookup_bin_checksum(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname, typep);
- FOR_REPODATAS(repo, i, data)
- {
- if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
- continue;
- if (!repodata_precheck_keyname(data, keyname))
- continue;
- chk = repodata_lookup_bin_checksum(data, entry, keyname, typep);
- if (chk)
- return chk;
- if (repodata_lookup_type(data, entry, keyname))
- return 0;
- }
- *typep = 0;
- return 0;
-}
-
-const char *
-repo_lookup_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
-{
- const unsigned char *chk = repo_lookup_bin_checksum(repo, entry, keyname, typep);
- return chk ? pool_bin2hex(repo->pool, chk, solv_chksum_len(*typep)) : 0;
-}
-
-int
-repo_lookup_void(Repo *repo, Id entry, Id keyname)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i;
- Id type;
-
- if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
- return repodata_lookup_void(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname);
- FOR_REPODATAS(repo, i, data)
- {
- if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
- continue;
- if (!repodata_precheck_keyname(data, keyname))
- continue;
- type = repodata_lookup_type(data, entry, keyname);
- if (type)
- return type == REPOKEY_TYPE_VOID;
- }
- return 0;
-}
-
-Id
-repo_lookup_type(Repo *repo, Id entry, Id keyname)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i;
- Id type;
-
- if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
- return repodata_lookup_type(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname);
- FOR_REPODATAS(repo, i, data)
- {
- if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
- continue;
- if (!repodata_precheck_keyname(data, keyname))
- continue;
- type = repodata_lookup_type(data, entry, keyname);
- if (type)
- return type == REPOKEY_TYPE_DELETED ? 0 : type;
- }
- return 0;
-}
-
-const void *
-repo_lookup_binary(Repo *repo, Id entry, Id keyname, int *lenp)
-{
- Pool *pool = repo->pool;
- Repodata *data;
- int i;
- const void *bin;
-
- if (entry == SOLVID_POS && pool->pos.repo == repo && pool->pos.repodataid)
- return repodata_lookup_binary(pool->pos.repo->repodata + pool->pos.repodataid, entry, keyname, lenp);
- FOR_REPODATAS(repo, i, data)
- {
- if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
- continue;
- if (!repodata_precheck_keyname(data, keyname))
- continue;
- bin = repodata_lookup_binary(data, entry, keyname, lenp);
- if (bin)
- return bin;
- }
- *lenp = 0;
- return 0;
-}
-
-/***********************************************************************/
-
-Repodata *
-repo_add_repodata(Repo *repo, int flags)
-{
- Repodata *data;
- int i;
- if ((flags & REPO_USE_LOADING) != 0)
- {
- for (i = repo->nrepodata - 1; i > 0; i--)
- if (repo->repodata[i].state == REPODATA_LOADING)
- {
- Repodata *data = repo->repodata + i;
- /* re-init */
- /* hack: we mis-use REPO_REUSE_REPODATA here */
- if (!(flags & REPO_REUSE_REPODATA))
- repodata_empty(data, (flags & REPO_LOCALPOOL) ? 1 : 0);
- return data;
- }
- return 0; /* must not create a new repodata! */
- }
- if ((flags & REPO_REUSE_REPODATA) != 0)
- {
- for (i = repo->nrepodata - 1; i > 0; i--)
- if (repo->repodata[i].state != REPODATA_STUB)
- return repo->repodata + i;
- }
- if (!repo->nrepodata)
- {
- repo->nrepodata = 2; /* start with id 1 */
- repo->repodata = solv_calloc(repo->nrepodata, sizeof(*data));
- }
- else
- {
- repo->nrepodata++;
- repo->repodata = solv_realloc2(repo->repodata, repo->nrepodata, sizeof(*data));
- }
- data = repo->repodata + repo->nrepodata - 1;
- repodata_initdata(data, repo, (flags & REPO_LOCALPOOL) ? 1 : 0);
- return data;
-}
-
-Repodata *
-repo_id2repodata(Repo *repo, Id id)
-{
- return id ? repo->repodata + id : 0;
-}
-
-Repodata *
-repo_last_repodata(Repo *repo)
-{
- int i;
- for (i = repo->nrepodata - 1; i > 0; i--)
- if (repo->repodata[i].state != REPODATA_STUB)
- return repo->repodata + i;
- return repo_add_repodata(repo, 0);
-}
-
-void
-repo_set_id(Repo *repo, Id p, Id keyname, Id id)
-{
- Repodata *data;
- if (p >= 0)
- {
- switch (keyname)
- {
- case SOLVABLE_NAME:
- repo->pool->solvables[p].name = id;
- return;
- case SOLVABLE_ARCH:
- repo->pool->solvables[p].arch = id;
- return;
- case SOLVABLE_EVR:
- repo->pool->solvables[p].evr = id;
- return;
- case SOLVABLE_VENDOR:
- repo->pool->solvables[p].vendor = id;
- return;
- }
- }
- data = repo_last_repodata(repo);
- if (data->localpool)
- id = repodata_localize_id(data, id, 1);
- repodata_set_id(data, p, keyname, id);
-}
-
-void
-repo_set_num(Repo *repo, Id p, Id keyname, unsigned long long num)
-{
- Repodata *data;
- if (p >= 0)
- {
- if (keyname == RPM_RPMDBID)
- {
- if (!repo->rpmdbid)
- repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
- repo->rpmdbid[p - repo->start] = num;
- return;
- }
- }
- data = repo_last_repodata(repo);
- repodata_set_num(data, p, keyname, num);
-}
-
-void
-repo_set_str(Repo *repo, Id p, Id keyname, const char *str)
-{
- Repodata *data;
- if (p >= 0)
- {
- switch (keyname)
- {
- case SOLVABLE_NAME:
- case SOLVABLE_ARCH:
- case SOLVABLE_EVR:
- case SOLVABLE_VENDOR:
- repo_set_id(repo, p, keyname, pool_str2id(repo->pool, str, 1));
- return;
- }
- }
- data = repo_last_repodata(repo);
- repodata_set_str(data, p, keyname, str);
-}
-
-void
-repo_set_poolstr(Repo *repo, Id p, Id keyname, const char *str)
-{
- Repodata *data;
- if (p >= 0)
- {
- switch (keyname)
- {
- case SOLVABLE_NAME:
- case SOLVABLE_ARCH:
- case SOLVABLE_EVR:
- case SOLVABLE_VENDOR:
- repo_set_id(repo, p, keyname, pool_str2id(repo->pool, str, 1));
- return;
- }
- }
- data = repo_last_repodata(repo);
- repodata_set_poolstr(data, p, keyname, str);
-}
-
-void
-repo_add_poolstr_array(Repo *repo, Id p, Id keyname, const char *str)
-{
- Repodata *data = repo_last_repodata(repo);
- repodata_add_poolstr_array(data, p, keyname, str);
-}
-
-void
-repo_add_deparray(Repo *repo, Id p, Id keyname, Id dep, Id marker)
-{
- Repodata *data;
- if (marker == -1 || marker == 1)
- marker = solv_depmarker(keyname, marker);
- if (p >= 0)
- {
- Solvable *s = repo->pool->solvables + p;
- switch (keyname)
- {
- case SOLVABLE_PROVIDES:
- s->provides = repo_addid_dep(repo, s->provides, dep, marker);
- return;
- case SOLVABLE_OBSOLETES:
- s->obsoletes = repo_addid_dep(repo, s->obsoletes, dep, marker);
- return;
- case SOLVABLE_CONFLICTS:
- s->conflicts = repo_addid_dep(repo, s->conflicts, dep, marker);
- return;
- case SOLVABLE_REQUIRES:
- s->requires = repo_addid_dep(repo, s->requires, dep, marker);
- return;
- case SOLVABLE_RECOMMENDS:
- s->recommends = repo_addid_dep(repo, s->recommends, dep, marker);
- return;
- case SOLVABLE_SUGGESTS:
- s->suggests = repo_addid_dep(repo, s->suggests, dep, marker);
- return;
- case SOLVABLE_SUPPLEMENTS:
- s->supplements = repo_addid_dep(repo, s->supplements, dep, marker);
- return;
- case SOLVABLE_ENHANCES:
- s->enhances = repo_addid_dep(repo, s->enhances, dep, marker);
- return;
- }
- }
- data = repo_last_repodata(repo);
- repodata_add_idarray(data, p, keyname, dep);
-}
-
-void
-repo_add_idarray(Repo *repo, Id p, Id keyname, Id id)
-{
- repo_add_deparray(repo, p, keyname, id, 0);
-}
-
-static Offset
-repo_set_idarray_solvable(Repo *repo, Queue *q)
-{
- Offset o = 0;
- int i;
- for (i = 0; i < q->count; i++)
- repo_addid_dep(repo, o, q->elements[i], 0);
- return o;
-}
-
-void
-repo_set_deparray(Repo *repo, Id p, Id keyname, Queue *q, Id marker)
-{
- Repodata *data;
- if (marker == -1 || marker == 1)
- marker = solv_depmarker(keyname, marker);
- if (marker)
- {
- /* complex case, splice old and new arrays */
- int i;
- Queue q2;
- queue_init(&q2);
- repo_lookup_deparray(repo, p, keyname, &q2, -marker);
- if (marker > 0)
- {
- if (q->count)
- {
- queue_push(&q2, marker);
- for (i = 0; i < q->count; i++)
- queue_push(&q2, q->elements[i]);
- }
- }
- else
- {
- if (q2.count)
- queue_insert(&q2, 0, -marker);
- queue_insertn(&q2, 0, q->count, q->elements);
- }
- repo_set_deparray(repo, p, keyname, &q2, 0);
- queue_free(&q2);
- return;
- }
- if (p >= 0)
- {
- Solvable *s = repo->pool->solvables + p;
- switch (keyname)
- {
- case SOLVABLE_PROVIDES:
- s->provides = repo_set_idarray_solvable(repo, q);
- return;
- case SOLVABLE_OBSOLETES:
- s->obsoletes = repo_set_idarray_solvable(repo, q);
- return;
- case SOLVABLE_CONFLICTS:
- s->conflicts = repo_set_idarray_solvable(repo, q);
- return;
- case SOLVABLE_REQUIRES:
- s->requires = repo_set_idarray_solvable(repo, q);
- return;
- case SOLVABLE_RECOMMENDS:
- s->recommends = repo_set_idarray_solvable(repo, q);
- return;
- case SOLVABLE_SUGGESTS:
- s->suggests = repo_set_idarray_solvable(repo, q);
- return;
- case SOLVABLE_SUPPLEMENTS:
- s->supplements = repo_set_idarray_solvable(repo, q);
- return;
- case SOLVABLE_ENHANCES:
- s->enhances = repo_set_idarray_solvable(repo, q);
- return;
- }
- }
- data = repo_last_repodata(repo);
- repodata_set_idarray(data, p, keyname, q);
-}
-
-void
-repo_set_idarray(Repo *repo, Id p, Id keyname, Queue *q)
-{
- repo_set_deparray(repo, p, keyname, q, 0);
-}
-
-void
-repo_unset(Repo *repo, Id p, Id keyname)
-{
- Repodata *data;
- if (p >= 0)
- {
- Solvable *s = repo->pool->solvables + p;
- switch (keyname)
- {
- case SOLVABLE_NAME:
- s->name = 0;
- return;
- case SOLVABLE_ARCH:
- s->arch = 0;
- return;
- case SOLVABLE_EVR:
- s->evr = 0;
- return;
- case SOLVABLE_VENDOR:
- s->vendor = 0;
- return;
- case RPM_RPMDBID:
- if (repo->rpmdbid)
- repo->rpmdbid[p - repo->start] = 0;
- return;
- case SOLVABLE_PROVIDES:
- s->provides = 0;
- return;
- case SOLVABLE_OBSOLETES:
- s->obsoletes = 0;
- return;
- case SOLVABLE_CONFLICTS:
- s->conflicts = 0;
- return;
- case SOLVABLE_REQUIRES:
- s->requires = 0;
- return;
- case SOLVABLE_RECOMMENDS:
- s->recommends = 0;
- return;
- case SOLVABLE_SUGGESTS:
- s->suggests = 0;
- return;
- case SOLVABLE_SUPPLEMENTS:
- s->supplements = 0;
- case SOLVABLE_ENHANCES:
- s->enhances = 0;
- return;
- default:
- break;
- }
- }
- data = repo_last_repodata(repo);
- repodata_unset(data, p, keyname);
-}
-
-void
-repo_internalize(Repo *repo)
-{
- int i;
- Repodata *data;
-
- FOR_REPODATAS(repo, i, data)
- if (data->attrs || data->xattrs)
- repodata_internalize(data);
-}
-
-void
-repo_disable_paging(Repo *repo)
-{
- int i;
- Repodata *data;
-
- FOR_REPODATAS(repo, i, data)
- repodata_disable_paging(data);
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo.h
- *
- */
-
-#ifndef LIBSOLV_REPO_H
-#define LIBSOLV_REPO_H
-
-#include "pooltypes.h"
-#include "pool.h"
-#include "repodata.h"
-#include "dataiterator.h"
-#include "hash.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct _Repo {
- const char *name; /* name pointer */
- Id repoid; /* our id */
- void *appdata; /* application private pointer */
-
- Pool *pool; /* pool containing this repo */
-
- int start; /* start of this repo solvables within pool->solvables */
- int end; /* last solvable + 1 of this repo */
- int nsolvables; /* number of solvables repo is contributing to pool */
-
- int disabled; /* ignore the solvables? */
- int priority; /* priority of this repo */
- int subpriority; /* sub-priority of this repo, used just for sorting, not pruning */
-
- Id *idarraydata; /* array of metadata Ids, solvable dependencies are offsets into this array */
- int idarraysize;
-
- int nrepodata; /* number of our stores.. */
-
- Id *rpmdbid; /* solvable side data: rpm database id */
-
-#ifdef LIBSOLV_INTERNAL
- Repodata *repodata; /* our stores for non-solvable related data */
- Offset lastoff; /* start of last array in idarraydata */
-
- Hashtable lastidhash; /* hash to speed up repo_addid_dep */
- Hashval lastidhash_mask;
- int lastidhash_idarraysize;
- int lastmarker;
- Offset lastmarkerpos;
-#endif /* LIBSOLV_INTERNAL */
-} Repo;
-
-extern Repo *repo_create(Pool *pool, const char *name);
-extern void repo_free(Repo *repo, int reuseids);
-extern void repo_empty(Repo *repo, int reuseids);
-extern void repo_freedata(Repo *repo);
-extern Id repo_add_solvable(Repo *repo);
-extern Id repo_add_solvable_block(Repo *repo, int count);
-extern void repo_free_solvable(Repo *repo, Id p, int reuseids);
-extern void repo_free_solvable_block(Repo *repo, Id start, int count, int reuseids);
-extern void *repo_sidedata_create(Repo *repo, size_t size);
-extern void *repo_sidedata_extend(Repo *repo, void *b, size_t size, Id p, int count);
-extern Id repo_add_solvable_block_before(Repo *repo, int count, Repo *beforerepo);
-
-extern Offset repo_addid(Repo *repo, Offset olddeps, Id id);
-extern Offset repo_addid_dep(Repo *repo, Offset olddeps, Id id, Id marker);
-extern Offset repo_reserve_ids(Repo *repo, Offset olddeps, int num);
-extern Offset repo_fix_supplements(Repo *repo, Offset provides, Offset supplements, Offset freshens);
-extern Offset repo_fix_conflicts(Repo *repo, Offset conflicts);
-
-static inline const char *repo_name(const Repo *repo)
-{
- return repo->name;
-}
-
-/* those two functions are here because they need the Repo definition */
-
-static inline Repo *pool_id2repo(Pool *pool, Id repoid)
-{
- return repoid < pool->nrepos ? pool->repos[repoid] : 0;
-}
-
-static inline int pool_disabled_solvable(const Pool *pool, Solvable *s)
-{
- if (s->repo && s->repo->disabled)
- return 1;
- if (pool->considered)
- {
- Id id = s - pool->solvables;
- if (!MAPTST(pool->considered, id))
- return 1;
- }
- return 0;
-}
-
-static inline int pool_installable(const Pool *pool, Solvable *s)
-{
- if (!s->arch || s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
- return 0;
- if (s->repo && s->repo->disabled)
- return 0;
- if (pool->id2arch && (s->arch > pool->lastarch || !pool->id2arch[s->arch]))
- return 0;
- if (pool->considered)
- {
- Id id = s - pool->solvables;
- if (!MAPTST(pool->considered, id))
- return 0;
- }
- return 1;
-}
-
-/* search callback values */
-#define SEARCH_NEXT_KEY 1
-#define SEARCH_NEXT_SOLVABLE 2
-#define SEARCH_STOP 3
-#define SEARCH_ENTERSUB -1
-
-/* standard flags used in the repo_add functions */
-#define REPO_REUSE_REPODATA (1 << 0)
-#define REPO_NO_INTERNALIZE (1 << 1)
-#define REPO_LOCALPOOL (1 << 2)
-#define REPO_USE_LOADING (1 << 3)
-#define REPO_EXTEND_SOLVABLES (1 << 4)
-#define REPO_USE_ROOTDIR (1 << 5)
-#define REPO_NO_LOCATION (1 << 6)
-
-Repodata *repo_add_repodata(Repo *repo, int flags);
-Repodata *repo_id2repodata(Repo *repo, Id id);
-Repodata *repo_last_repodata(Repo *repo);
-
-void repo_search(Repo *repo, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata);
-
-/* returns the string value of the attribute, or NULL if not found */
-Id repo_lookup_type(Repo *repo, Id entry, Id keyname);
-const char *repo_lookup_str(Repo *repo, Id entry, Id keyname);
-/* returns the integer value of the attribute, or notfound if not found */
-unsigned long long repo_lookup_num(Repo *repo, Id entry, Id keyname, unsigned long long notfound);
-Id repo_lookup_id(Repo *repo, Id entry, Id keyname);
-int repo_lookup_idarray(Repo *repo, Id entry, Id keyname, Queue *q);
-int repo_lookup_deparray(Repo *repo, Id entry, Id keyname, Queue *q, Id marker);
-int repo_lookup_void(Repo *repo, Id entry, Id keyname);
-const char *repo_lookup_checksum(Repo *repo, Id entry, Id keyname, Id *typep);
-const unsigned char *repo_lookup_bin_checksum(Repo *repo, Id entry, Id keyname, Id *typep);
-const void *repo_lookup_binary(Repo *repo, Id entry, Id keyname, int *lenp);
-Id solv_depmarker(Id keyname, Id marker);
-
-void repo_set_id(Repo *repo, Id p, Id keyname, Id id);
-void repo_set_num(Repo *repo, Id p, Id keyname, unsigned long long num);
-void repo_set_str(Repo *repo, Id p, Id keyname, const char *str);
-void repo_set_poolstr(Repo *repo, Id p, Id keyname, const char *str);
-void repo_add_poolstr_array(Repo *repo, Id p, Id keyname, const char *str);
-void repo_add_idarray(Repo *repo, Id p, Id keyname, Id id);
-void repo_add_deparray(Repo *repo, Id p, Id keyname, Id dep, Id marker);
-void repo_set_idarray(Repo *repo, Id p, Id keyname, Queue *q);
-void repo_set_deparray(Repo *repo, Id p, Id keyname, Queue *q, Id marker);
-void repo_unset(Repo *repo, Id p, Id keyname);
-
-void repo_internalize(Repo *repo);
-void repo_disable_paging(Repo *repo);
-
-/* iterator macros */
-#define FOR_REPO_SOLVABLES(r, p, s) \
- for (p = (r)->start, s = (r)->pool->solvables + p; p < (r)->end; p++, s = (r)->pool->solvables + p) \
- if (s->repo != (r)) \
- continue; \
- else
-
-#ifdef LIBSOLV_INTERNAL
-#define FOR_REPODATAS(repo, rdid, data) \
- for (rdid = 1, data = repo->repodata + rdid; rdid < repo->nrepodata; rdid++, data++)
-#else
-#define FOR_REPODATAS(repo, rdid, data) \
- for (rdid = 1; rdid < repo->nrepodata && (data = repo_id2repodata(repo, rdid)); rdid++)
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_REPO_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo_solv.c
- *
- * Add a repo in solv format
- *
- */
-
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "repo_solv.h"
-#include "util.h"
-
-#include "repopack.h"
-#include "repopage.h"
-
-#include "poolid_private.h" /* WHATPROVIDES_BLOCK */
-
-#define INTERESTED_START SOLVABLE_NAME
-#define INTERESTED_END SOLVABLE_ENHANCES
-
-#define SOLV_ERROR_NOT_SOLV 1
-#define SOLV_ERROR_UNSUPPORTED 2
-#define SOLV_ERROR_EOF 3
-#define SOLV_ERROR_ID_RANGE 4
-#define SOLV_ERROR_OVERFLOW 5
-#define SOLV_ERROR_CORRUPT 6
-
-
-
-/*******************************************************************************
- * functions to extract data from a file handle
- */
-
-/*
- * read u32
- */
-
-static unsigned int
-read_u32(Repodata *data)
-{
- int c, i;
- unsigned int x = 0;
-
- if (data->error)
- return 0;
- for (i = 0; i < 4; i++)
- {
- c = getc(data->fp);
- if (c == EOF)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_EOF, "unexpected EOF");
- return 0;
- }
- x = (x << 8) | c;
- }
- return x;
-}
-
-
-/*
- * read u8
- */
-
-static unsigned int
-read_u8(Repodata *data)
-{
- int c;
-
- if (data->error)
- return 0;
- c = getc(data->fp);
- if (c == EOF)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_EOF, "unexpected EOF");
- return 0;
- }
- return c;
-}
-
-
-/*
- * read Id
- */
-
-static Id
-read_id(Repodata *data, Id max)
-{
- unsigned int x = 0;
- int c, i;
-
- if (data->error)
- return 0;
- for (i = 0; i < 5; i++)
- {
- c = getc(data->fp);
- if (c == EOF)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_EOF, "unexpected EOF");
- return 0;
- }
- if (!(c & 128))
- {
- x = (x << 7) | c;
- if (max && x >= (unsigned int)max)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "read_id: id too large (%u/%u)", x, max);
- return 0;
- }
- return x;
- }
- x = (x << 7) ^ c ^ 128;
- }
- data->error = pool_error(data->repo->pool, SOLV_ERROR_CORRUPT, "read_id: id too long");
- return 0;
-}
-
-
-static Id *
-read_idarray(Repodata *data, Id max, Id *map, Id *store, Id *end)
-{
- unsigned int x = 0;
- int c;
-
- if (data->error)
- return 0;
- for (;;)
- {
- c = getc(data->fp);
- if (c == EOF)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_EOF, "unexpected EOF");
- return 0;
- }
- if ((c & 128) != 0)
- {
- x = (x << 7) ^ c ^ 128;
- continue;
- }
- x = (x << 6) | (c & 63);
- if (max && x >= (unsigned int)max)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "read_idarray: id too large (%u/%u)", x, max);
- return 0;
- }
- if (map)
- x = map[x];
- if (store == end)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_OVERFLOW, "read_idarray: array overflow");
- return 0;
- }
- *store++ = x;
- if ((c & 64) == 0)
- {
- if (x == 0) /* already have trailing zero? */
- return store;
- if (store == end)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_OVERFLOW, "read_idarray: array overflow");
- return 0;
- }
- *store++ = 0;
- return store;
- }
- x = 0;
- }
-}
-
-
-/*******************************************************************************
- * functions to extract data from memory
- */
-
-/*
- * read array of Ids
- */
-
-static inline unsigned char *
-data_read_id_max(unsigned char *dp, Id *ret, Id *map, int max, Repodata *data)
-{
- Id x;
- dp = data_read_id(dp, &x);
- if (x < 0 || (max && x >= max))
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "data_read_id_max: id too large (%u/%u)", x, max);
- x = 0;
- }
- *ret = map ? map[x] : x;
- return dp;
-}
-
-static unsigned char *
-data_read_idarray(unsigned char *dp, Id **storep, Id *map, int max, Repodata *data)
-{
- Id *store = *storep;
- unsigned int x = 0;
- int c;
-
- for (;;)
- {
- c = *dp++;
- if ((c & 128) != 0)
- {
- x = (x << 7) ^ c ^ 128;
- continue;
- }
- x = (x << 6) | (c & 63);
- if (max && x >= (unsigned int)max)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "data_read_idarray: id too large (%u/%u)", x, max);
- data->error = SOLV_ERROR_ID_RANGE;
- break;
- }
- *store++ = x;
- if ((c & 64) == 0)
- break;
- x = 0;
- }
- *store++ = 0;
- *storep = store;
- return dp;
-}
-
-static unsigned char *
-data_read_rel_idarray(unsigned char *dp, Id **storep, Id *map, int max, Repodata *data, Id marker)
-{
- Id *store = *storep;
- Id old = 0;
- unsigned int x = 0;
- int c;
-
- for (;;)
- {
- c = *dp++;
- if ((c & 128) != 0)
- {
- x = (x << 7) ^ c ^ 128;
- continue;
- }
- x = (x << 6) | (c & 63);
- if (x == 0)
- {
- if (!(c & 64))
- break;
- if (marker)
- *store++ = marker;
- old = 0;
- continue;
- }
- x = old + (x - 1);
- old = x;
- if (max && x >= (unsigned int)max)
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "data_read_rel_idarray: id too large (%u/%u)", x, max);
- break;
- }
- *store++ = map ? map[x] : x;
- if (!(c & 64))
- break;
- x = 0;
- }
- *store++ = 0;
- *storep = store;
- return dp;
-}
-
-
-
-
-/*******************************************************************************
- * functions to add data to our incore memory space
- */
-
-#define INCORE_ADD_CHUNK 8192
-#define DATA_READ_CHUNK 8192
-
-static void
-incore_add_id(Repodata *data, Id sx)
-{
- unsigned int x = (unsigned int)sx;
- unsigned char *dp;
- /* make sure we have at least 5 bytes free */
- if (data->incoredatafree < 5)
- {
- data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + INCORE_ADD_CHUNK);
- data->incoredatafree = INCORE_ADD_CHUNK;
- }
- dp = data->incoredata + data->incoredatalen;
- if (x >= (1 << 14))
- {
- if (x >= (1 << 28))
- *dp++ = (x >> 28) | 128;
- if (x >= (1 << 21))
- *dp++ = (x >> 21) | 128;
- *dp++ = (x >> 14) | 128;
- }
- if (x >= (1 << 7))
- *dp++ = (x >> 7) | 128;
- *dp++ = x & 127;
- data->incoredatafree -= dp - (data->incoredata + data->incoredatalen);
- data->incoredatalen = dp - data->incoredata;
-}
-
-static void
-incore_add_sizek(Repodata *data, unsigned int sx)
-{
- if (sx < (1 << 22))
- incore_add_id(data, (Id)(sx << 10));
- else
- {
- if ((sx >> 25) != 0)
- {
- incore_add_id(data, (Id)(sx >> 25));
- data->incoredata[data->incoredatalen - 1] |= 128;
- }
- incore_add_id(data, (Id)((sx << 10) | 0x80000000));
- data->incoredata[data->incoredatalen - 5] = (sx >> 18) | 128;
- }
-}
-
-static void
-incore_add_ideof(Repodata *data, Id sx, int eof)
-{
- unsigned int x = (unsigned int)sx;
- unsigned char *dp;
- /* make sure we have at least 5 bytes free */
- if (data->incoredatafree < 5)
- {
- data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + INCORE_ADD_CHUNK);
- data->incoredatafree = INCORE_ADD_CHUNK;
- }
- dp = data->incoredata + data->incoredatalen;
- if (x >= (1 << 13))
- {
- if (x >= (1 << 27))
- *dp++ = (x >> 27) | 128;
- if (x >= (1 << 20))
- *dp++ = (x >> 20) | 128;
- *dp++ = (x >> 13) | 128;
- }
- if (x >= (1 << 6))
- *dp++ = (x >> 6) | 128;
- *dp++ = eof ? (x & 63) : (x & 63) | 64;
- data->incoredatafree -= dp - (data->incoredata + data->incoredatalen);
- data->incoredatalen = dp - data->incoredata;
-}
-
-static void
-incore_add_blob(Repodata *data, unsigned char *buf, int len)
-{
- if (data->incoredatafree < (unsigned int)len)
- {
- data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + INCORE_ADD_CHUNK + len);
- data->incoredatafree = INCORE_ADD_CHUNK + len;
- }
- memcpy(data->incoredata + data->incoredatalen, buf, len);
- data->incoredatafree -= len;
- data->incoredatalen += len;
-}
-
-static void
-incore_map_idarray(Repodata *data, unsigned char *dp, Id *map, Id max)
-{
- /* We have to map the IDs, which might also change
- the necessary number of bytes, so we can't just copy
- over the blob and adjust it. */
- for (;;)
- {
- Id id;
- int eof;
- dp = data_read_ideof(dp, &id, &eof);
- if (id < 0 || (max && id >= max))
- {
- data->error = pool_error(data->repo->pool, SOLV_ERROR_ID_RANGE, "incore_map_idarray: id too large (%u/%u)", id, max);
- break;
- }
- id = map[id];
- incore_add_ideof(data, id, eof);
- if (eof)
- break;
- }
-}
-
-#if 0
-static void
-incore_add_u32(Repodata *data, unsigned int x)
-{
- unsigned char *dp;
- /* make sure we have at least 4 bytes free */
- if (data->incoredatafree < 4)
- {
- data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + INCORE_ADD_CHUNK);
- data->incoredatafree = INCORE_ADD_CHUNK;
- }
- dp = data->incoredata + data->incoredatalen;
- *dp++ = x >> 24;
- *dp++ = x >> 16;
- *dp++ = x >> 8;
- *dp++ = x;
- data->incoredatafree -= 4;
- data->incoredatalen += 4;
-}
-
-static void
-incore_add_u8(Repodata *data, unsigned int x)
-{
- unsigned char *dp;
- /* make sure we have at least 1 byte free */
- if (data->incoredatafree < 1)
- {
- data->incoredata = solv_realloc(data->incoredata, data->incoredatalen + 1024);
- data->incoredatafree = 1024;
- }
- dp = data->incoredata + data->incoredatalen;
- *dp++ = x;
- data->incoredatafree--;
- data->incoredatalen++;
-}
-#endif
-
-
-/*******************************************************************************
- * our main function
- */
-
-/*
- * read repo from .solv file and add it to pool
- */
-
-int
-repo_add_solv(Repo *repo, FILE *fp, int flags)
-{
- Pool *pool = repo->pool;
- int i, l;
- int numid, numrel, numdir, numsolv;
- int numkeys, numschemata;
-
- Offset sizeid;
- Offset *str; /* map Id -> Offset into string space */
- char *strsp; /* repo string space */
- char *sp; /* pointer into string space */
- Id *idmap; /* map of repo Ids to pool Ids */
- Id id, type;
- Hashval hashmask, h, hh;
- Hashtable hashtbl;
- Id name, evr, did;
- int relflags;
- Reldep *ran;
- unsigned int size_idarray;
- Id *idarraydatap, *idarraydataend;
- Offset ido;
- Solvable *s;
- unsigned int solvflags;
- unsigned int solvversion;
- Repokey *keys;
- Id *schemadata, *schemadatap, *schemadataend;
- Id *schemata, key, *keyp;
- int nentries;
- int have_incoredata;
- int maxsize, allsize;
- unsigned char *buf, *bufend, *dp, *dps;
- Id stack[3 * 5];
- int keydepth;
- int needchunk; /* need a new chunk of data */
- unsigned int now;
- int oldnstrings = pool->ss.nstrings;
- int oldnrels = pool->nrels;
-
- struct _Stringpool *spool;
-
- Repodata *parent = 0;
- Repodata data;
-
- int extendstart = 0, extendend = 0; /* set in case we're extending */
-
- now = solv_timems(0);
-
- if ((flags & REPO_USE_LOADING) != 0)
- {
- /* this is a stub replace operation */
- flags |= REPO_EXTEND_SOLVABLES;
- /* use REPO_REUSE_REPODATA hack so that the old repodata is kept */
- parent = repo_add_repodata(repo, flags | REPO_REUSE_REPODATA);
- extendstart = parent->start;
- extendend = parent->end;
- }
- else if (flags & REPO_EXTEND_SOLVABLES)
- {
- /* extend all solvables of this repo */
- extendstart = repo->start;
- extendend = repo->end;
- }
-
- memset(&data, 0, sizeof(data));
- data.repo = repo;
- data.fp = fp;
- repopagestore_init(&data.store);
-
- if (read_u32(&data) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
- return pool_error(pool, SOLV_ERROR_NOT_SOLV, "not a SOLV file");
- solvversion = read_u32(&data);
- switch (solvversion)
- {
- case SOLV_VERSION_8:
- break;
- default:
- return pool_error(pool, SOLV_ERROR_UNSUPPORTED, "unsupported SOLV version");
- }
-
- numid = (int)read_u32(&data);
- numrel = (int)read_u32(&data);
- numdir = (int)read_u32(&data);
- numsolv = (int)read_u32(&data);
- numkeys = (int)read_u32(&data);
- numschemata = (int)read_u32(&data);
- solvflags = read_u32(&data);
-
- if (numid < 0 || numid >= 0x20000000)
- return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of ids");
- if (numrel < 0 || numrel >= 0x20000000)
- return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of rels");
- if (numdir && (numdir < 2 || numdir >= 0x20000000))
- return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of dirs");
- if (numsolv < 0 || numsolv >= 0x20000000)
- return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of solvables");
- if (numkeys < 0 || numkeys >= 0x20000000)
- return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of keys");
- if (numschemata < 0 || numschemata >= 0x20000000)
- return pool_error(pool, SOLV_ERROR_CORRUPT, "bad number of schematas");
-
- if (numrel && (flags & REPO_LOCALPOOL) != 0)
- return pool_error(pool, SOLV_ERROR_CORRUPT, "relations are forbidden in a local pool");
- if ((flags & REPO_EXTEND_SOLVABLES) && numsolv)
- {
- /* make sure that we exactly replace the stub repodata */
- if (extendend - extendstart != numsolv)
- return pool_error(pool, SOLV_ERROR_CORRUPT, "sub-repository solvable number does not match main repository (%d - %d)", extendend - extendstart, numsolv);
- for (i = 0; i < numsolv; i++)
- if (pool->solvables[extendstart + i].repo != repo)
- return pool_error(pool, SOLV_ERROR_CORRUPT, "main repository contains holes, cannot extend");
- }
-
- /******* Part 1: string IDs *****************************************/
-
- sizeid = read_u32(&data); /* size of string space */
-
- /*
- * read strings and Ids
- *
- */
-
-
- /*
- * alloc buffers
- */
-
- if (!(flags & REPO_LOCALPOOL))
- {
- spool = &pool->ss;
- /* alloc max needed string buffer and string pointers, will shrink again later */
-#if 0
- spool->stringspace = solv_realloc(spool->stringspace, spool->sstrings + sizeid + 1);
- spool->strings = solv_realloc2(spool->strings, spool->nstrings + numid, sizeof(Offset));
-#else
- spool->sstrings += sizeid + 1;
- spool->nstrings += numid;
- stringpool_shrink(spool); /* we misuse stringpool_shrink so that the correct BLOCK factor is used */
- spool->sstrings -= sizeid + 1;
- spool->nstrings -= numid;
-#endif
- }
- else
- {
- data.localpool = 1;
- spool = &data.spool;
- spool->stringspace = solv_malloc(7 + sizeid + 1);
- spool->strings = solv_malloc2(numid < 2 ? 2 : numid, sizeof(Offset));
- strcpy(spool->stringspace, "<NULL>");
- spool->sstrings = 7;
- spool->nstrings = 1;
- spool->strings[0] = 0; /* <NULL> */
- }
-
-
- /*
- * read string data and append to old string space
- */
-
- strsp = spool->stringspace + spool->sstrings; /* append new entries */
- if ((solvflags & SOLV_FLAG_PREFIX_POOL) == 0)
- {
- if (sizeid && fread(strsp, sizeid, 1, fp) != 1)
- {
- repodata_freedata(&data);
- return pool_error(pool, SOLV_ERROR_EOF, "read error while reading strings");
- }
- }
- else
- {
- unsigned int pfsize = read_u32(&data);
- char *prefix = solv_malloc(pfsize);
- char *pp = prefix;
- char *old_str = 0;
- char *dest = strsp;
- int freesp = sizeid;
-
- if (pfsize && fread(prefix, pfsize, 1, fp) != 1)
- {
- solv_free(prefix);
- repodata_freedata(&data);
- return pool_error(pool, SOLV_ERROR_EOF, "read error while reading strings");
- }
- for (i = 1; i < numid; i++)
- {
- int same = (unsigned char)*pp++;
- size_t len = strlen(pp) + 1;
- freesp -= same + len;
- if (freesp < 0)
- {
- solv_free(prefix);
- repodata_freedata(&data);
- return pool_error(pool, SOLV_ERROR_OVERFLOW, "overflow while expanding strings");
- }
- if (same)
- memcpy(dest, old_str, same);
- memcpy(dest + same, pp, len);
- pp += len;
- old_str = dest;
- dest += same + len;
- }
- solv_free(prefix);
- if (freesp != 0)
- {
- repodata_freedata(&data);
- return pool_error(pool, SOLV_ERROR_CORRUPT, "expanding strings size mismatch");
- }
- }
- strsp[sizeid] = 0; /* make string space \0 terminated */
- sp = strsp;
-
- /* now merge */
- str = spool->strings; /* array of offsets into strsp, indexed by Id */
- if ((flags & REPO_LOCALPOOL) != 0)
- {
- /* no shared pool, thus no idmap and no unification needed */
- idmap = 0;
- spool->nstrings = numid < 2 ? 2 : numid; /* make sure we have at least id 0 and 1 */
- if (*sp)
- {
- /* we need id 1 to be '' for directories */
- repodata_freedata(&data);
- return pool_error(pool, SOLV_ERROR_CORRUPT, "store strings don't start with an empty string");
- }
- for (i = 1; i < spool->nstrings; i++)
- {
- if (sp >= strsp + sizeid && numid >= 2)
- {
- repodata_freedata(&data);
- return pool_error(pool, SOLV_ERROR_OVERFLOW, "not enough strings");
- }
- str[i] = sp - spool->stringspace;
- sp += strlen(sp) + 1;
- }
- spool->sstrings = sp - spool->stringspace;
- }
- else
- {
- Offset oldsstrings = spool->sstrings;
-
- /* alloc id map for name and rel Ids. this maps ids in the solv files
- * to the ids in our pool */
- idmap = solv_calloc(numid + numrel, sizeof(Id));
-
- /* grow hash if needed, otherwise reuse */
- hashmask = mkmask(spool->nstrings + numid);
-#if 0
- POOL_DEBUG(SOLV_DEBUG_STATS, "read %d strings\n", numid);
- POOL_DEBUG(SOLV_DEBUG_STATS, "string hash buckets: %d, old %d\n", hashmask + 1, spool->stringhashmask + 1);
-#endif
- if (hashmask > spool->stringhashmask)
- {
- spool->stringhashtbl = solv_free(spool->stringhashtbl);
- spool->stringhashmask = hashmask;
- spool->stringhashtbl = hashtbl = solv_calloc(hashmask + 1, sizeof(Id));
- for (i = 1; i < spool->nstrings; i++)
- {
- h = strhash(spool->stringspace + spool->strings[i]) & hashmask;
- hh = HASHCHAIN_START;
- while (hashtbl[h])
- h = HASHCHAIN_NEXT(h, hh, hashmask);
- hashtbl[h] = i;
- }
- }
- else
- {
- hashtbl = spool->stringhashtbl;
- hashmask = spool->stringhashmask;
- }
-
- /*
- * run over strings and merge with pool.
- * also populate id map (maps solv Id -> pool Id)
- */
- for (i = 1; i < numid; i++)
- {
- if (sp >= strsp + sizeid)
- {
- solv_free(idmap);
- spool->nstrings = oldnstrings;
- spool->sstrings = oldsstrings;
- stringpool_freehash(spool);
- repodata_freedata(&data);
- return pool_error(pool, SOLV_ERROR_OVERFLOW, "not enough strings %d %d", i, numid);
- }
- 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)
- break;
- if (!strcmp(spool->stringspace + spool->strings[id], sp))
- break; /* already in pool */
- h = HASHCHAIN_NEXT(h, hh, hashmask);
- }
-
- /* length == offset to next string */
- l = strlen(sp) + 1;
- if (!id) /* end of hash chain -> new string */
- {
- id = spool->nstrings++;
- hashtbl[h] = id;
- str[id] = spool->sstrings; /* save offset */
- if (sp != spool->stringspace + spool->sstrings)
- memmove(spool->stringspace + spool->sstrings, sp, l);
- spool->sstrings += l;
- }
- idmap[i] = id; /* repo relative -> pool relative */
- sp += l; /* next string */
- }
- if (hashmask > mkmask(spool->nstrings + 8192))
- {
- spool->stringhashtbl = solv_free(spool->stringhashtbl);
- spool->stringhashmask = 0;
- }
- stringpool_shrink(spool); /* vacuum */
- }
-
-
- /******* Part 2: Relation IDs ***************************************/
-
- /*
- * read RelDeps
- *
- */
-
- if (numrel)
- {
- /* extend rels */
- pool->rels = solv_realloc2(pool->rels, pool->nrels + numrel, sizeof(Reldep));
- ran = pool->rels;
-
- /* grow hash if needed, otherwise reuse */
- hashmask = mkmask(pool->nrels + numrel);
-#if 0
- POOL_DEBUG(SOLV_DEBUG_STATS, "read %d rels\n", numrel);
- POOL_DEBUG(SOLV_DEBUG_STATS, "rel hash buckets: %d, old %d\n", hashmask + 1, pool->relhashmask + 1);
-#endif
- if (hashmask > pool->relhashmask)
- {
- pool->relhashtbl = solv_free(pool->relhashtbl);
- pool->relhashmask = hashmask;
- pool->relhashtbl = hashtbl = solv_calloc(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;
- }
- }
- else
- {
- hashtbl = pool->relhashtbl;
- hashmask = pool->relhashmask;
- }
-
- /*
- * read RelDeps from repo
- */
- for (i = 0; i < numrel; i++)
- {
- name = read_id(&data, i + numid); /* read (repo relative) Ids */
- evr = read_id(&data, i + numid);
- relflags = read_u8(&data);
- name = idmap[name]; /* map to (pool relative) Ids */
- evr = idmap[evr];
- h = relhash(name, evr, relflags) & hashmask;
- hh = HASHCHAIN_START;
- for (;;)
- {
- id = hashtbl[h];
- if (!id) /* end of hash chain reached */
- break;
- if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == relflags)
- break;
- h = HASHCHAIN_NEXT(h, hh, hashmask);
- }
- if (!id) /* new RelDep */
- {
- id = pool->nrels++;
- hashtbl[h] = id;
- ran[id].name = name;
- ran[id].evr = evr;
- ran[id].flags = relflags;
- }
- idmap[i + numid] = MAKERELDEP(id); /* fill Id map */
- }
- if (hashmask > mkmask(pool->nrels + 4096))
- {
- pool->relhashtbl = solv_free(pool->relhashtbl);
- pool->relhashmask = 0;
- }
- pool_shrink_rels(pool); /* vacuum */
- }
-
- /* if we added ids/rels, make room in our whatprovide arrays */
- if (!(flags & REPO_LOCALPOOL))
- {
- if (pool->whatprovides && oldnstrings != pool->ss.nstrings)
- {
- int newlen = (pool->ss.nstrings + WHATPROVIDES_BLOCK) & ~WHATPROVIDES_BLOCK;
- pool->whatprovides = solv_realloc2(pool->whatprovides, newlen, sizeof(Offset));
- memset(pool->whatprovides + oldnstrings, 0, (newlen - oldnstrings) * sizeof(Offset));
- }
- if (pool->whatprovides_rel && oldnrels != pool->nrels)
- {
- int newlen = (pool->nrels + WHATPROVIDES_BLOCK) & ~WHATPROVIDES_BLOCK;
- pool->whatprovides_rel = solv_realloc2(pool->whatprovides_rel, newlen, sizeof(Offset));
- memset(pool->whatprovides_rel + oldnrels, 0, (newlen - oldnrels) * sizeof(Offset));
- }
- }
-
- /******* Part 3: Dirs ***********************************************/
- if (numdir)
- {
- data.dirpool.dirs = solv_malloc2(numdir, sizeof(Id));
- data.dirpool.ndirs = numdir;
- data.dirpool.dirs[0] = 0; /* dir 0: virtual root */
- data.dirpool.dirs[1] = 1; /* dir 1: / */
- for (i = 2; i < numdir; i++)
- {
- id = read_id(&data, i + numid);
- if (id >= numid)
- data.dirpool.dirs[i] = -(id - numid);
- else if (idmap)
- data.dirpool.dirs[i] = idmap[id];
- else
- data.dirpool.dirs[i] = id;
- }
- }
-
- /******* Part 4: Keys ***********************************************/
-
- keys = solv_calloc(numkeys, sizeof(*keys));
- /* keys start at 1 */
- for (i = 1; i < numkeys; i++)
- {
- id = read_id(&data, numid);
- if (idmap)
- id = idmap[id];
- else if ((flags & REPO_LOCALPOOL) != 0)
- id = pool_str2id(pool, stringpool_id2str(spool, id), 1);
- type = read_id(&data, numid);
- if (idmap)
- type = idmap[type];
- else if ((flags & REPO_LOCALPOOL) != 0)
- type = pool_str2id(pool, stringpool_id2str(spool, type), 1);
- if (type < REPOKEY_TYPE_VOID || type > REPOKEY_TYPE_FLEXARRAY)
- {
- data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "unsupported data type '%s'", pool_id2str(pool, type));
- type = REPOKEY_TYPE_VOID;
- }
- keys[i].name = id;
- keys[i].type = type;
- keys[i].size = read_id(&data, keys[i].type == REPOKEY_TYPE_CONSTANTID ? numid + numrel : 0);
- keys[i].storage = read_id(&data, 0);
- /* old versions used SOLVABLE for main solvable data */
- if (keys[i].storage == KEY_STORAGE_SOLVABLE)
- keys[i].storage = KEY_STORAGE_INCORE;
- if (keys[i].storage != KEY_STORAGE_INCORE && keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
- data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "unsupported storage type %d", keys[i].storage);
- if (id >= SOLVABLE_NAME && id <= RPM_RPMDBID)
- {
- if (keys[i].storage != KEY_STORAGE_INCORE)
- data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "main solvable data must use incore storage %d", keys[i].storage);
- keys[i].storage = KEY_STORAGE_SOLVABLE;
- }
- /* cannot handle rel idarrays in incore/vertical */
- if (type == REPOKEY_TYPE_REL_IDARRAY && keys[i].storage != KEY_STORAGE_SOLVABLE)
- data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "type REL_IDARRAY is only supported for STORAGE_SOLVABLE");
- /* cannot handle mapped ids in vertical */
- if (!(flags & REPO_LOCALPOOL) && keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET && (type == REPOKEY_TYPE_ID || type == REPOKEY_TYPE_IDARRAY))
- data.error = pool_error(pool, SOLV_ERROR_UNSUPPORTED, "mapped ids are not supported for STORAGE_VERTICAL_OFFSET");
-
- if (keys[i].type == REPOKEY_TYPE_CONSTANTID && idmap)
- keys[i].size = idmap[keys[i].size];
-#if 0
- fprintf(stderr, "key %d %s %s %d %d\n", i, pool_id2str(pool,id), pool_id2str(pool, keys[i].type),
- keys[i].size, keys[i].storage);
-#endif
- }
-
- have_incoredata = 0;
- for (i = 1; i < numkeys; i++)
- if (keys[i].storage == KEY_STORAGE_INCORE || keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
- have_incoredata = 1;
-
- data.keys = keys;
- data.nkeys = numkeys;
- for (i = 1; i < numkeys; i++)
- {
- id = keys[i].name;
- data.keybits[(id >> 3) & (sizeof(data.keybits) - 1)] |= 1 << (id & 7);
- }
-
- /******* Part 5: Schemata ********************************************/
-
- id = read_id(&data, 0);
- schemadata = solv_calloc(id + 1, sizeof(Id));
- schemadatap = schemadata + 1;
- schemadataend = schemadatap + id;
- schemata = solv_calloc(numschemata, sizeof(Id));
- for (i = 1; i < numschemata; i++)
- {
- schemata[i] = schemadatap - schemadata;
- schemadatap = read_idarray(&data, numid, 0, schemadatap, schemadataend);
-#if 0
- Id *sp = schemadata + schemata[i];
- fprintf(stderr, "schema %d:", i);
- for (; *sp; sp++)
- fprintf(stderr, " %d", *sp);
- fprintf(stderr, "\n");
-#endif
- }
- data.schemata = schemata;
- data.nschemata = numschemata;
- data.schemadata = schemadata;
- data.schemadatalen = schemadataend - data.schemadata;
-
- /******* Part 6: Data ********************************************/
-
- idarraydatap = idarraydataend = 0;
- size_idarray = 0;
-
- maxsize = read_id(&data, 0);
- allsize = read_id(&data, 0);
- maxsize += 5; /* so we can read the next schema of an array */
- if (maxsize > allsize)
- maxsize = allsize;
-
- buf = solv_calloc(maxsize + DATA_READ_CHUNK + 4, 1); /* 4 extra bytes to detect overflows */
- bufend = buf;
- dp = buf;
-
- l = maxsize;
- if (l < DATA_READ_CHUNK)
- l = DATA_READ_CHUNK;
- if (l > allsize)
- l = allsize;
- if (!l || fread(buf, l, 1, data.fp) != 1)
- {
- data.error = pool_error(pool, SOLV_ERROR_EOF, "unexpected EOF");
- id = 0;
- }
- else
- {
- bufend = buf + l;
- allsize -= l;
- dp = data_read_id_max(dp, &id, 0, numschemata, &data);
- }
-
- incore_add_id(&data, 0); /* so that incoreoffset 0 means schema 0 */
- incore_add_id(&data, id); /* main schema id */
- keyp = schemadata + schemata[id];
- data.mainschema = id;
- for (i = 0; keyp[i]; i++)
- ;
- if (i)
- data.mainschemaoffsets = solv_calloc(i, sizeof(Id));
-
- nentries = 0;
- keydepth = 0;
- s = 0;
- needchunk = 1;
- for(;;)
- {
- /* make sure we have enough room */
- if (keydepth == 0 || needchunk)
- {
- int left = bufend - dp;
- /* read data chunk to dp */
- if (data.error)
- break;
- if (left < 0)
- {
- data.error = pool_error(pool, SOLV_ERROR_EOF, "buffer overrun");
- break;
- }
- if (left < maxsize)
- {
- if (left)
- memmove(buf, dp, left);
- l = maxsize - left;
- if (l < DATA_READ_CHUNK)
- l = DATA_READ_CHUNK;
- if (l > allsize)
- l = allsize;
- if (l && fread(buf + left, l, 1, data.fp) != 1)
- {
- data.error = pool_error(pool, SOLV_ERROR_EOF, "unexpected EOF");
- break;
- }
- allsize -= l;
- left += l;
- bufend = buf + left;
- if (allsize + left < maxsize)
- maxsize = allsize + left;
- dp = buf;
- }
- needchunk = 0;
- }
-
- key = *keyp++;
-#if 0
-printf("key %d at %d\n", key, (int)(keyp - 1 - schemadata));
-#endif
- if (!key)
- {
- if (keydepth <= 3)
- needchunk = 1;
- if (nentries)
- {
- if (s && keydepth == 3)
- {
- s++; /* next solvable */
- if (have_incoredata)
- data.incoreoffset[(s - pool->solvables) - data.start] = data.incoredatalen;
- }
- id = stack[keydepth - 1];
- if (!id)
- {
- dp = data_read_id_max(dp, &id, 0, numschemata, &data);
- incore_add_id(&data, id);
- }
- keyp = schemadata + schemata[id];
- nentries--;
- continue;
- }
- if (!keydepth)
- break;
- --keydepth;
- keyp = schemadata + stack[--keydepth];
- nentries = stack[--keydepth];
-#if 0
-printf("pop flexarray %d %d\n", keydepth, nentries);
-#endif
- if (!keydepth && s)
- s = 0; /* back from solvables */
- continue;
- }
-
- if (keydepth == 0)
- data.mainschemaoffsets[keyp - 1 - (schemadata + schemata[data.mainschema])] = data.incoredatalen;
-
-#if 0
-printf("=> %s %s %p\n", pool_id2str(pool, keys[key].name), pool_id2str(pool, keys[key].type), s);
-#endif
- id = keys[key].name;
- if (keys[key].storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- dps = dp;
- dp = data_skip(dp, REPOKEY_TYPE_ID);
- dp = data_skip(dp, REPOKEY_TYPE_ID);
- incore_add_blob(&data, dps, dp - dps); /* just record offset/size */
- continue;
- }
- switch (keys[key].type)
- {
- case REPOKEY_TYPE_ID:
- dp = data_read_id_max(dp, &did, idmap, numid + numrel, &data);
- if (s && id == SOLVABLE_NAME)
- s->name = did;
- else if (s && id == SOLVABLE_ARCH)
- s->arch = did;
- else if (s && id == SOLVABLE_EVR)
- s->evr = did;
- else if (s && id == SOLVABLE_VENDOR)
- s->vendor = did;
- else if (keys[key].storage == KEY_STORAGE_INCORE)
- incore_add_id(&data, did);
-#if 0
- POOL_DEBUG(SOLV_DEBUG_STATS, "%s -> %s\n", pool_id2str(pool, id), pool_id2str(pool, did));
-#endif
- break;
- case REPOKEY_TYPE_IDARRAY:
- case REPOKEY_TYPE_REL_IDARRAY:
- if (!s || id < INTERESTED_START || id > INTERESTED_END)
- {
- dps = dp;
- dp = data_skip(dp, REPOKEY_TYPE_IDARRAY);
- if (keys[key].storage != KEY_STORAGE_INCORE)
- break;
- if (idmap)
- incore_map_idarray(&data, dps, idmap, numid + numrel);
- else
- incore_add_blob(&data, dps, dp - dps);
- break;
- }
- ido = idarraydatap - repo->idarraydata;
- if (keys[key].type == REPOKEY_TYPE_IDARRAY)
- dp = data_read_idarray(dp, &idarraydatap, idmap, numid + numrel, &data);
- else if (id == SOLVABLE_REQUIRES)
- dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data, SOLVABLE_PREREQMARKER);
- else if (id == SOLVABLE_PROVIDES)
- dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data, SOLVABLE_FILEMARKER);
- else
- dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data, 0);
- if (idarraydatap > idarraydataend)
- {
- data.error = pool_error(pool, SOLV_ERROR_OVERFLOW, "idarray overflow");
- break;
- }
- if (id == SOLVABLE_PROVIDES)
- s->provides = ido;
- else if (id == SOLVABLE_OBSOLETES)
- s->obsoletes = ido;
- else if (id == SOLVABLE_CONFLICTS)
- s->conflicts = ido;
- else if (id == SOLVABLE_REQUIRES)
- s->requires = ido;
- else if (id == SOLVABLE_RECOMMENDS)
- s->recommends= ido;
- else if (id == SOLVABLE_SUPPLEMENTS)
- s->supplements = ido;
- else if (id == SOLVABLE_SUGGESTS)
- s->suggests = ido;
- else if (id == SOLVABLE_ENHANCES)
- s->enhances = ido;
-#if 0
- POOL_DEBUG(SOLV_DEBUG_STATS, "%s ->\n", pool_id2str(pool, id));
- for (; repo->idarraydata[ido]; ido++)
- POOL_DEBUG(SOLV_DEBUG_STATS," %s\n", pool_dep2str(pool, repo->idarraydata[ido]));
-#endif
- break;
- case REPOKEY_TYPE_FIXARRAY:
- case REPOKEY_TYPE_FLEXARRAY:
- if (!keydepth)
- needchunk = 1;
- if (keydepth == sizeof(stack)/sizeof(*stack))
- {
- data.error = pool_error(pool, SOLV_ERROR_OVERFLOW, "array stack overflow");
- break;
- }
- stack[keydepth++] = nentries;
- stack[keydepth++] = keyp - schemadata;
- stack[keydepth++] = 0;
- dp = data_read_id_max(dp, &nentries, 0, 0, &data);
- incore_add_id(&data, nentries);
- if (!nentries)
- {
- /* zero size array? */
- keydepth -= 2;
- nentries = stack[--keydepth];
- break;
- }
- if (keydepth == 3 && id == REPOSITORY_SOLVABLES)
- {
- /* horray! here come the solvables */
- if (nentries != numsolv)
- {
- data.error = pool_error(pool, SOLV_ERROR_CORRUPT, "inconsistent number of solvables: %d %d", nentries, numsolv);
- break;
- }
- if (idarraydatap)
- {
- data.error = pool_error(pool, SOLV_ERROR_CORRUPT, "more than one solvable block");
- break;
- }
- if ((flags & REPO_EXTEND_SOLVABLES) != 0)
- s = pool_id2solvable(pool, extendstart);
- else
- s = pool_id2solvable(pool, repo_add_solvable_block(repo, numsolv));
- data.start = s - pool->solvables;
- data.end = data.start + numsolv;
- repodata_extend_block(&data, data.start, numsolv);
- for (i = 1; i < numkeys; i++)
- {
- id = keys[i].name;
- if ((keys[i].type == REPOKEY_TYPE_IDARRAY || keys[i].type == REPOKEY_TYPE_REL_IDARRAY)
- && id >= INTERESTED_START && id <= INTERESTED_END)
- size_idarray += keys[i].size;
- }
- /* allocate needed space in repo */
- /* we add maxsize because it is an upper limit for all idarrays, thus we can't overflow */
- repo_reserve_ids(repo, 0, size_idarray + maxsize + 1);
- idarraydatap = repo->idarraydata + repo->idarraysize;
- repo->idarraysize += size_idarray;
- idarraydataend = idarraydatap + size_idarray;
- repo->lastoff = 0;
- if (have_incoredata)
- data.incoreoffset[(s - pool->solvables) - data.start] = data.incoredatalen;
- }
- nentries--;
- dp = data_read_id_max(dp, &id, 0, numschemata, &data);
- incore_add_id(&data, id);
- if (keys[key].type == REPOKEY_TYPE_FIXARRAY)
- {
- if (!id)
- data.error = pool_error(pool, SOLV_ERROR_CORRUPT, "illegal fixarray");
- stack[keydepth - 1] = id;
- }
- keyp = schemadata + schemata[id];
- break;
- case REPOKEY_TYPE_NUM:
- if (!(solvflags & SOLV_FLAG_SIZE_BYTES) && keys[key].storage == KEY_STORAGE_INCORE &&
- (id == SOLVABLE_INSTALLSIZE || id == SOLVABLE_DOWNLOADSIZE || id == DELTA_DOWNLOADSIZE))
- {
- /* old solv file with sizes in kilos. transcode. */
- dp = data_read_id(dp, &id);
- incore_add_sizek(&data, (unsigned int)id);
- break;
- }
- /* FALLTHROUGH */
- default:
- if (id == RPM_RPMDBID && s && (keys[key].type == REPOKEY_TYPE_U32 || keys[key].type == REPOKEY_TYPE_NUM))
- {
- if (keys[key].type == REPOKEY_TYPE_U32)
- dp = data_read_u32(dp, (unsigned int *)&id);
- else
- dp = data_read_id_max(dp, &id, 0, 0, &data);
- if (!repo->rpmdbid)
- repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
- repo->rpmdbid[(s - pool->solvables) - repo->start] = id;
- break;
- }
- dps = dp;
- dp = data_skip(dp, keys[key].type);
- if (keys[key].storage == KEY_STORAGE_INCORE)
- incore_add_blob(&data, dps, dp - dps);
- break;
- }
- }
- /* should shrink idarraydata again */
-
- if (keydepth)
- data.error = pool_error(pool, SOLV_ERROR_EOF, "unexpected EOF, depth = %d", keydepth);
- if (!data.error)
- {
- if (dp > bufend)
- data.error = pool_error(pool, SOLV_ERROR_EOF, "buffer overrun");
- }
- solv_free(buf);
-
- if (data.error)
- {
- /* free solvables */
- repo_free_solvable_block(repo, data.start, data.end - data.start, 1);
- /* free id array */
- repo->idarraysize -= size_idarray;
- /* free incore data */
- data.incoredata = solv_free(data.incoredata);
- data.incoredatalen = data.incoredatafree = 0;
- }
-
- if (data.incoredatafree)
- {
- /* shrink excess size */
- data.incoredata = solv_realloc(data.incoredata, data.incoredatalen);
- data.incoredatafree = 0;
- }
- solv_free(idmap);
-
- /* fixup the special idarray type */
- for (i = 1; i < numkeys; i++)
- if (keys[i].type == REPOKEY_TYPE_REL_IDARRAY)
- keys[i].type = REPOKEY_TYPE_IDARRAY;
-
- for (i = 1; i < numkeys; i++)
- if (keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
- break;
- if (i < numkeys && !data.error)
- {
- Id fileoffset = 0;
- unsigned int pagesize;
-
- /* we have vertical data, make it available */
- data.verticaloffset = solv_calloc(numkeys, sizeof(Id));
- for (i = 1; i < numkeys; i++)
- if (keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- data.verticaloffset[i] = fileoffset;
- fileoffset += keys[i].size;
- }
- data.lastverticaloffset = fileoffset;
- pagesize = read_u32(&data);
- if (!data.error)
- {
- data.error = repopagestore_read_or_setup_pages(&data.store, data.fp, pagesize, fileoffset);
- if (data.error == SOLV_ERROR_EOF)
- pool_error(pool, data.error, "repopagestore setup: unexpected EOF");
- else if (data.error)
- pool_error(pool, data.error, "repopagestore setup failed");
- }
- }
- data.fp = 0; /* no longer needed */
-
- if (data.error)
- {
- i = data.error;
- repodata_freedata(&data);
- return i;
- }
-
- if (parent)
- {
- /* overwrite stub repodata */
- repodata_freedata(parent);
- data.repodataid = parent->repodataid;
- *parent = data;
- }
- else
- {
- /* make it available as new repodata */
- if (!repo->nrepodata)
- {
- repo->nrepodata = 1;
- repo->repodata = solv_calloc(2, sizeof(data));
- }
- else
- repo->repodata = solv_realloc2(repo->repodata, repo->nrepodata + 1, sizeof(data));
- data.repodataid = repo->nrepodata;
- repo->repodata[repo->nrepodata++] = data;
- }
-
- /* create stub repodata entries for all external */
- if (!(flags & SOLV_ADD_NO_STUBS) && !parent)
- {
- for (key = 1 ; key < data.nkeys; key++)
- if (data.keys[key].name == REPOSITORY_EXTERNAL && data.keys[key].type == REPOKEY_TYPE_FLEXARRAY)
- break;
- if (key < data.nkeys)
- repodata_create_stubs(repo->repodata + (repo->nrepodata - 1));
- }
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_solv took %d ms\n", solv_timems(now));
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
- POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", data.incoredatalen/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo_solv.h
- *
- */
-
-#ifndef LIBSOLV_REPO_SOLVE_H
-#define LIBSOLV_REPO_SOLVE_H
-
-#include <stdio.h>
-
-#include "pool.h"
-#include "repo.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern int repo_add_solv(Repo *repo, FILE *fp, int flags);
-
-#define SOLV_ADD_NO_STUBS (1 << 8)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_REPO_SOLVE_H */
+++ /dev/null
-/*
- * Copyright (c) 2007-2011, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo_write.c
- *
- * Write Repo data out to a file in solv format
- *
- * See doc/README.format for a description
- * of the binary file format
- *
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "util.h"
-#include "repo_write.h"
-#include "repopage.h"
-
-/*------------------------------------------------------------------*/
-/* Id map optimizations */
-
-typedef struct needid {
- Id need;
- Id map;
-} NeedId;
-
-
-#define RELOFF(id) (needid[0].map + GETRELID(id))
-
-/*
- * increment need Id
- * idarray: array of Ids, ID_NULL terminated
- * needid: array of Id->NeedId
- *
- * return size of array (including trailing zero)
- *
- */
-
-static void
-incneedid(Pool *pool, Id id, NeedId *needid)
-{
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- needid[RELOFF(id)].need++;
- if (ISRELDEP(rd->evr))
- incneedid(pool, rd->evr, needid);
- else
- needid[rd->evr].need++;
- id = rd->name;
- }
- needid[id].need++;
-}
-
-static int
-incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
-{
- Id id;
- int n = 0;
-
- if (!idarray)
- return 0;
- while ((id = *idarray++) != 0)
- {
- n++;
- while (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- needid[RELOFF(id)].need++;
- if (ISRELDEP(rd->evr))
- incneedid(pool, rd->evr, needid);
- else
- needid[rd->evr].need++;
- id = rd->name;
- }
- needid[id].need++;
- }
- return n + 1;
-}
-
-
-/*
- *
- */
-
-static int
-needid_cmp_need(const void *ap, const void *bp, void *dp)
-{
- const NeedId *a = ap;
- const NeedId *b = bp;
- int r;
- r = b->need - a->need;
- if (r)
- return r;
- return a->map - b->map;
-}
-
-static int
-needid_cmp_need_s(const void *ap, const void *bp, void *dp)
-{
- const NeedId *a = ap;
- const NeedId *b = bp;
- Stringpool *spool = dp;
- const char *as;
- const char *bs;
-
- int r;
- r = b->need - a->need;
- if (r)
- return r;
- as = spool->stringspace + spool->strings[a->map];
- bs = spool->stringspace + spool->strings[b->map];
- return strcmp(as, bs);
-}
-
-
-/*------------------------------------------------------------------*/
-/* output helper routines, used for writing the header */
-/* (the data itself is accumulated in memory and written with
- * write_blob) */
-
-/*
- * unsigned 32-bit
- */
-
-static void
-write_u32(Repodata *data, unsigned int x)
-{
- FILE *fp = data->fp;
- if (data->error)
- return;
- if (putc(x >> 24, fp) == EOF ||
- putc(x >> 16, fp) == EOF ||
- putc(x >> 8, fp) == EOF ||
- putc(x, fp) == EOF)
- {
- data->error = pool_error(data->repo->pool, -1, "write error u32: %s", strerror(errno));
- }
-}
-
-
-/*
- * unsigned 8-bit
- */
-
-static void
-write_u8(Repodata *data, unsigned int x)
-{
- if (data->error)
- return;
- if (putc(x, data->fp) == EOF)
- {
- data->error = pool_error(data->repo->pool, -1, "write error u8: %s", strerror(errno));
- }
-}
-
-/*
- * data blob
- */
-
-static void
-write_blob(Repodata *data, void *blob, int len)
-{
- if (data->error)
- return;
- if (len && fwrite(blob, len, 1, data->fp) != 1)
- {
- data->error = pool_error(data->repo->pool, -1, "write error blob: %s", strerror(errno));
- }
-}
-
-/*
- * Id
- */
-
-static void
-write_id(Repodata *data, Id x)
-{
- FILE *fp = data->fp;
- if (data->error)
- return;
- 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)
- {
- data->error = pool_error(data->repo->pool, -1, "write error id: %s", strerror(errno));
- }
-}
-
-static inline void
-write_id_eof(Repodata *data, Id x, int eof)
-{
- if (x >= 64)
- x = (x & 63) | ((x & ~63) << 1);
- write_id(data, x | (eof ? 0 : 64));
-}
-
-
-
-static inline void
-write_str(Repodata *data, const char *str)
-{
- if (data->error)
- return;
- if (fputs(str, data->fp) == EOF || putc(0, data->fp) == EOF)
- {
- data->error = pool_error(data->repo->pool, -1, "write error str: %s", strerror(errno));
- }
-}
-
-/*
- * Array of Ids
- */
-
-static void
-write_idarray(Repodata *data, Pool *pool, NeedId *needid, Id *ids)
-{
- Id id;
- if (!ids)
- return;
- if (!*ids)
- {
- write_u8(data, 0);
- return;
- }
- for (;;)
- {
- id = *ids++;
- if (needid)
- id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
- if (id >= 64)
- id = (id & 63) | ((id & ~63) << 1);
- if (!*ids)
- {
- write_id(data, id);
- return;
- }
- write_id(data, id | 64);
- }
-}
-
-static int
-cmp_ids(const void *pa, const void *pb, void *dp)
-{
- Id a = *(Id *)pa;
- Id b = *(Id *)pb;
- return a - b;
-}
-
-#if 0
-static void
-write_idarray_sort(Repodata *data, Pool *pool, NeedId *needid, Id *ids, Id marker)
-{
- int len, i;
- Id lids[64], *sids;
-
- if (!ids)
- return;
- if (!*ids)
- {
- write_u8(data, 0);
- return;
- }
- for (len = 0; len < 64 && ids[len]; len++)
- {
- Id id = ids[len];
- if (needid)
- id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
- lids[len] = id;
- }
- if (ids[len])
- {
- for (i = len + 1; ids[i]; i++)
- ;
- sids = solv_malloc2(i, sizeof(Id));
- memcpy(sids, lids, 64 * sizeof(Id));
- for (; ids[len]; len++)
- {
- Id id = ids[len];
- if (needid)
- id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
- sids[len] = id;
- }
- }
- else
- sids = lids;
-
- /* That bloody solvable:prereqmarker needs to stay in position :-( */
- if (needid)
- marker = needid[marker].need;
- for (i = 0; i < len; i++)
- if (sids[i] == marker)
- break;
- if (i > 1)
- solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
- if ((len - i) > 2)
- solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
-
- Id id, old = 0;
-
- /* The differencing above produces many runs of ones and twos. I tried
- fairly elaborate schemes to RLE those, but they give only very mediocre
- improvements in compression, as coding the escapes costs quite some
- space. Even if they are coded only as bits in IDs. The best improvement
- was about 2.7% for the whole .solv file. It's probably better to
- invest some complexity into sharing idarrays, than RLEing. */
- for (i = 0; i < len - 1; i++)
- {
- id = sids[i];
- /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
- hence all real differences are offsetted by 1. Otherwise we would
- have to handle negative differences, which would cost code space for
- the encoding of the sign. We loose the exact mapping of prereq here,
- but we know the result, so we can recover from that in the reader. */
- if (id == marker)
- id = old = 0;
- else
- {
- id = id - old + 1;
- old = sids[i];
- }
- /* XXX If difference is zero we have multiple equal elements,
- we might want to skip writing them out. */
- if (id >= 64)
- id = (id & 63) | ((id & ~63) << 1);
- write_id(data, id | 64);
- }
- id = sids[i];
- if (id == marker)
- id = 0;
- else
- id = id - old + 1;
- if (id >= 64)
- id = (id & 63) | ((id & ~63) << 1);
- write_id(data, id);
- if (sids != lids)
- solv_free(sids);
-}
-#endif
-
-
-struct extdata {
- unsigned char *buf;
- int len;
-};
-
-struct cbdata {
- Repo *repo;
- Repodata *target;
-
- Stringpool *ownspool;
- Dirpool *owndirpool;
-
- Id *keymap;
- int nkeymap;
- Id *keymapstart;
-
- NeedId *needid;
-
- Id *schema; /* schema construction space */
- Id *sp; /* pointer in above */
- Id *oldschema, *oldsp;
-
- Id *solvschemata;
- Id *subschemata;
- int nsubschemata;
- int current_sub;
-
- struct extdata *extdata;
-
- Id *dirused;
-
- Id vstart;
-
- Id maxdata;
- Id lastlen;
-
- int doingsolvables; /* working on solvables data */
- int filelistmode;
-};
-
-#define NEEDED_BLOCK 1023
-#define SCHEMATA_BLOCK 31
-#define SCHEMATADATA_BLOCK 255
-#define EXTDATA_BLOCK 4095
-
-static inline void
-data_addid(struct extdata *xd, Id sx)
-{
- unsigned int x = (unsigned int)sx;
- unsigned char *dp;
-
- xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
- dp = xd->buf + xd->len;
-
- if (x >= (1 << 14))
- {
- if (x >= (1 << 28))
- *dp++ = (x >> 28) | 128;
- if (x >= (1 << 21))
- *dp++ = (x >> 21) | 128;
- *dp++ = (x >> 14) | 128;
- }
- if (x >= (1 << 7))
- *dp++ = (x >> 7) | 128;
- *dp++ = x & 127;
- xd->len = dp - xd->buf;
-}
-
-static inline void
-data_addideof(struct extdata *xd, Id sx, int eof)
-{
- unsigned int x = (unsigned int)sx;
- unsigned char *dp;
-
- xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
- dp = xd->buf + xd->len;
-
- if (x >= (1 << 13))
- {
- if (x >= (1 << 27))
- *dp++ = (x >> 27) | 128;
- if (x >= (1 << 20))
- *dp++ = (x >> 20) | 128;
- *dp++ = (x >> 13) | 128;
- }
- if (x >= (1 << 6))
- *dp++ = (x >> 6) | 128;
- *dp++ = eof ? (x & 63) : (x & 63) | 64;
- xd->len = dp - xd->buf;
-}
-
-static inline int
-data_addideof_len(Id sx)
-{
- unsigned int x = (unsigned int)sx;
- if (x >= (1 << 13))
- {
- if (x >= (1 << 27))
- return 5;
- return x >= (1 << 20) ? 4 : 3;
- }
- return x >= (1 << 6) ? 2 : 1;
-}
-
-static void
-data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
-{
- if (hx)
- {
- if (hx > 7)
- {
- data_addid(xd, (Id)(hx >> 3));
- xd->buf[xd->len - 1] |= 128;
- hx &= 7;
- }
- data_addid(xd, (Id)(x | 0x80000000));
- xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
- }
- else
- data_addid(xd, (Id)x);
-}
-
-static void
-data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
-{
- int len, i;
- Id lids[64], *sids;
- Id id, old;
-
- if (!ids)
- return;
- if (!*ids)
- {
- data_addid(xd, 0);
- return;
- }
- for (len = 0; len < 64 && ids[len]; len++)
- {
- Id id = ids[len];
- if (needid)
- id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
- lids[len] = id;
- }
- if (ids[len])
- {
- for (i = len + 1; ids[i]; i++)
- ;
- sids = solv_malloc2(i, sizeof(Id));
- memcpy(sids, lids, 64 * sizeof(Id));
- for (; ids[len]; len++)
- {
- Id id = ids[len];
- if (needid)
- id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
- sids[len] = id;
- }
- }
- else
- sids = lids;
-
- /* That bloody solvable:prereqmarker needs to stay in position :-( */
- if (needid)
- marker = needid[marker].need;
- for (i = 0; i < len; i++)
- if (sids[i] == marker)
- break;
- if (i > 1)
- solv_sort(sids, i, sizeof(Id), cmp_ids, 0);
- if ((len - i) > 2)
- solv_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
-
- old = 0;
-
- /* The differencing above produces many runs of ones and twos. I tried
- fairly elaborate schemes to RLE those, but they give only very mediocre
- improvements in compression, as coding the escapes costs quite some
- space. Even if they are coded only as bits in IDs. The best improvement
- was about 2.7% for the whole .solv file. It's probably better to
- invest some complexity into sharing idarrays, than RLEing. */
- for (i = 0; i < len - 1; i++)
- {
- id = sids[i];
- /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
- hence all real differences are offsetted by 1. Otherwise we would
- have to handle negative differences, which would cost code space for
- the encoding of the sign. We loose the exact mapping of prereq here,
- but we know the result, so we can recover from that in the reader. */
- if (id == marker)
- id = old = 0;
- else
- {
- id = id - old + 1;
- old = sids[i];
- }
- /* XXX If difference is zero we have multiple equal elements,
- we might want to skip writing them out. */
- data_addideof(xd, id, 0);
- }
- id = sids[i];
- if (id == marker)
- id = 0;
- else
- id = id - old + 1;
- data_addideof(xd, id, 1);
- if (sids != lids)
- solv_free(sids);
-}
-
-static inline void
-data_addblob(struct extdata *xd, unsigned char *blob, int len)
-{
- xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
- memcpy(xd->buf + xd->len, blob, len);
- xd->len += len;
-}
-
-static inline void
-data_addu32(struct extdata *xd, unsigned int num)
-{
- unsigned char d[4];
- d[0] = num >> 24;
- d[1] = num >> 16;
- d[2] = num >> 8;
- d[3] = num;
- data_addblob(xd, d, 4);
-}
-
-static Id
-putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
-{
- const char *str = stringpool_id2str(ss, id);
- id = stringpool_str2id(cbdata->ownspool, str, 1);
- if (id >= cbdata->needid[0].map)
- {
- int oldoff = cbdata->needid[0].map;
- int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
- int nrels = cbdata->repo->pool->nrels;
- cbdata->needid = solv_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
- if (nrels)
- memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
- memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
- cbdata->needid[0].map = newoff;
- }
- return id;
-}
-
-static Id
-putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
-{
- Id compid, parent;
-
- parent = dirpool_parent(dp, dir);
- if (parent)
- parent = putinowndirpool(cbdata, data, dp, parent);
- compid = dp->dirs[dir];
- if (cbdata->ownspool && compid > 1)
- compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
- return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
-}
-
-/*
- * collect usage information about the dirs
- * 1: dir used, no child of dir used
- * 2: dir used as parent of another used dir
- */
-static inline void
-setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
-{
- if (cbdata->dirused[dir])
- return;
- cbdata->dirused[dir] = 1;
- while ((dir = dirpool_parent(dp, dir)) != 0)
- {
- if (cbdata->dirused[dir] == 2)
- return;
- if (cbdata->dirused[dir])
- {
- cbdata->dirused[dir] = 2;
- return;
- }
- cbdata->dirused[dir] = 2;
- }
- cbdata->dirused[0] = 2;
-}
-
-/*
- * pass 1 callback:
- * collect key/id/dirid usage information, create needed schemas
- */
-static int
-repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
-{
- Id id;
- int rm;
-
- if (key->name == REPOSITORY_SOLVABLES)
- return SEARCH_NEXT_KEY; /* we do not want this one */
-
- /* hack: ignore some keys, see BUGS */
- if (data->repodataid != data->repo->nrepodata - 1)
- if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
- return SEARCH_NEXT_KEY;
-
- rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
- if (!rm)
- return SEARCH_NEXT_KEY; /* we do not want this one */
-
- /* record key in schema */
- if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
- && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
- *cbdata->sp++ = rm;
-
- switch(key->type)
- {
- case REPOKEY_TYPE_ID:
- case REPOKEY_TYPE_IDARRAY:
- id = kv->id;
- if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
- id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
- incneedid(repo->pool, id, cbdata->needid);
- break;
- case REPOKEY_TYPE_DIR:
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- case REPOKEY_TYPE_DIRSTRARRAY:
- id = kv->id;
- if (cbdata->owndirpool)
- putinowndirpool(cbdata, data, &data->dirpool, id);
- else
- setdirused(cbdata, &data->dirpool, id);
- break;
- case REPOKEY_TYPE_FIXARRAY:
- if (kv->eof == 0)
- {
- if (cbdata->oldschema)
- {
- cbdata->target->error = pool_error(cbdata->repo->pool, -1, "nested fixarray structs not yet implemented");
- return SEARCH_NEXT_KEY;
- }
- cbdata->oldschema = cbdata->schema;
- cbdata->oldsp = cbdata->sp;
- cbdata->schema = solv_calloc(cbdata->target->nkeys, sizeof(Id));
- cbdata->sp = cbdata->schema;
- }
- else if (kv->eof == 1)
- {
- cbdata->current_sub++;
- *cbdata->sp = 0;
- cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
- cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, cbdata->schema, 1);
-#if 0
- fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
-#endif
- cbdata->sp = cbdata->schema;
- }
- else
- {
- solv_free(cbdata->schema);
- cbdata->schema = cbdata->oldschema;
- cbdata->sp = cbdata->oldsp;
- cbdata->oldsp = cbdata->oldschema = 0;
- }
- break;
- case REPOKEY_TYPE_FLEXARRAY:
- if (kv->entry == 0)
- {
- if (kv->eof != 2)
- *cbdata->sp++ = 0; /* mark start */
- }
- else
- {
- /* just finished a schema, rewind */
- Id *sp = cbdata->sp - 1;
- *sp = 0;
- while (sp[-1])
- sp--;
- cbdata->subschemata = solv_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
- cbdata->subschemata[cbdata->nsubschemata++] = repodata_schema2id(cbdata->target, sp, 1);
- cbdata->sp = kv->eof == 2 ? sp - 1: sp;
- }
- break;
- default:
- break;
- }
- return 0;
-}
-
-static int
-repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
-{
- struct cbdata *cbdata = vcbdata;
- Repo *repo = data->repo;
-
-#if 0
- if (s)
- fprintf(stderr, "solvable %d (%s): key (%d)%s %d\n", s ? s - repo->pool->solvables : 0, s ? pool_id2str(repo->pool, s->name) : "", key->name, pool_id2str(repo->pool, key->name), key->type);
-#endif
- return repo_write_collect_needed(cbdata, repo, data, key, kv);
-}
-
-
-/*
- * pass 2 callback:
- * encode all of the data into the correct buffers
- */
-
-static int
-repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
-{
- int rm;
- Id id;
- unsigned int u32;
- unsigned char v[4];
- struct extdata *xd;
- NeedId *needid;
-
- if (key->name == REPOSITORY_SOLVABLES)
- return SEARCH_NEXT_KEY;
-
- /* hack: ignore some keys, see BUGS */
- if (data->repodataid != data->repo->nrepodata - 1)
- if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS || key->name == REPOSITORY_TOOLVERSION)
- return SEARCH_NEXT_KEY;
-
- rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
- if (!rm)
- return SEARCH_NEXT_KEY; /* we do not want this one */
-
- if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- xd = cbdata->extdata + rm; /* vertical buffer */
- if (cbdata->vstart == -1)
- cbdata->vstart = xd->len;
- }
- else
- xd = cbdata->extdata + 0; /* incore buffer */
- switch(key->type)
- {
- case REPOKEY_TYPE_VOID:
- case REPOKEY_TYPE_CONSTANT:
- case REPOKEY_TYPE_CONSTANTID:
- break;
- case REPOKEY_TYPE_ID:
- id = kv->id;
- if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
- id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
- needid = cbdata->needid;
- id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
- data_addid(xd, id);
- break;
- case REPOKEY_TYPE_IDARRAY:
- id = kv->id;
- if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
- id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
- needid = cbdata->needid;
- id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
- data_addideof(xd, id, kv->eof);
- break;
- case REPOKEY_TYPE_STR:
- data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
- break;
- case REPOKEY_TYPE_MD5:
- data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
- break;
- case REPOKEY_TYPE_SHA1:
- data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
- break;
- case REPOKEY_TYPE_SHA224:
- data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA224);
- break;
- case REPOKEY_TYPE_SHA256:
- data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
- break;
- case REPOKEY_TYPE_SHA384:
- data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA384);
- break;
- case REPOKEY_TYPE_SHA512:
- data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA512);
- break;
- case REPOKEY_TYPE_U32:
- u32 = kv->num;
- v[0] = u32 >> 24;
- v[1] = u32 >> 16;
- v[2] = u32 >> 8;
- v[3] = u32;
- data_addblob(xd, v, 4);
- break;
- case REPOKEY_TYPE_NUM:
- data_addid64(xd, kv->num, kv->num2);
- break;
- case REPOKEY_TYPE_DIR:
- id = kv->id;
- if (cbdata->owndirpool)
- id = putinowndirpool(cbdata, data, &data->dirpool, id);
- id = cbdata->dirused[id];
- data_addid(xd, id);
- break;
- case REPOKEY_TYPE_BINARY:
- data_addid(xd, kv->num);
- if (kv->num)
- data_addblob(xd, (unsigned char *)kv->str, kv->num);
- break;
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- id = kv->id;
- if (cbdata->owndirpool)
- id = putinowndirpool(cbdata, data, &data->dirpool, id);
- id = cbdata->dirused[id];
- data_addid(xd, id);
- data_addid(xd, kv->num);
- data_addideof(xd, kv->num2, kv->eof);
- break;
- case REPOKEY_TYPE_DIRSTRARRAY:
- id = kv->id;
- if (cbdata->owndirpool)
- id = putinowndirpool(cbdata, data, &data->dirpool, id);
- id = cbdata->dirused[id];
- if (cbdata->filelistmode > 0)
- {
- xd->len += data_addideof_len(id) + strlen(kv->str) + 1;
- break;
- }
- data_addideof(xd, id, kv->eof);
- data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
- if (cbdata->filelistmode < 0)
- return 0;
- break;
- case REPOKEY_TYPE_FIXARRAY:
- if (kv->eof == 0)
- {
- if (kv->num)
- {
- data_addid(xd, kv->num);
- data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
-#if 0
- fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
-#endif
- }
- }
- else if (kv->eof == 1)
- {
- cbdata->current_sub++;
- }
- break;
- case REPOKEY_TYPE_FLEXARRAY:
- if (!kv->entry)
- data_addid(xd, kv->num);
- if (kv->eof != 2)
- data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
- if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
- {
- if (xd->len - cbdata->lastlen > cbdata->maxdata)
- cbdata->maxdata = xd->len - cbdata->lastlen;
- cbdata->lastlen = xd->len;
- }
- break;
- default:
- cbdata->target->error = pool_error(cbdata->repo->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
- break;
- }
- if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
- {
- /* we can re-use old data in the blob here! */
- data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
- data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
- cbdata->vstart = -1;
- }
- return 0;
-}
-
-static int
-repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
-{
- struct cbdata *cbdata = vcbdata;
- return repo_write_adddata(cbdata, data, key, kv);
-}
-
-/* traverse through directory with first child "dir" */
-static int
-traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
-{
- Id sib, child;
- Id parent, lastn;
-
- parent = n;
- /* special case for '/', which has to come first */
- if (parent == 1)
- dirmap[n++] = 1;
- for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
- {
- if (used && !used[sib])
- continue;
- if (sib == 1 && parent == 1)
- continue; /* already did that one above */
- dirmap[n++] = sib;
- }
-
- /* now go through all the siblings we just added and
- * do recursive calls on them */
- lastn = n;
- for (; parent < lastn; parent++)
- {
- sib = dirmap[parent];
- if (used && used[sib] != 2) /* 2: used as parent */
- continue;
- child = dirpool_child(dp, sib);
- if (child)
- {
- dirmap[n++] = -parent; /* start new block */
- n = traverse_dirs(dp, dirmap, n, child, used);
- }
- }
- return n;
-}
-
-static void
-write_compressed_page(Repodata *data, unsigned char *page, int len)
-{
- int clen;
- unsigned char cpage[REPOPAGE_BLOBSIZE];
-
- clen = repopagestore_compress_page(page, len, cpage, len - 1);
- if (!clen)
- {
- write_u32(data, len * 2);
- write_blob(data, page, len);
- }
- else
- {
- write_u32(data, clen * 2 + 1);
- write_blob(data, cpage, clen);
- }
-}
-
-static Id verticals[] = {
- SOLVABLE_AUTHORS,
- SOLVABLE_DESCRIPTION,
- SOLVABLE_MESSAGEDEL,
- SOLVABLE_MESSAGEINS,
- SOLVABLE_EULA,
- SOLVABLE_DISKUSAGE,
- SOLVABLE_FILELIST,
- SOLVABLE_CHECKSUM,
- DELTA_CHECKSUM,
- DELTA_SEQ_NUM,
- SOLVABLE_PKGID,
- SOLVABLE_HDRID,
- SOLVABLE_LEADSIGID,
- SOLVABLE_CHANGELOG_AUTHOR,
- SOLVABLE_CHANGELOG_TEXT,
- 0
-};
-
-static char *languagetags[] = {
- "solvable:summary:",
- "solvable:description:",
- "solvable:messageins:",
- "solvable:messagedel:",
- "solvable:eula:",
- 0
-};
-
-int
-repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
-{
- const char *keyname;
- int i;
-
- for (i = 0; verticals[i]; i++)
- if (key->name == verticals[i])
- return KEY_STORAGE_VERTICAL_OFFSET;
- keyname = pool_id2str(repo->pool, key->name);
- for (i = 0; languagetags[i] != 0; i++)
- if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
- return KEY_STORAGE_VERTICAL_OFFSET;
- return KEY_STORAGE_INCORE;
-}
-
-/*
- * return true if the repodata contains the filelist (and just
- * the filelist). The same code is used in the dataiterator. The way
- * it is used is completely wrong, of course, as having the filelist
- * key does not mean it is used for a specific solvable. Nevertheless
- * it is better to have it than to write broken solv files.
- */
-static inline int
-is_filelist_extension(Repodata *data)
-{
- int j;
- for (j = 1; j < data->nkeys; j++)
- if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
- return 0;
- return 1;
-}
-
-
-static int
-write_compressed_extdata(Repodata *target, struct extdata *xd, unsigned char *vpage, int lpage)
-{
- unsigned char *dp = xd->buf;
- int l = xd->len;
- while (l)
- {
- int ll = REPOPAGE_BLOBSIZE - lpage;
- if (l < ll)
- ll = l;
- memcpy(vpage + lpage, dp, ll);
- dp += ll;
- lpage += ll;
- l -= ll;
- if (lpage == REPOPAGE_BLOBSIZE)
- {
- write_compressed_page(target, vpage, lpage);
- lpage = 0;
- }
- }
- return lpage;
-}
-
-/*
- * Repo
- */
-
-/*
- * the code works the following way:
- *
- * 1) find which keys should be written
- * 2) collect usage information for keys/ids/dirids, create schema
- * data
- * 3) use usage information to create mapping tables, so that often
- * used ids get a lower number
- * 4) encode data into buffers using the mapping tables
- * 5) write everything to disk
- */
-int
-repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
-{
- Pool *pool = repo->pool;
- int i, j, n, lastfilelistn;
- Solvable *s;
- NeedId *needid;
- int nstrings, nrels;
- unsigned int sizeid;
- unsigned int solv_flags;
- Reldep *ran;
- Id *idarraydata;
-
- Id id, *sp;
-
- Id *dirmap;
- int ndirmap;
- Id *keyused;
- unsigned char *repodataused;
- int anyrepodataused = 0;
- int anysolvableused = 0;
-
- struct cbdata cbdata;
- int clonepool;
- Repokey *key;
- int poolusage, dirpoolusage, idused, dirused;
- int reloff;
-
- Repodata *data, *dirpooldata;
-
- Repodata target;
-
- Stringpool *spool;
- Dirpool *dirpool;
-
- Id mainschema;
-
- struct extdata *xd;
-
- Id type_constantid = REPOKEY_TYPE_CONSTANTID;
-
-
- memset(&cbdata, 0, sizeof(cbdata));
- cbdata.repo = repo;
- cbdata.target = ⌖
-
- repodata_initdata(&target, repo, 1);
-
- /* go through all repodata and find the keys we need */
- /* also unify keys */
- /* keymapstart - maps repo number to keymap offset */
- /* keymap - maps repo key to my key, 0 -> not used */
-
- /* start with all KEY_STORAGE_SOLVABLE ids */
-
- n = ID_NUM_INTERNAL;
- FOR_REPODATAS(repo, i, data)
- n += data->nkeys;
- cbdata.keymap = solv_calloc(n, sizeof(Id));
- cbdata.keymapstart = solv_calloc(repo->nrepodata, sizeof(Id));
- repodataused = solv_calloc(repo->nrepodata, 1);
-
- clonepool = 0;
- poolusage = 0;
-
- /* add keys for STORAGE_SOLVABLE */
- for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
- {
- Repokey keyd;
- keyd.name = i;
- if (i < SOLVABLE_PROVIDES)
- keyd.type = REPOKEY_TYPE_ID;
- else if (i < RPM_RPMDBID)
- keyd.type = REPOKEY_TYPE_REL_IDARRAY;
- else
- keyd.type = REPOKEY_TYPE_NUM;
- keyd.size = 0;
- keyd.storage = KEY_STORAGE_SOLVABLE;
- if (keyfilter)
- {
- keyd.storage = keyfilter(repo, &keyd, kfdata);
- if (keyd.storage == KEY_STORAGE_DROPPED)
- continue;
- keyd.storage = KEY_STORAGE_SOLVABLE;
- }
- poolusage = 1;
- clonepool = 1;
- cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
- }
-
- if (repo->nsolvables)
- {
- Repokey keyd;
- keyd.name = REPOSITORY_SOLVABLES;
- keyd.type = REPOKEY_TYPE_FLEXARRAY;
- keyd.size = 0;
- keyd.storage = KEY_STORAGE_INCORE;
- cbdata.keymap[keyd.name] = repodata_key2id(&target, &keyd, 1);
- }
-
- dirpoolusage = 0;
-
- spool = 0;
- dirpool = 0;
- dirpooldata = 0;
- n = ID_NUM_INTERNAL;
- lastfilelistn = 0;
- FOR_REPODATAS(repo, i, data)
- {
- cbdata.keymapstart[i] = n;
- cbdata.keymap[n++] = 0; /* key 0 */
- idused = 0;
- dirused = 0;
- if (keyfilter)
- {
- Repokey keyd;
- /* check if we want this repodata */
- memset(&keyd, 0, sizeof(keyd));
- keyd.name = 1;
- keyd.type = 1;
- keyd.size = i;
- if (keyfilter(repo, &keyd, kfdata) == -1)
- continue;
- }
- for (j = 1; j < data->nkeys; j++, n++)
- {
- key = data->keys + j;
- if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
- {
- cbdata.keymap[n] = cbdata.keymap[key->name];
- continue;
- }
- if (key->type == REPOKEY_TYPE_DELETED)
- {
- cbdata.keymap[n] = 0;
- continue;
- }
- if (key->type == REPOKEY_TYPE_CONSTANTID && data->localpool)
- {
- Repokey keyd = *key;
- keyd.size = repodata_globalize_id(data, key->size, 1);
- id = repodata_key2id(&target, &keyd, 0);
- }
- else
- id = repodata_key2id(&target, key, 0);
- if (!id)
- {
- Repokey keyd = *key;
- keyd.storage = KEY_STORAGE_INCORE;
- if (keyd.type == REPOKEY_TYPE_CONSTANTID)
- keyd.size = repodata_globalize_id(data, key->size, 1);
- else if (keyd.type != REPOKEY_TYPE_CONSTANT)
- keyd.size = 0;
- if (keyfilter)
- {
- keyd.storage = keyfilter(repo, &keyd, kfdata);
- if (keyd.storage == KEY_STORAGE_DROPPED)
- {
- cbdata.keymap[n] = 0;
- continue;
- }
- }
- id = repodata_key2id(&target, &keyd, 1);
- }
- cbdata.keymap[n] = id;
- /* load repodata if not already loaded */
- if (data->state == REPODATA_STUB)
- {
- if (data->loadcallback)
- data->loadcallback(data);
- else
- data->state = REPODATA_ERROR;
- if (data->state != REPODATA_ERROR)
- {
- /* redo this repodata! */
- j = 0;
- n = cbdata.keymapstart[i];
- continue;
- }
- }
- if (data->state == REPODATA_ERROR)
- {
- /* too bad! */
- cbdata.keymap[n] = 0;
- continue;
- }
-
- repodataused[i] = 1;
- anyrepodataused = 1;
- if (key->type == REPOKEY_TYPE_CONSTANTID || key->type == REPOKEY_TYPE_ID ||
- key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
- idused = 1;
- else if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
- {
- idused = 1; /* dirs also use ids */
- dirused = 1;
- }
- if (key->type == REPOKEY_TYPE_DIRSTRARRAY && key->name == SOLVABLE_FILELIST)
- {
- /* is this a file list extension */
- if (is_filelist_extension(data))
- {
- /* hmm, we have a file list extension. Kill filelist of other repodata.
- * XXX: this is wrong, as the extension does not need to cover all
- * solvables of the other repodata */
- if (lastfilelistn)
- cbdata.keymap[lastfilelistn] = 0;
- }
- else
- lastfilelistn = n;
- }
- }
- if (idused)
- {
- if (data->localpool)
- {
- if (poolusage)
- poolusage = 3; /* need own pool */
- else
- {
- poolusage = 2;
- spool = &data->spool;
- }
- }
- else
- {
- if (poolusage == 0)
- poolusage = 1;
- else if (poolusage != 1)
- poolusage = 3; /* need own pool */
- }
- }
- if (dirused)
- {
- if (dirpoolusage)
- dirpoolusage = 3; /* need own dirpool */
- else
- {
- dirpoolusage = 2;
- dirpool = &data->dirpool;
- dirpooldata = data;
- }
- }
- }
- cbdata.nkeymap = n;
-
- /* 0: no pool needed at all */
- /* 1: use global pool */
- /* 2: use repodata local pool */
- /* 3: need own pool */
- if (poolusage == 3)
- {
- spool = &target.spool;
- /* hack: reuse global pool data so we don't have to map pool ids */
- if (clonepool)
- {
- stringpool_free(spool);
- stringpool_clone(spool, &pool->ss);
- }
- cbdata.ownspool = spool;
- }
- else if (poolusage == 0 || poolusage == 1)
- {
- poolusage = 1;
- spool = &pool->ss;
- }
-
- if (dirpoolusage == 3)
- {
- dirpool = &target.dirpool;
- dirpooldata = 0;
- cbdata.owndirpool = dirpool;
- }
- else if (dirpool)
- cbdata.dirused = solv_calloc(dirpool->ndirs, sizeof(Id));
-
-
-/********************************************************************/
-#if 0
-fprintf(stderr, "poolusage: %d\n", poolusage);
-fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
-fprintf(stderr, "nkeys: %d\n", target.nkeys);
-for (i = 1; i < target.nkeys; i++)
- fprintf(stderr, " %2d: %s[%d] %d %d %d\n", i, pool_id2str(pool, target.keys[i].name), target.keys[i].name, target.keys[i].type, target.keys[i].size, target.keys[i].storage);
-#endif
-
- /* copy keys if requested */
- if (keyq)
- {
- queue_empty(keyq);
- for (i = 1; i < target.nkeys; i++)
- queue_push2(keyq, target.keys[i].name, target.keys[i].type);
- }
-
- if (poolusage > 1)
- {
- /* put all the keys we need in our string pool */
- /* put mapped ids right into target.keys */
- for (i = 1, key = target.keys + i; i < target.nkeys; i++, key++)
- {
- key->name = stringpool_str2id(spool, pool_id2str(pool, key->name), 1);
- if (key->type == REPOKEY_TYPE_CONSTANTID)
- {
- key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
- type_constantid = key->type;
- key->size = stringpool_str2id(spool, pool_id2str(pool, key->size), 1);
- }
- else
- key->type = stringpool_str2id(spool, pool_id2str(pool, key->type), 1);
- }
- if (poolusage == 2)
- stringpool_freehash(spool); /* free some mem */
- }
-
-
-/********************************************************************/
-
- /* set needed count of all strings and rels,
- * find which keys are used in the solvables
- * put all strings in own spool
- */
-
- reloff = spool->nstrings;
- if (poolusage == 3)
- reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
-
- needid = calloc(reloff + pool->nrels, sizeof(*needid));
- needid[0].map = reloff;
-
- cbdata.needid = needid;
- cbdata.schema = solv_calloc(target.nkeys, sizeof(Id));
- cbdata.sp = cbdata.schema;
- cbdata.solvschemata = solv_calloc(repo->nsolvables, sizeof(Id));
-
- /* create main schema */
- cbdata.sp = cbdata.schema;
- /* collect all other data from all repodatas */
- /* XXX: merge arrays of equal keys? */
- FOR_REPODATAS(repo, j, data)
- {
- if (!repodataused[j])
- continue;
- repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
- }
- sp = cbdata.sp;
- /* add solvables if needed (may revert later) */
- if (repo->nsolvables)
- {
- *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
- target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
- }
- *sp = 0;
- mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
-
- idarraydata = repo->idarraydata;
-
- anysolvableused = 0;
- cbdata.doingsolvables = 1;
- for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
- {
- if (s->repo != repo)
- continue;
-
- /* set schema info, keep in sync with further down */
- sp = cbdata.schema;
- if (cbdata.keymap[SOLVABLE_NAME])
- {
- *sp++ = cbdata.keymap[SOLVABLE_NAME];
- needid[s->name].need++;
- }
- if (cbdata.keymap[SOLVABLE_ARCH])
- {
- *sp++ = cbdata.keymap[SOLVABLE_ARCH];
- needid[s->arch].need++;
- }
- if (cbdata.keymap[SOLVABLE_EVR])
- {
- *sp++ = cbdata.keymap[SOLVABLE_EVR];
- needid[s->evr].need++;
- }
- if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
- {
- *sp++ = cbdata.keymap[SOLVABLE_VENDOR];
- needid[s->vendor].need++;
- }
- if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
- {
- *sp++ = cbdata.keymap[SOLVABLE_PROVIDES];
- target.keys[cbdata.keymap[SOLVABLE_PROVIDES]].size += incneedidarray(pool, idarraydata + s->provides, needid);
- }
- if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
- {
- *sp++ = cbdata.keymap[SOLVABLE_OBSOLETES];
- target.keys[cbdata.keymap[SOLVABLE_OBSOLETES]].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
- }
- if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
- {
- *sp++ = cbdata.keymap[SOLVABLE_CONFLICTS];
- target.keys[cbdata.keymap[SOLVABLE_CONFLICTS]].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
- }
- if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
- {
- *sp++ = cbdata.keymap[SOLVABLE_REQUIRES];
- target.keys[cbdata.keymap[SOLVABLE_REQUIRES]].size += incneedidarray(pool, idarraydata + s->requires, needid);
- }
- if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
- {
- *sp++ = cbdata.keymap[SOLVABLE_RECOMMENDS];
- target.keys[cbdata.keymap[SOLVABLE_RECOMMENDS]].size += incneedidarray(pool, idarraydata + s->recommends, needid);
- }
- if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
- {
- *sp++ = cbdata.keymap[SOLVABLE_SUGGESTS];
- target.keys[cbdata.keymap[SOLVABLE_SUGGESTS]].size += incneedidarray(pool, idarraydata + s->suggests, needid);
- }
- if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
- {
- *sp++ = cbdata.keymap[SOLVABLE_SUPPLEMENTS];
- target.keys[cbdata.keymap[SOLVABLE_SUPPLEMENTS]].size += incneedidarray(pool, idarraydata + s->supplements, needid);
- }
- if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
- {
- *sp++ = cbdata.keymap[SOLVABLE_ENHANCES];
- target.keys[cbdata.keymap[SOLVABLE_ENHANCES]].size += incneedidarray(pool, idarraydata + s->enhances, needid);
- }
- if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
- {
- *sp++ = cbdata.keymap[RPM_RPMDBID];
- target.keys[cbdata.keymap[RPM_RPMDBID]].size++;
- }
- cbdata.sp = sp;
-
- if (anyrepodataused)
- {
- FOR_REPODATAS(repo, j, data)
- {
- if (!repodataused[j])
- continue;
- if (i < data->start || i >= data->end)
- continue;
- repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
- needid = cbdata.needid;
- }
- }
- *cbdata.sp = 0;
- cbdata.solvschemata[n] = repodata_schema2id(cbdata.target, cbdata.schema, 1);
- if (cbdata.solvschemata[n])
- anysolvableused = 1;
- n++;
- }
- cbdata.doingsolvables = 0;
- assert(n == repo->nsolvables);
-
- if (repo->nsolvables && !anysolvableused)
- {
- /* strip off solvable from the main schema */
- target.keys[cbdata.keymap[REPOSITORY_SOLVABLES]].size = 0;
- sp = cbdata.schema;
- for (i = 0; target.schemadata[target.schemata[mainschema] + i]; i++)
- {
- *sp = target.schemadata[target.schemata[mainschema] + i];
- if (*sp != cbdata.keymap[REPOSITORY_SOLVABLES])
- sp++;
- }
- assert(target.schemadatalen == target.schemata[mainschema] + i + 1);
- *sp = 0;
- target.schemadatalen = target.schemata[mainschema];
- target.nschemata--;
- repodata_free_schemahash(&target);
- mainschema = repodata_schema2id(cbdata.target, cbdata.schema, 1);
- }
-
-/********************************************************************/
-
- /* remove unused keys */
- keyused = solv_calloc(target.nkeys, sizeof(Id));
- for (i = 1; i < (int)target.schemadatalen; i++)
- keyused[target.schemadata[i]] = 1;
- keyused[0] = 0;
- for (n = i = 1; i < target.nkeys; i++)
- {
- if (!keyused[i])
- continue;
- keyused[i] = n;
- if (i != n)
- {
- target.keys[n] = target.keys[i];
- if (keyq)
- {
- keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
- keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
- }
- }
- n++;
- }
- target.nkeys = n;
- if (keyq)
- queue_truncate(keyq, 2 * n - 2);
-
- /* update schema data to the new key ids */
- for (i = 1; i < (int)target.schemadatalen; i++)
- target.schemadata[i] = keyused[target.schemadata[i]];
- /* update keymap to the new key ids */
- for (i = 0; i < cbdata.nkeymap; i++)
- cbdata.keymap[i] = keyused[cbdata.keymap[i]];
- keyused = solv_free(keyused);
-
- /* increment needid of the used keys, they are already mapped to
- * the correct string pool */
- for (i = 1; i < target.nkeys; i++)
- {
- if (target.keys[i].type == type_constantid)
- needid[target.keys[i].size].need++;
- needid[target.keys[i].name].need++;
- needid[target.keys[i].type].need++;
- }
-
-/********************************************************************/
-
- if (dirpool && cbdata.dirused && !cbdata.dirused[0])
- {
- /* no dirs used at all */
- cbdata.dirused = solv_free(cbdata.dirused);
- dirpool = 0;
- }
-
- /* increment need id for used dir components */
- if (dirpool)
- {
- /* if we have own dirpool, all entries in it are used.
- also, all comp ids are already mapped by putinowndirpool(),
- so we can simply increment needid.
- (owndirpool != 0, dirused == 0, dirpooldata == 0) */
- /* else we re-use a dirpool of repodata "dirpooldata".
- dirused tells us which of the ids are used.
- we need to map comp ids if we generate a new pool.
- (owndirpool == 0, dirused != 0, dirpooldata != 0) */
- for (i = 1; i < dirpool->ndirs; i++)
- {
-#if 0
-fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
-#endif
- if (cbdata.dirused && !cbdata.dirused[i])
- continue;
- id = dirpool->dirs[i];
- if (id <= 0)
- continue;
- if (dirpooldata && cbdata.ownspool && id > 1)
- {
- id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
- needid = cbdata.needid;
- }
- needid[id].need++;
- }
- }
-
-
-/********************************************************************/
-
- /*
- * create mapping table, new keys are sorted by needid[].need
- *
- * needid[key].need : old key -> new key
- * needid[key].map : new key -> old key
- */
-
- /* zero out id 0 and rel 0 just in case */
- reloff = needid[0].map;
- needid[0].need = 0;
- needid[reloff].need = 0;
-
- for (i = 1; i < reloff + pool->nrels; i++)
- needid[i].map = i;
-
-#if 0
- solv_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
-#else
- /* make first entry '' */
- needid[1].need = 1;
- solv_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
-#endif
- solv_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
- /* now needid is in new order, needid[newid].map -> oldid */
-
- /* calculate string space size, also zero out needid[].need */
- sizeid = 0;
- for (i = 1; i < reloff; i++)
- {
- if (!needid[i].need)
- break; /* as we have sorted, every entry after this also has need == 0 */
- needid[i].need = 0;
- sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
- }
- nstrings = i; /* our new string id end */
-
- /* make needid[oldid].need point to newid */
- for (i = 1; i < nstrings; i++)
- needid[needid[i].map].need = i;
-
- /* same as above for relations */
- for (i = 0; i < pool->nrels; i++)
- {
- if (!needid[reloff + i].need)
- break;
- needid[reloff + i].need = 0;
- }
- nrels = i; /* our new rel id end */
-
- for (i = 0; i < nrels; i++)
- needid[needid[reloff + i].map].need = nstrings + i;
-
- /* now we have: needid[oldid].need -> newid
- needid[newid].map -> oldid
- both for strings and relations */
-
-
-/********************************************************************/
-
- ndirmap = 0;
- dirmap = 0;
- if (dirpool)
- {
- /* create our new target directory structure by traversing through all
- * used dirs. This will concatenate blocks with the same parent
- * directory into single blocks.
- * Instead of components, traverse_dirs stores the old dirids,
- * we will change this in the second step below */
- /* (dirpooldata and dirused are 0 if we have our own dirpool) */
- if (cbdata.dirused && !cbdata.dirused[1])
- cbdata.dirused[1] = 1; /* always want / entry */
- dirmap = solv_calloc(dirpool->ndirs, sizeof(Id));
- dirmap[0] = 0;
- ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
-
- /* (re)create dirused, so that it maps from "old dirid" to "new dirid" */
- /* change dirmap so that it maps from "new dirid" to "new compid" */
- if (!cbdata.dirused)
- cbdata.dirused = solv_malloc2(dirpool->ndirs, sizeof(Id));
- memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
- for (i = 1; i < ndirmap; i++)
- {
- if (dirmap[i] <= 0)
- continue;
- cbdata.dirused[dirmap[i]] = i;
- id = dirpool->dirs[dirmap[i]];
- if (dirpooldata && cbdata.ownspool && id > 1)
- id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
- dirmap[i] = needid[id].need;
- }
- /* now the new target directory structure is complete (dirmap), and we have
- * dirused[olddirid] -> newdirid */
- }
-
-/********************************************************************/
-
- /* collect all data
- * we use extdata[0] for incore data and extdata[keyid] for vertical data
- */
-
- cbdata.extdata = solv_calloc(target.nkeys, sizeof(struct extdata));
-
- xd = cbdata.extdata;
- cbdata.current_sub = 0;
- /* add main schema */
- cbdata.lastlen = 0;
- data_addid(xd, mainschema);
-
-#if 1
- FOR_REPODATAS(repo, j, data)
- {
- if (!repodataused[j])
- continue;
- repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
- }
-#endif
-
- if (xd->len - cbdata.lastlen > cbdata.maxdata)
- cbdata.maxdata = xd->len - cbdata.lastlen;
- cbdata.lastlen = xd->len;
-
- if (anysolvableused)
- {
- data_addid(xd, repo->nsolvables); /* FLEXARRAY nentries */
- cbdata.doingsolvables = 1;
-
- /* check if we can do the special filelist memory optimization */
- if (anyrepodataused)
- {
- for (i = 1; i < target.nkeys; i++)
- if (target.keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
- cbdata.filelistmode |= cbdata.filelistmode == 0 && target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY ? 1 : 2;
- else if (target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY)
- cbdata.filelistmode = 2;
- if (cbdata.filelistmode != 1)
- cbdata.filelistmode = 0;
- }
-
- for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
- {
- if (s->repo != repo)
- continue;
- data_addid(xd, cbdata.solvschemata[n]);
- if (cbdata.keymap[SOLVABLE_NAME])
- data_addid(xd, needid[s->name].need);
- if (cbdata.keymap[SOLVABLE_ARCH])
- data_addid(xd, needid[s->arch].need);
- if (cbdata.keymap[SOLVABLE_EVR])
- data_addid(xd, needid[s->evr].need);
- if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
- data_addid(xd, needid[s->vendor].need);
- if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
- data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
- if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
- data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
- if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
- data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
- if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
- data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
- if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
- data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
- if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
- data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
- if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
- data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
- if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
- data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
- if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
- data_addid(xd, repo->rpmdbid[i - repo->start]);
- if (anyrepodataused)
- {
- cbdata.vstart = -1;
- FOR_REPODATAS(repo, j, data)
- {
- if (!repodataused[j])
- continue;
- if (i < data->start || i >= data->end)
- continue;
- repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
- }
- }
- if (xd->len - cbdata.lastlen > cbdata.maxdata)
- cbdata.maxdata = xd->len - cbdata.lastlen;
- cbdata.lastlen = xd->len;
- n++;
- }
- cbdata.doingsolvables = 0;
- }
-
- assert(cbdata.current_sub == cbdata.nsubschemata);
- if (cbdata.subschemata)
- {
- cbdata.subschemata = solv_free(cbdata.subschemata);
- cbdata.nsubschemata = 0;
- }
-
-/********************************************************************/
-
- target.fp = fp;
-
- /* write header */
-
- /* write file header */
- write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
- write_u32(&target, SOLV_VERSION_8);
-
-
- /* write counts */
- write_u32(&target, nstrings);
- write_u32(&target, nrels);
- write_u32(&target, ndirmap);
- write_u32(&target, anysolvableused ? repo->nsolvables : 0);
- write_u32(&target, target.nkeys);
- write_u32(&target, target.nschemata);
- solv_flags = 0;
- solv_flags |= SOLV_FLAG_PREFIX_POOL;
- solv_flags |= SOLV_FLAG_SIZE_BYTES;
- write_u32(&target, solv_flags);
-
- if (nstrings)
- {
- /*
- * calculate prefix encoding of the strings
- */
- unsigned char *prefixcomp = solv_malloc(nstrings);
- unsigned int compsum = 0;
- char *old_str = "";
-
- prefixcomp[0] = 0;
- for (i = 1; i < nstrings; i++)
- {
- char *str = spool->stringspace + spool->strings[needid[i].map];
- int same;
- for (same = 0; same < 255; same++)
- if (!old_str[same] || old_str[same] != str[same])
- break;
- prefixcomp[i] = same;
- compsum += same;
- old_str = str;
- }
-
- /*
- * write strings
- */
- write_u32(&target, sizeid);
- /* we save compsum bytes but need 1 extra byte for every string */
- write_u32(&target, sizeid + nstrings - 1 - compsum);
- for (i = 1; i < nstrings; i++)
- {
- char *str = spool->stringspace + spool->strings[needid[i].map];
- write_u8(&target, prefixcomp[i]);
- write_str(&target, str + prefixcomp[i]);
- }
- solv_free(prefixcomp);
- }
- else
- {
- write_u32(&target, 0);
- write_u32(&target, 0);
- }
-
- /*
- * write RelDeps
- */
- for (i = 0; i < nrels; i++)
- {
- ran = pool->rels + (needid[reloff + i].map - reloff);
- write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
- write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
- write_u8(&target, ran->flags);
- }
-
- /*
- * write dirs (skip both root and / entry)
- */
- for (i = 2; i < ndirmap; i++)
- {
- if (dirmap[i] > 0)
- write_id(&target, dirmap[i]);
- else
- write_id(&target, nstrings - dirmap[i]);
- }
- solv_free(dirmap);
-
- /*
- * write keys
- */
- for (i = 1; i < target.nkeys; i++)
- {
- write_id(&target, needid[target.keys[i].name].need);
- write_id(&target, needid[target.keys[i].type].need);
- if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
- {
- if (target.keys[i].type == type_constantid)
- write_id(&target, needid[target.keys[i].size].need);
- else
- write_id(&target, target.keys[i].size);
- }
- else
- write_id(&target, cbdata.extdata[i].len);
- write_id(&target, target.keys[i].storage);
- }
-
- /*
- * write schemata
- */
- write_id(&target, target.schemadatalen); /* XXX -1? */
- for (i = 1; i < target.nschemata; i++)
- write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
-
-/********************************************************************/
-
- write_id(&target, cbdata.maxdata);
- write_id(&target, cbdata.extdata[0].len);
- if (cbdata.extdata[0].len)
- write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
- solv_free(cbdata.extdata[0].buf);
-
- /* do we have vertical data? */
- for (i = 1; i < target.nkeys; i++)
- if (cbdata.extdata[i].len)
- break;
- if (i < target.nkeys)
- {
- /* yes, write it in pages */
- unsigned char vpage[REPOPAGE_BLOBSIZE];
- int lpage = 0;
-
- write_u32(&target, REPOPAGE_BLOBSIZE);
- for (i = 1; i < target.nkeys; i++)
- if (cbdata.extdata[i].len)
- {
- if (cbdata.filelistmode)
- break;
- lpage = write_compressed_extdata(&target, cbdata.extdata + i, vpage, lpage);
- }
- if (cbdata.filelistmode && i < target.nkeys)
- {
- /* ok, just this single extdata, which is a filelist */
- xd = cbdata.extdata + i;
- xd->len = 0;
- cbdata.filelistmode = -1;
- for (j = 0; j < cbdata.nkeymap; j++)
- if (cbdata.keymap[j] != i)
- cbdata.keymap[j] = 0;
- for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
- {
- if (s->repo != repo)
- continue;
- FOR_REPODATAS(repo, j, data)
- {
- if (!repodataused[j])
- continue;
- if (i < data->start || i >= data->end)
- continue;
- repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
- }
- if (xd->len > 1024 * 1024)
- {
- lpage = write_compressed_extdata(&target, xd, vpage, lpage);
- xd->len = 0;
- }
- }
- if (xd->len)
- lpage = write_compressed_extdata(&target, xd, vpage, lpage);
- }
- if (lpage)
- write_compressed_page(&target, vpage, lpage);
- }
-
- for (i = 1; i < target.nkeys; i++)
- solv_free(cbdata.extdata[i].buf);
- solv_free(cbdata.extdata);
-
- target.fp = 0;
- repodata_freedata(&target);
-
- solv_free(needid);
- solv_free(cbdata.solvschemata);
- solv_free(cbdata.schema);
-
- solv_free(cbdata.keymap);
- solv_free(cbdata.keymapstart);
- solv_free(cbdata.dirused);
- solv_free(repodataused);
- return target.error;
-}
-
-struct repodata_write_data {
- int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
- void *kfdata;
- int repodataid;
-};
-
-static int
-repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
-{
- struct repodata_write_data *wd = kfdata;
-
- /* XXX: special repodata selection hack */
- if (key->name == 1 && key->size != wd->repodataid)
- return -1;
- if (key->storage == KEY_STORAGE_SOLVABLE)
- return KEY_STORAGE_DROPPED; /* not part of this repodata */
- if (wd->keyfilter)
- return (*wd->keyfilter)(repo, key, wd->kfdata);
- return key->storage;
-}
-
-int
-repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
-{
- struct repodata_write_data wd;
-
- wd.keyfilter = keyfilter;
- wd.kfdata = kfdata;
- wd.repodataid = data->repodataid;
- return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
-}
-
-int
-repodata_write(Repodata *data, FILE *fp)
-{
- return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
-}
-
-int
-repo_write(Repo *repo, FILE *fp)
-{
- return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repo_write.h
- *
- */
-
-#ifndef REPO_WRITE_H
-#define REPO_WRITE_H
-
-#include <stdio.h>
-
-#include "repo.h"
-#include "queue.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern int repo_write(Repo *repo, FILE *fp);
-extern int repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq);
-
-extern int repodata_write(Repodata *data , FILE *fp);
-extern int repodata_write_filtered(Repodata *data , FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq);
-
-extern int repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repodata.c
- *
- * Manage data coming from one repository
- *
- * a repository can contain multiple repodata entries, consisting of
- * different sets of keys and different sets of solvables
- */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <fnmatch.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <assert.h>
-#include <regex.h>
-
-#include "repo.h"
-#include "pool.h"
-#include "poolid_private.h"
-#include "util.h"
-#include "hash.h"
-#include "chksum.h"
-
-#include "repopack.h"
-#include "repopage.h"
-
-#define REPODATA_BLOCK 255
-
-static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
-
-void
-repodata_initdata(Repodata *data, Repo *repo, int localpool)
-{
- memset(data, 0, sizeof (*data));
- data->repodataid = data - repo->repodata;
- data->repo = repo;
- data->localpool = localpool;
- if (localpool)
- stringpool_init_empty(&data->spool);
- /* dirpool_init(&data->dirpool); just zeros out again */
- data->keys = solv_calloc(1, sizeof(Repokey));
- data->nkeys = 1;
- data->schemata = solv_calloc(1, sizeof(Id));
- data->schemadata = solv_calloc(1, sizeof(Id));
- data->nschemata = 1;
- data->schemadatalen = 1;
- repopagestore_init(&data->store);
-}
-
-void
-repodata_freedata(Repodata *data)
-{
- int i;
-
- solv_free(data->keys);
-
- solv_free(data->schemata);
- solv_free(data->schemadata);
- solv_free(data->schematahash);
-
- stringpool_free(&data->spool);
- dirpool_free(&data->dirpool);
-
- solv_free(data->mainschemaoffsets);
- solv_free(data->incoredata);
- solv_free(data->incoreoffset);
- solv_free(data->verticaloffset);
-
- repopagestore_free(&data->store);
-
- solv_free(data->vincore);
-
- if (data->attrs)
- for (i = 0; i < data->end - data->start; i++)
- solv_free(data->attrs[i]);
- solv_free(data->attrs);
- if (data->xattrs)
- for (i = 0; i < data->nxattrs; i++)
- solv_free(data->xattrs[i]);
- solv_free(data->xattrs);
-
- solv_free(data->attrdata);
- solv_free(data->attriddata);
- solv_free(data->attrnum64data);
-
- solv_free(data->dircache);
-}
-
-void
-repodata_free(Repodata *data)
-{
- Repo *repo = data->repo;
- int i = data - repo->repodata;
- if (i == 0)
- return;
- repodata_freedata(data);
- if (i < repo->nrepodata - 1)
- {
- /* whoa! this changes the repodataids! */
- memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
- for (; i < repo->nrepodata - 1; i++)
- repo->repodata[i].repodataid = i;
- }
- repo->nrepodata--;
- if (repo->nrepodata == 1)
- {
- repo->repodata = solv_free(repo->repodata);
- repo->nrepodata = 0;
- }
-}
-
-void
-repodata_empty(Repodata *data, int localpool)
-{
- void (*loadcallback)(Repodata *) = data->loadcallback;
- int state = data->state;
- repodata_freedata(data);
- repodata_initdata(data, data->repo, localpool);
- data->state = state;
- data->loadcallback = loadcallback;
-}
-
-
-/***************************************************************
- * key pool management
- */
-
-/* this is not so time critical that we need a hash, so we do a simple
- * linear search */
-Id
-repodata_key2id(Repodata *data, Repokey *key, int create)
-{
- Id keyid;
-
- for (keyid = 1; keyid < data->nkeys; keyid++)
- if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
- {
- if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
- continue;
- break;
- }
- if (keyid == data->nkeys)
- {
- if (!create)
- return 0;
- /* allocate new key */
- data->keys = solv_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
- data->keys[data->nkeys++] = *key;
- if (data->verticaloffset)
- {
- data->verticaloffset = solv_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
- data->verticaloffset[data->nkeys - 1] = 0;
- }
- data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
- }
- return keyid;
-}
-
-
-/***************************************************************
- * schema pool management
- */
-
-#define SCHEMATA_BLOCK 31
-#define SCHEMATADATA_BLOCK 255
-
-Id
-repodata_schema2id(Repodata *data, Id *schema, int create)
-{
- int h, len, i;
- Id *sp, cid;
- Id *schematahash;
-
- if (!*schema)
- return 0; /* XXX: allow empty schema? */
- if ((schematahash = data->schematahash) == 0)
- {
- data->schematahash = schematahash = solv_calloc(256, sizeof(Id));
- for (i = 1; i < data->nschemata; i++)
- {
- for (sp = data->schemadata + data->schemata[i], h = 0; *sp;)
- h = h * 7 + *sp++;
- h &= 255;
- schematahash[h] = i;
- }
- data->schemadata = solv_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
- data->schemata = solv_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
- }
-
- for (sp = schema, len = 0, h = 0; *sp; len++)
- h = h * 7 + *sp++;
- h &= 255;
- len++;
-
- cid = schematahash[h];
- if (cid)
- {
- if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
- return cid;
- /* cache conflict, do a slow search */
- for (cid = 1; cid < data->nschemata; cid++)
- if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
- return cid;
- }
- /* a new one */
- if (!create)
- return 0;
- data->schemadata = solv_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
- data->schemata = solv_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
- /* add schema */
- memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
- data->schemata[data->nschemata] = data->schemadatalen;
- data->schemadatalen += len;
- schematahash[h] = data->nschemata;
-#if 0
-fprintf(stderr, "schema2id: new schema\n");
-#endif
- return data->nschemata++;
-}
-
-void
-repodata_free_schemahash(Repodata *data)
-{
- data->schematahash = solv_free(data->schematahash);
- /* shrink arrays */
- data->schemata = solv_realloc2(data->schemata, data->nschemata, sizeof(Id));
- data->schemadata = solv_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
-}
-
-
-/***************************************************************
- * dir pool management
- */
-
-#ifndef HAVE_STRCHRNUL
-static inline const char *strchrnul(const char *str, char x)
-{
- const char *p = strchr(str, x);
- return p ? p : str + strlen(str);
-}
-#endif
-
-#define DIRCACHE_SIZE 41 /* < 1k */
-
-#ifdef DIRCACHE_SIZE
-struct dircache {
- Id ids[DIRCACHE_SIZE];
- char str[(DIRCACHE_SIZE * (DIRCACHE_SIZE - 1)) / 2];
-};
-#endif
-
-Id
-repodata_str2dir(Repodata *data, const char *dir, int create)
-{
- Id id, parent;
-#ifdef DIRCACHE_SIZE
- const char *dirs;
-#endif
- const char *dire;
-
- parent = 0;
- if (!*dir)
- return 0;
- while (*dir == '/' && dir[1] == '/')
- dir++;
- if (*dir == '/' && !dir[1])
- {
- if (data->dirpool.ndirs)
- return 1;
- return dirpool_add_dir(&data->dirpool, 0, 1, create);
- }
-#ifdef DIRCACHE_SIZE
- dirs = dir;
- if (data->dircache)
- {
- int l;
- struct dircache *dircache = data->dircache;
- l = strlen(dir);
- while (l > 0)
- {
- if (l < DIRCACHE_SIZE && dircache->ids[l] && !memcmp(dircache->str + l * (l - 1) / 2, dir, l))
- {
- parent = dircache->ids[l];
- dir += l;
- if (!*dir)
- return parent;
- while (*dir == '/')
- dir++;
- break;
- }
- while (--l)
- if (dir[l] == '/')
- break;
- }
- }
-#endif
- while (*dir)
- {
- dire = strchrnul(dir, '/');
- if (data->localpool)
- id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
- else
- id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
- if (!id)
- return 0;
- parent = dirpool_add_dir(&data->dirpool, parent, id, create);
- if (!parent)
- return 0;
-#ifdef DIRCACHE_SIZE
- if (!data->dircache)
- data->dircache = solv_calloc(1, sizeof(struct dircache));
- if (data->dircache)
- {
- int l = dire - dirs;
- if (l < DIRCACHE_SIZE)
- {
- data->dircache->ids[l] = parent;
- memcpy(data->dircache->str + l * (l - 1) / 2, dirs, l);
- }
- }
-#endif
- if (!*dire)
- break;
- dir = dire + 1;
- while (*dir == '/')
- dir++;
- }
- return parent;
-}
-
-void
-repodata_free_dircache(Repodata *data)
-{
- data->dircache = solv_free(data->dircache);
-}
-
-const char *
-repodata_dir2str(Repodata *data, Id did, const char *suf)
-{
- Pool *pool = data->repo->pool;
- int l = 0;
- Id parent, comp;
- const char *comps;
- char *p;
-
- if (!did)
- return suf ? suf : "";
- parent = did;
- while (parent)
- {
- comp = dirpool_compid(&data->dirpool, parent);
- comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
- l += strlen(comps);
- parent = dirpool_parent(&data->dirpool, parent);
- if (parent)
- l++;
- }
- if (suf)
- l += strlen(suf) + 1;
- p = pool_alloctmpspace(pool, l + 1) + l;
- *p = 0;
- if (suf)
- {
- p -= strlen(suf);
- strcpy(p, suf);
- *--p = '/';
- }
- parent = did;
- while (parent)
- {
- comp = dirpool_compid(&data->dirpool, parent);
- comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
- l = strlen(comps);
- p -= l;
- strncpy(p, comps, l);
- parent = dirpool_parent(&data->dirpool, parent);
- if (parent)
- *--p = '/';
- }
- return p;
-}
-
-
-/***************************************************************
- * data management
- */
-
-static inline unsigned char *
-data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
-{
- Id *keyp = data->schemadata + data->schemata[schema];
- for (; *keyp; keyp++)
- dp = data_skip_key(data, dp, data->keys + *keyp);
- return dp;
-}
-
-static unsigned char *
-data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
-{
- int nentries, schema;
- switch(key->type)
- {
- case REPOKEY_TYPE_FIXARRAY:
- dp = data_read_id(dp, &nentries);
- if (!nentries)
- return dp;
- dp = data_read_id(dp, &schema);
- while (nentries--)
- dp = data_skip_schema(data, dp, schema);
- return dp;
- case REPOKEY_TYPE_FLEXARRAY:
- dp = data_read_id(dp, &nentries);
- while (nentries--)
- {
- dp = data_read_id(dp, &schema);
- dp = data_skip_schema(data, dp, schema);
- }
- return dp;
- default:
- if (key->storage == KEY_STORAGE_INCORE)
- dp = data_skip(dp, key->type);
- else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- dp = data_skip(dp, REPOKEY_TYPE_ID);
- dp = data_skip(dp, REPOKEY_TYPE_ID);
- }
- return dp;
- }
-}
-
-static unsigned char *
-forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
-{
- Id k;
-
- if (!keyid)
- return 0;
- if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
- {
- int i;
- for (i = 0; (k = *keyp++) != 0; i++)
- if (k == keyid)
- return data->incoredata + data->mainschemaoffsets[i];
- return 0;
- }
- while ((k = *keyp++) != 0)
- {
- if (k == keyid)
- return dp;
- if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip offset */
- dp = data_skip(dp, REPOKEY_TYPE_ID); /* skip length */
- continue;
- }
- if (data->keys[k].storage != KEY_STORAGE_INCORE)
- continue;
- dp = data_skip_key(data, dp, data->keys + k);
- }
- return 0;
-}
-
-static unsigned char *
-get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
-{
- unsigned char *dp;
- if (len <= 0)
- return 0;
- if (off >= data->lastverticaloffset)
- {
- off -= data->lastverticaloffset;
- if ((unsigned int)off + len > data->vincorelen)
- return 0;
- return data->vincore + off;
- }
- if ((unsigned int)off + len > key->size)
- return 0;
- /* we now have the offset, go into vertical */
- off += data->verticaloffset[key - data->keys];
- /* fprintf(stderr, "key %d page %d\n", key->name, off / REPOPAGE_BLOBSIZE); */
- dp = repopagestore_load_page_range(&data->store, off / REPOPAGE_BLOBSIZE, (off + len - 1) / REPOPAGE_BLOBSIZE);
- data->storestate++;
- if (dp)
- dp += off % REPOPAGE_BLOBSIZE;
- return dp;
-}
-
-static inline unsigned char *
-get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
-{
- unsigned char *dp = *dpp;
-
- if (!dp)
- return 0;
- if (key->storage == KEY_STORAGE_INCORE)
- {
- if (advance)
- *dpp = data_skip_key(data, dp, key);
- return dp;
- }
- else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- Id off, len;
- dp = data_read_id(dp, &off);
- dp = data_read_id(dp, &len);
- if (advance)
- *dpp = dp;
- return get_vertical_data(data, key, off, len);
- }
- return 0;
-}
-
-static int
-load_repodata(Repodata *data)
-{
- if (data->loadcallback)
- {
- data->loadcallback(data);
- if (data->state == REPODATA_AVAILABLE)
- return 1;
- }
- data->state = REPODATA_ERROR;
- return 0;
-}
-
-static inline int
-maybe_load_repodata(Repodata *data, Id keyname)
-{
- if (keyname && !repodata_precheck_keyname(data, keyname))
- return 0; /* do not bother... */
- switch(data->state)
- {
- case REPODATA_STUB:
- if (keyname)
- {
- int i;
- for (i = 1; i < data->nkeys; i++)
- if (keyname == data->keys[i].name)
- break;
- if (i == data->nkeys)
- return 0;
- }
- return load_repodata(data);
- case REPODATA_ERROR:
- return 0;
- case REPODATA_AVAILABLE:
- case REPODATA_LOADING:
- return 1;
- default:
- data->state = REPODATA_ERROR;
- return 0;
- }
-}
-
-static inline unsigned char *
-solvid2data(Repodata *data, Id solvid, Id *schemap)
-{
- unsigned char *dp = data->incoredata;
- if (!dp)
- return 0;
- if (solvid == SOLVID_META)
- dp += 1; /* offset of "meta" solvable */
- else if (solvid == SOLVID_POS)
- {
- Pool *pool = data->repo->pool;
- if (data->repo != pool->pos.repo)
- return 0;
- if (data != data->repo->repodata + pool->pos.repodataid)
- return 0;
- dp += pool->pos.dp;
- if (pool->pos.dp != 1)
- {
- *schemap = pool->pos.schema;
- return dp;
- }
- }
- else
- {
- if (solvid < data->start || solvid >= data->end)
- return 0;
- dp += data->incoreoffset[solvid - data->start];
- }
- return data_read_id(dp, schemap);
-}
-
-/************************************************************************
- * data lookup
- */
-
-static unsigned char *
-find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
-{
- unsigned char *dp;
- Id schema, *keyp, *kp;
- Repokey *key;
-
- if (!maybe_load_repodata(data, keyname))
- return 0;
- dp = solvid2data(data, solvid, &schema);
- if (!dp)
- return 0;
- keyp = data->schemadata + data->schemata[schema];
- for (kp = keyp; *kp; kp++)
- if (data->keys[*kp].name == keyname)
- break;
- if (!*kp)
- return 0;
- *keypp = key = data->keys + *kp;
- if (key->type == REPOKEY_TYPE_DELETED)
- return 0;
- if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
- return dp; /* no need to forward... */
- if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
- return 0; /* get_data will not work, no need to forward */
- dp = forward_to_key(data, *kp, keyp, dp);
- if (!dp)
- return 0;
- return get_data(data, key, &dp, 0);
-}
-
-Id
-repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
-{
- Id schema, *keyp, *kp;
- if (!maybe_load_repodata(data, keyname))
- return 0;
- if (!solvid2data(data, solvid, &schema))
- return 0;
- keyp = data->schemadata + data->schemata[schema];
- for (kp = keyp; *kp; kp++)
- if (data->keys[*kp].name == keyname)
- return data->keys[*kp].type;
- return 0;
-}
-
-Id
-repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
-{
- unsigned char *dp;
- Repokey *key;
- Id id;
-
- dp = find_key_data(data, solvid, keyname, &key);
- if (!dp)
- return 0;
- if (key->type == REPOKEY_TYPE_CONSTANTID)
- return key->size;
- if (key->type != REPOKEY_TYPE_ID)
- return 0;
- dp = data_read_id(dp, &id);
- return id;
-}
-
-const char *
-repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
-{
- unsigned char *dp;
- Repokey *key;
- Id id;
-
- dp = find_key_data(data, solvid, keyname, &key);
- if (!dp)
- return 0;
- if (key->type == REPOKEY_TYPE_STR)
- return (const char *)dp;
- if (key->type == REPOKEY_TYPE_CONSTANTID)
- id = key->size;
- else if (key->type == REPOKEY_TYPE_ID)
- dp = data_read_id(dp, &id);
- else
- return 0;
- if (data->localpool)
- return stringpool_id2str(&data->spool, id);
- return pool_id2str(data->repo->pool, id);
-}
-
-int
-repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value)
-{
- unsigned char *dp;
- Repokey *key;
- unsigned int high, low;
-
- *value = 0;
- dp = find_key_data(data, solvid, keyname, &key);
- if (!dp)
- return 0;
- switch (key->type)
- {
- case REPOKEY_TYPE_NUM:
- data_read_num64(dp, &low, &high);
- *value = (unsigned long long)high << 32 | low;
- return 1;
- case REPOKEY_TYPE_U32:
- data_read_u32(dp, &low);
- *value = low;
- return 1;
- case REPOKEY_TYPE_CONSTANT:
- *value = key->size;
- return 1;
- default:
- return 0;
- }
-}
-
-int
-repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
-{
- Id schema;
- Id *keyp;
- unsigned char *dp;
-
- if (!maybe_load_repodata(data, keyname))
- return 0;
- dp = solvid2data(data, solvid, &schema);
- if (!dp)
- return 0;
- /* can't use find_key_data as we need to test the type */
- for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
- if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
- return 1;
- return 0;
-}
-
-const unsigned char *
-repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
-{
- unsigned char *dp;
- Repokey *key;
-
- dp = find_key_data(data, solvid, keyname, &key);
- if (!dp)
- return 0;
- switch (key->type)
- {
- case_CHKSUM_TYPES:
- break;
- default:
- return 0;
- }
- *typep = key->type;
- return dp;
-}
-
-int
-repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
-{
- unsigned char *dp;
- Repokey *key;
- Id id;
- int eof = 0;
-
- queue_empty(q);
- dp = find_key_data(data, solvid, keyname, &key);
- if (!dp)
- return 0;
- if (key->type != REPOKEY_TYPE_IDARRAY)
- return 0;
- for (;;)
- {
- dp = data_read_ideof(dp, &id, &eof);
- queue_push(q, id);
- if (eof)
- break;
- }
- return 1;
-}
-
-const void *
-repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp)
-{
- unsigned char *dp;
- Repokey *key;
- Id len;
-
- dp = find_key_data(data, solvid, keyname, &key);
- if (!dp || key->type != REPOKEY_TYPE_BINARY)
- {
- *lenp = 0;
- return 0;
- }
- dp = data_read_id(dp, &len);
- *lenp = len;
- return dp;
-}
-
-Id
-repodata_globalize_id(Repodata *data, Id id, int create)
-{
- if (!id || !data || !data->localpool)
- return id;
- return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
-}
-
-Id
-repodata_localize_id(Repodata *data, Id id, int create)
-{
- if (!id || !data || !data->localpool)
- return id;
- return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
-}
-
-Id
-repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
-{
- if (!id || !data || !fromdata)
- return id;
- if (!data->localpool || !fromdata->localpool)
- {
- if (fromdata->localpool)
- id = repodata_globalize_id(fromdata, id, create);
- if (data->localpool)
- id = repodata_localize_id(data, id, create);
- return id;
- }
- /* localpool is set in both data and fromdata */
- return stringpool_str2id(&data->spool, stringpool_id2str(&fromdata->spool, id), create);
-}
-
-Id
-repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
-{
- Id *ap;
- if (!data->attrs)
- return 0;
- ap = data->attrs[solvid - data->start];
- if (!ap)
- return 0;
- for (; *ap; ap += 2)
- {
- if (data->keys[*ap].name != keyname)
- continue;
- if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
- return voidid;
- if (data->keys[*ap].type == REPOKEY_TYPE_ID)
- return ap[1];
- return 0;
- }
- return 0;
-}
-
-const char *
-repodata_lookup_dirstrarray_uninternalized(Repodata *data, Id solvid, Id keyname, Id *didp, Id *iterp)
-{
- Id *ap, did;
- Id iter = *iterp;
- if (iter == 0) /* find key data */
- {
- if (!data->attrs)
- return 0;
- ap = data->attrs[solvid - data->start];
- if (!ap)
- return 0;
- for (; *ap; ap += 2)
- if (data->keys[*ap].name == keyname && data->keys[*ap].type == REPOKEY_TYPE_DIRSTRARRAY)
- break;
- if (!*ap)
- return 0;
- iter = ap[1];
- }
- did = *didp;
- for (ap = data->attriddata + iter; *ap; ap += 2)
- {
- if (did && ap[0] != did)
- continue;
- *didp = ap[0];
- *iterp = ap - data->attriddata + 2;
- return (const char *)data->attrdata + ap[1];
- }
- *iterp = 0;
- return 0;
-}
-
-/************************************************************************
- * data search
- */
-
-
-const char *
-repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
-{
- switch (key->type)
- {
- case REPOKEY_TYPE_ID:
- case REPOKEY_TYPE_CONSTANTID:
- case REPOKEY_TYPE_IDARRAY:
- if (data && data->localpool)
- kv->str = stringpool_id2str(&data->spool, kv->id);
- else
- kv->str = pool_id2str(pool, kv->id);
- if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
- {
- const char *s;
- for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
- ;
- if (*s == ':' && s > kv->str)
- kv->str = s + 1;
- }
- return kv->str;
- case REPOKEY_TYPE_STR:
- return kv->str;
- case REPOKEY_TYPE_DIRSTRARRAY:
- if (!(flags & SEARCH_FILES))
- return kv->str; /* match just the basename */
- if (kv->num)
- return kv->str; /* already stringified */
- /* Put the full filename into kv->str. */
- kv->str = repodata_dir2str(data, kv->id, kv->str);
- kv->num = 1; /* mark stringification */
- return kv->str;
- case_CHKSUM_TYPES:
- if (!(flags & SEARCH_CHECKSUMS))
- return 0; /* skip em */
- if (kv->num)
- return kv->str; /* already stringified */
- kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
- kv->num = 1; /* mark stringification */
- return kv->str;
- default:
- return 0;
- }
-}
-
-
-struct subschema_data {
- Solvable *s;
- void *cbdata;
- KeyValue *parent;
-};
-
-/* search a specific repodata */
-void
-repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
-{
- Id schema;
- Repokey *key;
- Id keyid, *kp, *keyp;
- unsigned char *dp, *ddp;
- int onekey = 0;
- int stop;
- KeyValue kv;
- Solvable *s;
-
- if (!maybe_load_repodata(data, keyname))
- return;
- if (solvid == SOLVID_SUBSCHEMA)
- {
- struct subschema_data *subd = cbdata;
- cbdata = subd->cbdata;
- s = subd->s;
- schema = subd->parent->id;
- dp = (unsigned char *)subd->parent->str;
- kv.parent = subd->parent;
- }
- else
- {
- schema = 0;
- dp = solvid2data(data, solvid, &schema);
- if (!dp)
- return;
- s = data->repo->pool->solvables + solvid;
- kv.parent = 0;
- }
- keyp = data->schemadata + data->schemata[schema];
- if (keyname)
- {
- /* search for a specific key */
- for (kp = keyp; *kp; kp++)
- if (data->keys[*kp].name == keyname)
- break;
- if (!*kp)
- return;
- dp = forward_to_key(data, *kp, keyp, dp);
- if (!dp)
- return;
- keyp = kp;
- onekey = 1;
- }
- while ((keyid = *keyp++) != 0)
- {
- stop = 0;
- key = data->keys + keyid;
- ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
-
- if (key->type == REPOKEY_TYPE_DELETED)
- continue;
- if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
- {
- struct subschema_data subd;
- int nentries;
- Id schema = 0;
-
- subd.cbdata = cbdata;
- subd.s = s;
- subd.parent = &kv;
- ddp = data_read_id(ddp, &nentries);
- kv.num = nentries;
- kv.entry = 0;
- kv.eof = 0;
- while (ddp && nentries > 0)
- {
- if (!--nentries)
- kv.eof = 1;
- if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
- ddp = data_read_id(ddp, &schema);
- kv.id = schema;
- kv.str = (char *)ddp;
- stop = callback(cbdata, s, data, key, &kv);
- if (stop > SEARCH_NEXT_KEY)
- return;
- if (stop && stop != SEARCH_ENTERSUB)
- break;
- if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
- repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
- ddp = data_skip_schema(data, ddp, schema);
- kv.entry++;
- }
- if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
- {
- /* sentinel */
- kv.eof = 2;
- kv.str = (char *)ddp;
- stop = callback(cbdata, s, data, key, &kv);
- if (stop > SEARCH_NEXT_KEY)
- return;
- }
- if (onekey)
- return;
- continue;
- }
- kv.entry = 0;
- do
- {
- ddp = data_fetch(ddp, &kv, key);
- if (!ddp)
- break;
- stop = callback(cbdata, s, data, key, &kv);
- kv.entry++;
- }
- while (!kv.eof && !stop);
- if (onekey || stop > SEARCH_NEXT_KEY)
- return;
- }
-}
-
-void
-repodata_setpos_kv(Repodata *data, KeyValue *kv)
-{
- Pool *pool = data->repo->pool;
- if (!kv)
- pool_clear_pos(pool);
- else
- {
- pool->pos.repo = data->repo;
- pool->pos.repodataid = data - data->repo->repodata;
- pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
- pool->pos.schema = kv->id;
- }
-}
-
-/************************************************************************
- * data iterator functions
- */
-
-static inline Id *
-solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
-{
- kv->id = keyname;
- switch (keyname)
- {
- case SOLVABLE_NAME:
- kv->eof = 1;
- return &s->name;
- case SOLVABLE_ARCH:
- kv->eof = 1;
- return &s->arch;
- case SOLVABLE_EVR:
- kv->eof = 1;
- return &s->evr;
- case SOLVABLE_VENDOR:
- kv->eof = 1;
- return &s->vendor;
- case SOLVABLE_PROVIDES:
- kv->eof = 0;
- return s->provides ? s->repo->idarraydata + s->provides : 0;
- case SOLVABLE_OBSOLETES:
- kv->eof = 0;
- return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
- case SOLVABLE_CONFLICTS:
- kv->eof = 0;
- return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
- case SOLVABLE_REQUIRES:
- kv->eof = 0;
- return s->requires ? s->repo->idarraydata + s->requires : 0;
- case SOLVABLE_RECOMMENDS:
- kv->eof = 0;
- return s->recommends ? s->repo->idarraydata + s->recommends : 0;
- case SOLVABLE_SUPPLEMENTS:
- kv->eof = 0;
- return s->supplements ? s->repo->idarraydata + s->supplements : 0;
- case SOLVABLE_SUGGESTS:
- kv->eof = 0;
- return s->suggests ? s->repo->idarraydata + s->suggests : 0;
- case SOLVABLE_ENHANCES:
- kv->eof = 0;
- return s->enhances ? s->repo->idarraydata + s->enhances : 0;
- case RPM_RPMDBID:
- kv->eof = 1;
- return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
- default:
- return 0;
- }
-}
-
-int
-datamatcher_init(Datamatcher *ma, const char *match, int flags)
-{
- match = match ? solv_strdup(match) : 0;
- ma->match = match;
- ma->flags = flags;
- ma->error = 0;
- ma->matchdata = 0;
- if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
- {
- ma->matchdata = solv_calloc(1, sizeof(regex_t));
- ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
- if (ma->error)
- {
- solv_free(ma->matchdata);
- ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
- }
- }
- if ((flags & SEARCH_FILES) != 0 && match)
- {
- /* prepare basename check */
- if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
- {
- const char *p = strrchr(match, '/');
- ma->matchdata = (void *)(p ? p + 1 : match);
- }
- else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
- {
- const char *p;
- for (p = match + strlen(match) - 1; p >= match; p--)
- if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
- break;
- ma->matchdata = (void *)(p + 1);
- }
- }
- return ma->error;
-}
-
-void
-datamatcher_free(Datamatcher *ma)
-{
- if (ma->match)
- ma->match = solv_free((char *)ma->match);
- if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
- {
- regfree(ma->matchdata);
- solv_free(ma->matchdata);
- }
- ma->matchdata = 0;
-}
-
-int
-datamatcher_match(Datamatcher *ma, const char *str)
-{
- int l;
- switch ((ma->flags & SEARCH_STRINGMASK))
- {
- case SEARCH_SUBSTRING:
- if (ma->flags & SEARCH_NOCASE)
- return strcasestr(str, ma->match) != 0;
- else
- return strstr(str, ma->match) != 0;
- case SEARCH_STRING:
- if (ma->flags & SEARCH_NOCASE)
- return !strcasecmp(ma->match, str);
- else
- return !strcmp(ma->match, str);
- case SEARCH_STRINGSTART:
- if (ma->flags & SEARCH_NOCASE)
- return !strncasecmp(ma->match, str, strlen(ma->match));
- else
- return !strncmp(ma->match, str, strlen(ma->match));
- case SEARCH_STRINGEND:
- l = strlen(str) - strlen(ma->match);
- if (l < 0)
- return 0;
- if (ma->flags & SEARCH_NOCASE)
- return !strcasecmp(ma->match, str + l);
- else
- return !strcmp(ma->match, str + l);
- case SEARCH_GLOB:
- return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
- case SEARCH_REGEX:
- return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
- default:
- return 0;
- }
-}
-
-/* check if the matcher can match the provides basename */
-
-int
-datamatcher_checkbasename(Datamatcher *ma, const char *basename)
-{
- int l;
- const char *match = ma->matchdata;
- if (!match)
- return 1;
- switch (ma->flags & SEARCH_STRINGMASK)
- {
- case SEARCH_STRING:
- break;
- case SEARCH_STRINGEND:
- if (match != ma->match)
- break; /* had slash, do exact match on basename */
- /* FALLTHROUGH */
- case SEARCH_GLOB:
- /* check if the basename ends with match */
- l = strlen(basename) - strlen(match);
- if (l < 0)
- return 0;
- basename += l;
- break;
- default:
- return 1; /* maybe matches */
- }
- if ((ma->flags & SEARCH_NOCASE) != 0)
- return !strcasecmp(match, basename);
- else
- return !strcmp(match, basename);
-}
-
-int
-repodata_filelistfilter_matches(Repodata *data, const char *str)
-{
- /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
- /* for now hardcoded */
- if (strstr(str, "bin/"))
- return 1;
- if (!strncmp(str, "/etc/", 5))
- return 1;
- if (!strcmp(str, "/usr/lib/sendmail"))
- return 1;
- return 0;
-}
-
-
-enum {
- di_bye,
-
- di_enterrepo,
- di_entersolvable,
- di_enterrepodata,
- di_enterschema,
- di_enterkey,
-
- di_nextattr,
- di_nextkey,
- di_nextrepodata,
- di_nextsolvable,
- di_nextrepo,
-
- di_enterarray,
- di_nextarrayelement,
-
- di_entersub,
- di_leavesub,
-
- di_nextsolvablekey,
- di_entersolvablekey,
- di_nextsolvableattr
-};
-
-/* see dataiterator.h for documentation */
-int
-dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
-{
- memset(di, 0, sizeof(*di));
- di->pool = pool;
- di->flags = flags & ~SEARCH_THISSOLVID;
- if (!pool || (repo && repo->pool != pool))
- {
- di->state = di_bye;
- return -1;
- }
- if (match)
- {
- int error;
- if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
- {
- di->state = di_bye;
- return error;
- }
- }
- di->keyname = keyname;
- di->keynames[0] = keyname;
- dataiterator_set_search(di, repo, p);
- return 0;
-}
-
-void
-dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
-{
- *di = *from;
- if (di->dupstr)
- {
- if (di->dupstr == di->kv.str)
- di->dupstr = solv_memdup(di->dupstr, di->dupstrn);
- else
- {
- di->dupstr = 0;
- di->dupstrn = 0;
- }
- }
- memset(&di->matcher, 0, sizeof(di->matcher));
- if (from->matcher.match)
- datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
- if (di->nparents)
- {
- /* fix pointers */
- int i;
- for (i = 1; i < di->nparents; i++)
- di->parents[i].kv.parent = &di->parents[i - 1].kv;
- di->kv.parent = &di->parents[di->nparents - 1].kv;
- }
-}
-
-int
-dataiterator_set_match(Dataiterator *di, const char *match, int flags)
-{
- di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
- datamatcher_free(&di->matcher);
- memset(&di->matcher, 0, sizeof(di->matcher));
- if (match)
- {
- int error;
- if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
- {
- di->state = di_bye;
- return error;
- }
- }
- return 0;
-}
-
-void
-dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
-{
- di->repo = repo;
- di->repoid = 0;
- di->flags &= ~SEARCH_THISSOLVID;
- di->nparents = 0;
- di->rootlevel = 0;
- di->repodataid = 1;
- if (!di->pool->urepos)
- {
- di->state = di_bye;
- return;
- }
- if (!repo)
- {
- di->repoid = 1;
- di->repo = di->pool->repos[di->repoid];
- }
- di->state = di_enterrepo;
- if (p)
- dataiterator_jump_to_solvid(di, p);
-}
-
-void
-dataiterator_set_keyname(Dataiterator *di, Id keyname)
-{
- di->nkeynames = 0;
- di->keyname = keyname;
- di->keynames[0] = keyname;
-}
-
-void
-dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
-{
- int i;
-
- if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
- {
- di->state = di_bye; /* sorry */
- return;
- }
- for (i = di->nkeynames + 1; i > 0; i--)
- di->keynames[i] = di->keynames[i - 1];
- di->keynames[0] = di->keyname = keyname;
- di->nkeynames++;
-}
-
-void
-dataiterator_free(Dataiterator *di)
-{
- if (di->matcher.match)
- datamatcher_free(&di->matcher);
- if (di->dupstr)
- solv_free(di->dupstr);
-}
-
-static unsigned char *
-dataiterator_find_keyname(Dataiterator *di, Id keyname)
-{
- Id *keyp;
- Repokey *keys = di->data->keys, *key;
- unsigned char *dp;
-
- for (keyp = di->keyp; *keyp; keyp++)
- if (keys[*keyp].name == keyname)
- break;
- if (!*keyp)
- return 0;
- key = keys + *keyp;
- if (key->type == REPOKEY_TYPE_DELETED)
- return 0;
- if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
- return 0; /* get_data will not work, no need to forward */
- dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
- if (!dp)
- return 0;
- di->keyp = keyp;
- return dp;
-}
-
-static inline int
-is_filelist_extension(Repodata *data)
-{
- int j;
- if (!repodata_precheck_keyname(data, SOLVABLE_FILELIST))
- return 0;
- for (j = 1; j < data->nkeys; j++)
- if (data->keys[j].name == SOLVABLE_FILELIST)
- break;
- if (j == data->nkeys)
- return 0;
- if (data->state != REPODATA_AVAILABLE)
- return 1;
- for (j = 1; j < data->nkeys; j++)
- if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
- return 0;
- return 1;
-}
-
-static int
-dataiterator_filelistcheck(Dataiterator *di)
-{
- int j;
- int needcomplete = 0;
- Repodata *data = di->data;
-
- if ((di->flags & SEARCH_COMPLETE_FILELIST) != 0)
- if (!di->matcher.match
- || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
- && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
- || !repodata_filelistfilter_matches(data, di->matcher.match))
- needcomplete = 1;
- if (data->state != REPODATA_AVAILABLE)
- return needcomplete ? 1 : 0;
- if (!needcomplete)
- {
- /* we don't need the complete filelist, so ignore all stubs */
- if (data->repo->nrepodata == 2)
- return 1;
- for (j = 1; j < data->nkeys; j++)
- if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
- return 1;
- return 0;
- }
- else
- {
- /* we need the complete filelist. check if we habe a filtered filelist and there's
- * a extension with the complete filelist later on */
- for (j = 1; j < data->nkeys; j++)
- if (data->keys[j].name == SOLVABLE_FILELIST)
- break;
- if (j == data->nkeys)
- return 0; /* does not have filelist */
- for (j = 1; j < data->nkeys; j++)
- if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
- break;
- if (j == data->nkeys)
- return 1; /* this is the externsion */
- while (data - data->repo->repodata + 1 < data->repo->nrepodata)
- {
- data++;
- if (is_filelist_extension(data))
- return 0;
- }
- return 1;
- }
-}
-
-int
-dataiterator_step(Dataiterator *di)
-{
- Id schema;
-
- if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
- unsigned int ddpoff = di->ddp - di->vert_ddp;
- di->vert_off += ddpoff;
- di->vert_len -= ddpoff;
- di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
- di->vert_storestate = di->data->storestate;
- if (!di->ddp)
- di->state = di_nextkey;
- }
- for (;;)
- {
- switch (di->state)
- {
- case di_enterrepo: di_enterrepo:
- if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
- goto di_nextrepo;
- if (!(di->flags & SEARCH_THISSOLVID))
- {
- di->solvid = di->repo->start - 1; /* reset solvid iterator */
- goto di_nextsolvable;
- }
- /* FALLTHROUGH */
-
- case di_entersolvable: di_entersolvable:
- if (di->repodataid)
- {
- di->repodataid = 1; /* reset repodata iterator */
- if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents - di->rootlevel == di->nkeynames)
- {
- extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
-
- di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
- di->data = 0;
- goto di_entersolvablekey;
- }
- }
- /* FALLTHROUGH */
-
- case di_enterrepodata: di_enterrepodata:
- if (di->repodataid)
- {
- if (di->repodataid >= di->repo->nrepodata)
- goto di_nextsolvable;
- di->data = di->repo->repodata + di->repodataid;
- }
- if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
- goto di_nextrepodata;
- if (!maybe_load_repodata(di->data, di->keyname))
- goto di_nextrepodata;
- di->dp = solvid2data(di->data, di->solvid, &schema);
- if (!di->dp)
- goto di_nextrepodata;
- if (di->solvid == SOLVID_POS)
- di->solvid = di->pool->pos.solvid;
- /* reset key iterator */
- di->keyp = di->data->schemadata + di->data->schemata[schema];
- /* FALLTHROUGH */
-
- case di_enterschema: di_enterschema:
- if (di->keyname)
- di->dp = dataiterator_find_keyname(di, di->keyname);
- if (!di->dp || !*di->keyp)
- {
- if (di->kv.parent)
- goto di_leavesub;
- goto di_nextrepodata;
- }
- /* FALLTHROUGH */
-
- case di_enterkey: di_enterkey:
- di->kv.entry = -1;
- di->key = di->data->keys + *di->keyp;
- if (!di->dp)
- goto di_nextkey;
- /* this is get_data() modified to store vert_ data */
- if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- Id off, len;
- di->dp = data_read_id(di->dp, &off);
- di->dp = data_read_id(di->dp, &len);
- di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
- di->vert_off = off;
- di->vert_len = len;
- di->vert_storestate = di->data->storestate;
- }
- else if (di->key->storage == KEY_STORAGE_INCORE)
- {
- di->ddp = di->dp;
- if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
- di->dp = data_skip_key(di->data, di->dp, di->key);
- }
- else
- di->ddp = 0;
- if (!di->ddp)
- goto di_nextkey;
- if (di->key->type == REPOKEY_TYPE_DELETED)
- goto di_nextkey;
- if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
- goto di_enterarray;
- if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
- goto di_nextkey;
- /* FALLTHROUGH */
-
- case di_nextattr:
- di->kv.entry++;
- di->ddp = data_fetch(di->ddp, &di->kv, di->key);
- if (di->kv.eof)
- di->state = di_nextkey;
- else
- di->state = di_nextattr;
- break;
-
- case di_nextkey: di_nextkey:
- if (!di->keyname && *++di->keyp)
- goto di_enterkey;
- if (di->kv.parent)
- goto di_leavesub;
- /* FALLTHROUGH */
-
- case di_nextrepodata: di_nextrepodata:
- if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
- goto di_enterrepodata;
- /* FALLTHROUGH */
-
- case di_nextsolvable: di_nextsolvable:
- if (!(di->flags & SEARCH_THISSOLVID))
- {
- if (di->solvid < 0)
- di->solvid = di->repo->start;
- else
- di->solvid++;
- for (; di->solvid < di->repo->end; di->solvid++)
- {
- if (di->pool->solvables[di->solvid].repo == di->repo)
- goto di_entersolvable;
- }
- }
- /* FALLTHROUGH */
-
- case di_nextrepo: di_nextrepo:
- if (di->repoid > 0)
- {
- di->repoid++;
- di->repodataid = 1;
- if (di->repoid < di->pool->nrepos)
- {
- di->repo = di->pool->repos[di->repoid];
- goto di_enterrepo;
- }
- }
- /* FALLTHROUGH */
-
- case di_bye: di_bye:
- di->state = di_bye;
- return 0;
-
- case di_enterarray: di_enterarray:
- if (di->key->name == REPOSITORY_SOLVABLES)
- goto di_nextkey;
- di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
- di->kv.eof = 0;
- di->kv.entry = -1;
- /* FALLTHROUGH */
-
- case di_nextarrayelement: di_nextarrayelement:
- di->kv.entry++;
- if (di->kv.entry)
- di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
- if (di->kv.entry == di->kv.num)
- {
- if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
- goto di_nextkey;
- if (!(di->flags & SEARCH_ARRAYSENTINEL))
- goto di_nextkey;
- di->kv.str = (char *)di->ddp;
- di->kv.eof = 2;
- di->state = di_nextkey;
- break;
- }
- if (di->kv.entry == di->kv.num - 1)
- di->kv.eof = 1;
- if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
- di->ddp = data_read_id(di->ddp, &di->kv.id);
- di->kv.str = (char *)di->ddp;
- if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
- goto di_entersub;
- if ((di->flags & SEARCH_SUB) != 0)
- di->state = di_entersub;
- else
- di->state = di_nextarrayelement;
- break;
-
- case di_entersub: di_entersub:
- if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
- goto di_nextarrayelement; /* sorry, full */
- di->parents[di->nparents].kv = di->kv;
- di->parents[di->nparents].dp = di->dp;
- di->parents[di->nparents].keyp = di->keyp;
- di->dp = (unsigned char *)di->kv.str;
- di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
- memset(&di->kv, 0, sizeof(di->kv));
- di->kv.parent = &di->parents[di->nparents].kv;
- di->nparents++;
- di->keyname = di->keynames[di->nparents - di->rootlevel];
- goto di_enterschema;
-
- case di_leavesub: di_leavesub:
- if (di->nparents - 1 < di->rootlevel)
- goto di_bye;
- di->nparents--;
- di->dp = di->parents[di->nparents].dp;
- di->kv = di->parents[di->nparents].kv;
- di->keyp = di->parents[di->nparents].keyp;
- di->key = di->data->keys + *di->keyp;
- di->ddp = (unsigned char *)di->kv.str;
- di->keyname = di->keynames[di->nparents - di->rootlevel];
- goto di_nextarrayelement;
-
- /* special solvable attr handling follows */
-
- case di_nextsolvablekey: di_nextsolvablekey:
- if (di->keyname || di->key->name == RPM_RPMDBID)
- goto di_enterrepodata;
- di->key++;
- /* FALLTHROUGH */
-
- case di_entersolvablekey: di_entersolvablekey:
- di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
- if (!di->idp || !*di->idp)
- goto di_nextsolvablekey;
- if (di->kv.eof)
- {
- /* not an array */
- di->kv.id = *di->idp;
- di->kv.num = *di->idp; /* for rpmdbid */
- di->kv.num2 = 0; /* for rpmdbid */
- di->kv.entry = 0;
- di->state = di_nextsolvablekey;
- break;
- }
- di->kv.entry = -1;
- /* FALLTHROUGH */
-
- case di_nextsolvableattr:
- di->state = di_nextsolvableattr;
- di->kv.id = *di->idp++;
- di->kv.entry++;
- if (!*di->idp)
- {
- di->kv.eof = 1;
- di->state = di_nextsolvablekey;
- }
- break;
-
- }
-
- if (di->matcher.match)
- {
- const char *str;
- /* simple pre-check so that we don't need to stringify */
- if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
- if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
- continue;
- if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
- {
- if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
- return 1;
- continue;
- }
- if (!datamatcher_match(&di->matcher, str))
- continue;
- }
- else
- {
- if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
- repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
- }
- /* found something! */
- return 1;
- }
-}
-
-void
-dataiterator_entersub(Dataiterator *di)
-{
- if (di->state == di_nextarrayelement)
- di->state = di_entersub;
-}
-
-void
-dataiterator_setpos(Dataiterator *di)
-{
- if (di->kv.eof == 2)
- {
- pool_clear_pos(di->pool);
- return;
- }
- di->pool->pos.solvid = di->solvid;
- di->pool->pos.repo = di->repo;
- di->pool->pos.repodataid = di->data - di->repo->repodata;
- di->pool->pos.schema = di->kv.id;
- di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
-}
-
-void
-dataiterator_setpos_parent(Dataiterator *di)
-{
- if (!di->kv.parent || di->kv.parent->eof == 2)
- {
- pool_clear_pos(di->pool);
- return;
- }
- di->pool->pos.solvid = di->solvid;
- di->pool->pos.repo = di->repo;
- di->pool->pos.repodataid = di->data - di->repo->repodata;
- di->pool->pos.schema = di->kv.parent->id;
- di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
-}
-
-/* clones just the position, not the search keys/matcher */
-void
-dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
-{
- di->state = from->state;
- di->flags &= ~SEARCH_THISSOLVID;
- di->flags |= (from->flags & SEARCH_THISSOLVID);
- di->repo = from->repo;
- di->data = from->data;
- di->dp = from->dp;
- di->ddp = from->ddp;
- di->idp = from->idp;
- di->keyp = from->keyp;
- di->key = from->key;
- di->kv = from->kv;
- di->repodataid = from->repodataid;
- di->solvid = from->solvid;
- di->repoid = from->repoid;
- di->rootlevel = from->rootlevel;
- memcpy(di->parents, from->parents, sizeof(from->parents));
- di->nparents = from->nparents;
- if (di->nparents)
- {
- int i;
- for (i = 1; i < di->nparents; i++)
- di->parents[i].kv.parent = &di->parents[i - 1].kv;
- di->kv.parent = &di->parents[di->nparents - 1].kv;
- }
- di->dupstr = 0;
- di->dupstrn = 0;
- if (from->dupstr && from->dupstr == from->kv.str)
- {
- di->dupstrn = from->dupstrn;
- di->dupstr = solv_memdup(from->dupstr, from->dupstrn);
- }
-}
-
-void
-dataiterator_seek(Dataiterator *di, int whence)
-{
- if ((whence & DI_SEEK_STAY) != 0)
- di->rootlevel = di->nparents;
- switch (whence & ~DI_SEEK_STAY)
- {
- case DI_SEEK_CHILD:
- if (di->state != di_nextarrayelement)
- break;
- if ((whence & DI_SEEK_STAY) != 0)
- di->rootlevel = di->nparents + 1; /* XXX: dangerous! */
- di->state = di_entersub;
- break;
- case DI_SEEK_PARENT:
- if (!di->nparents)
- {
- di->state = di_bye;
- break;
- }
- di->nparents--;
- if (di->rootlevel > di->nparents)
- di->rootlevel = di->nparents;
- di->dp = di->parents[di->nparents].dp;
- di->kv = di->parents[di->nparents].kv;
- di->keyp = di->parents[di->nparents].keyp;
- di->key = di->data->keys + *di->keyp;
- di->ddp = (unsigned char *)di->kv.str;
- di->keyname = di->keynames[di->nparents - di->rootlevel];
- di->state = di_nextarrayelement;
- break;
- case DI_SEEK_REWIND:
- if (!di->nparents)
- {
- di->state = di_bye;
- break;
- }
- di->dp = (unsigned char *)di->kv.parent->str;
- di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
- di->state = di_enterschema;
- break;
- default:
- break;
- }
-}
-
-void
-dataiterator_skip_attribute(Dataiterator *di)
-{
- if (di->state == di_nextsolvableattr)
- di->state = di_nextsolvablekey;
- else
- di->state = di_nextkey;
-}
-
-void
-dataiterator_skip_solvable(Dataiterator *di)
-{
- di->nparents = 0;
- di->kv.parent = 0;
- di->rootlevel = 0;
- di->keyname = di->keynames[0];
- di->state = di_nextsolvable;
-}
-
-void
-dataiterator_skip_repo(Dataiterator *di)
-{
- di->nparents = 0;
- di->kv.parent = 0;
- di->rootlevel = 0;
- di->keyname = di->keynames[0];
- di->state = di_nextrepo;
-}
-
-void
-dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
-{
- di->nparents = 0;
- di->kv.parent = 0;
- di->rootlevel = 0;
- di->keyname = di->keynames[0];
- if (solvid == SOLVID_POS)
- {
- di->repo = di->pool->pos.repo;
- if (!di->repo)
- {
- di->state = di_bye;
- return;
- }
- di->repoid = 0;
- if (!di->pool->pos.repodataid && di->pool->pos.solvid == SOLVID_META) {
- solvid = SOLVID_META; /* META pos hack */
- } else {
- di->data = di->repo->repodata + di->pool->pos.repodataid;
- di->repodataid = 0;
- }
- }
- else if (solvid > 0)
- {
- di->repo = di->pool->solvables[solvid].repo;
- di->repoid = 0;
- }
- if (di->repoid > 0)
- {
- if (!di->pool->urepos)
- {
- di->state = di_bye;
- return;
- }
- di->repoid = 1;
- di->repo = di->pool->repos[di->repoid];
- }
- if (solvid != SOLVID_POS)
- di->repodataid = 1;
- di->solvid = solvid;
- if (solvid)
- di->flags |= SEARCH_THISSOLVID;
- di->state = di_enterrepo;
-}
-
-void
-dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
-{
- di->nparents = 0;
- di->kv.parent = 0;
- di->rootlevel = 0;
- di->repo = repo;
- di->repoid = 0; /* 0 means stay at repo */
- di->repodataid = 1;
- di->solvid = 0;
- di->flags &= ~SEARCH_THISSOLVID;
- di->state = di_enterrepo;
-}
-
-int
-dataiterator_match(Dataiterator *di, Datamatcher *ma)
-{
- const char *str;
- if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
- return 0;
- return ma ? datamatcher_match(ma, str) : 1;
-}
-
-void
-dataiterator_strdup(Dataiterator *di)
-{
- int l = -1;
-
- if (!di->kv.str || di->kv.str == di->dupstr)
- return;
- switch (di->key->type)
- {
- case_CHKSUM_TYPES:
- case REPOKEY_TYPE_DIRSTRARRAY:
- if (di->kv.num) /* was it stringified into tmp space? */
- l = strlen(di->kv.str) + 1;
- break;
- default:
- break;
- }
- if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- switch (di->key->type)
- {
- case REPOKEY_TYPE_STR:
- case REPOKEY_TYPE_DIRSTRARRAY:
- l = strlen(di->kv.str) + 1;
- break;
- case_CHKSUM_TYPES:
- l = solv_chksum_len(di->key->type);
- break;
- case REPOKEY_TYPE_BINARY:
- l = di->kv.num;
- break;
- }
- }
- if (l >= 0)
- {
- if (!di->dupstrn || di->dupstrn < l)
- {
- di->dupstrn = l + 16;
- di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
- }
- if (l)
- memcpy(di->dupstr, di->kv.str, l);
- di->kv.str = di->dupstr;
- }
-}
-
-/************************************************************************
- * data modify functions
- */
-
-/* extend repodata so that it includes solvables p */
-void
-repodata_extend(Repodata *data, Id p)
-{
- if (data->start == data->end)
- data->start = data->end = p;
- if (p >= data->end)
- {
- int old = data->end - data->start;
- int new = p - data->end + 1;
- if (data->attrs)
- {
- data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
- memset(data->attrs + old, 0, new * sizeof(Id *));
- }
- data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
- memset(data->incoreoffset + old, 0, new * sizeof(Id));
- data->end = p + 1;
- }
- if (p < data->start)
- {
- int old = data->end - data->start;
- int new = data->start - p;
- if (data->attrs)
- {
- data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
- memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
- memset(data->attrs, 0, new * sizeof(Id *));
- }
- data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
- memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
- memset(data->incoreoffset, 0, new * sizeof(Id));
- data->start = p;
- }
-}
-
-/* shrink end of repodata */
-void
-repodata_shrink(Repodata *data, int end)
-{
- int i;
-
- if (data->end <= end)
- return;
- if (data->start >= end)
- {
- if (data->attrs)
- {
- for (i = 0; i < data->end - data->start; i++)
- solv_free(data->attrs[i]);
- data->attrs = solv_free(data->attrs);
- }
- data->incoreoffset = solv_free(data->incoreoffset);
- data->start = data->end = 0;
- return;
- }
- if (data->attrs)
- {
- for (i = end; i < data->end; i++)
- solv_free(data->attrs[i - data->start]);
- data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
- }
- if (data->incoreoffset)
- data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
- data->end = end;
-}
-
-/* extend repodata so that it includes solvables from start to start + num - 1 */
-void
-repodata_extend_block(Repodata *data, Id start, Id num)
-{
- if (!num)
- return;
- if (!data->incoreoffset)
- {
- /* this also means that data->attrs is NULL */
- data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
- data->start = start;
- data->end = start + num;
- return;
- }
- repodata_extend(data, start);
- if (num > 1)
- repodata_extend(data, start + num - 1);
-}
-
-/**********************************************************************/
-
-
-#define REPODATA_ATTRS_BLOCK 31
-#define REPODATA_ATTRDATA_BLOCK 1023
-#define REPODATA_ATTRIDDATA_BLOCK 63
-#define REPODATA_ATTRNUM64DATA_BLOCK 15
-
-
-Id
-repodata_new_handle(Repodata *data)
-{
- if (!data->nxattrs)
- {
- data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
- data->nxattrs = 2; /* -1: SOLVID_META */
- }
- data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
- data->xattrs[data->nxattrs] = 0;
- return -(data->nxattrs++);
-}
-
-static inline Id **
-repodata_get_attrp(Repodata *data, Id handle)
-{
- if (handle < 0)
- {
- if (handle == SOLVID_META && !data->xattrs)
- {
- data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
- data->nxattrs = 2;
- }
- return data->xattrs - handle;
- }
- if (handle < data->start || handle >= data->end)
- repodata_extend(data, handle);
- if (!data->attrs)
- data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
- return data->attrs + (handle - data->start);
-}
-
-static void
-repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
-{
- Id *pp;
- Id *ap, **app;
- int i;
-
- app = repodata_get_attrp(data, handle);
- ap = *app;
- i = 0;
- if (ap)
- {
- /* Determine equality based on the name only, allows us to change
- type (when overwrite is set), and makes TYPE_CONSTANT work. */
- for (pp = ap; *pp; pp += 2)
- if (data->keys[*pp].name == data->keys[keyid].name)
- break;
- if (*pp)
- {
- if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
- {
- pp[0] = keyid;
- pp[1] = val;
- }
- return;
- }
- i = pp - ap;
- }
- ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
- *app = ap;
- pp = ap + i;
- *pp++ = keyid;
- *pp++ = val;
- *pp = 0;
-}
-
-
-static void
-repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
-{
- Id keyid;
-
- keyid = repodata_key2id(data, key, 1);
- repodata_insert_keyid(data, solvid, keyid, val, 1);
-}
-
-void
-repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
-{
- Repokey key;
- key.name = keyname;
- key.type = REPOKEY_TYPE_ID;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- repodata_set(data, solvid, &key, id);
-}
-
-void
-repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
-{
- Repokey key;
- key.name = keyname;
- key.type = REPOKEY_TYPE_NUM;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- if (num >= 0x80000000)
- {
- data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
- data->attrnum64data[data->attrnum64datalen] = num;
- num = 0x80000000 | data->attrnum64datalen++;
- }
- repodata_set(data, solvid, &key, (Id)num);
-}
-
-void
-repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
-{
- Repokey key;
- Id id;
- if (data->localpool)
- id = stringpool_str2id(&data->spool, str, 1);
- else
- id = pool_str2id(data->repo->pool, str, 1);
- key.name = keyname;
- key.type = REPOKEY_TYPE_ID;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- repodata_set(data, solvid, &key, id);
-}
-
-void
-repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
-{
- Repokey key;
- key.name = keyname;
- key.type = REPOKEY_TYPE_CONSTANT;
- key.size = constant;
- key.storage = KEY_STORAGE_INCORE;
- repodata_set(data, solvid, &key, 0);
-}
-
-void
-repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
-{
- Repokey key;
- key.name = keyname;
- key.type = REPOKEY_TYPE_CONSTANTID;
- key.size = id;
- key.storage = KEY_STORAGE_INCORE;
- repodata_set(data, solvid, &key, 0);
-}
-
-void
-repodata_set_void(Repodata *data, Id solvid, Id keyname)
-{
- Repokey key;
- key.name = keyname;
- key.type = REPOKEY_TYPE_VOID;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- repodata_set(data, solvid, &key, 0);
-}
-
-void
-repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
-{
- Repokey key;
- int l;
-
- l = strlen(str) + 1;
- key.name = keyname;
- key.type = REPOKEY_TYPE_STR;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
- memcpy(data->attrdata + data->attrdatalen, str, l);
- repodata_set(data, solvid, &key, data->attrdatalen);
- data->attrdatalen += l;
-}
-
-void
-repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
-{
- Repokey key;
- unsigned char *dp;
-
- if (len < 0)
- return;
- key.name = keyname;
- key.type = REPOKEY_TYPE_BINARY;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
- dp = data->attrdata + data->attrdatalen;
- if (len >= (1 << 14))
- {
- if (len >= (1 << 28))
- *dp++ = (len >> 28) | 128;
- if (len >= (1 << 21))
- *dp++ = (len >> 21) | 128;
- *dp++ = (len >> 14) | 128;
- }
- if (len >= (1 << 7))
- *dp++ = (len >> 7) | 128;
- *dp++ = len & 127;
- if (len)
- memcpy(dp, buf, len);
- repodata_set(data, solvid, &key, data->attrdatalen);
- data->attrdatalen = dp + len - data->attrdata;
-}
-
-/* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
- * so that the caller can append entrysize new elements plus the termination zero there */
-static void
-repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
-{
- int oldsize;
- Id *ida, *pp, **ppp;
-
- /* check if it is the same as last time, this speeds things up a lot */
- if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
- {
- /* great! just append the new data */
- data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
- data->attriddatalen--; /* overwrite terminating 0 */
- data->lastdatalen += entrysize;
- return;
- }
-
- ppp = repodata_get_attrp(data, handle);
- pp = *ppp;
- if (pp)
- {
- for (; *pp; pp += 2)
- if (data->keys[*pp].name == keyname)
- break;
- }
- if (!pp || !*pp || data->keys[*pp].type != keytype)
- {
- /* not found. allocate new key */
- Repokey key;
- Id keyid;
- key.name = keyname;
- key.type = keytype;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
- keyid = repodata_key2id(data, &key, 1);
- repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
- data->lasthandle = handle;
- data->lastkey = keyid;
- data->lastdatalen = data->attriddatalen + entrysize + 1;
- return;
- }
- oldsize = 0;
- for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
- oldsize += entrysize;
- if (ida + 1 == data->attriddata + data->attriddatalen)
- {
- /* this was the last entry, just append it */
- data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
- data->attriddatalen--; /* overwrite terminating 0 */
- }
- else
- {
- /* too bad. move to back. */
- data->attriddata = solv_extend(data->attriddata, data->attriddatalen, oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
- memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
- pp[1] = data->attriddatalen;
- data->attriddatalen += oldsize;
- }
- data->lasthandle = handle;
- data->lastkey = *pp;
- data->lastdatalen = data->attriddatalen + entrysize + 1;
-}
-
-void
-repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
- const unsigned char *str)
-{
- Repokey key;
- int l;
-
- if (!(l = solv_chksum_len(type)))
- return;
- key.name = keyname;
- key.type = type;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
- memcpy(data->attrdata + data->attrdatalen, str, l);
- repodata_set(data, solvid, &key, data->attrdatalen);
- data->attrdatalen += l;
-}
-
-void
-repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
- const char *str)
-{
- unsigned char buf[64];
- int l;
-
- if (!(l = solv_chksum_len(type)))
- return;
- if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
- return;
- repodata_set_bin_checksum(data, solvid, keyname, type, buf);
-}
-
-const char *
-repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
-{
- int l;
-
- if (!(l = solv_chksum_len(type)))
- return "";
- return pool_bin2hex(data->repo->pool, buf, l);
-}
-
-/* rpm filenames don't contain the epoch, so strip it */
-static inline const char *
-evrid2vrstr(Pool *pool, Id evrid)
-{
- const char *p, *evr = pool_id2str(pool, evrid);
- if (!evr)
- return evr;
- for (p = evr; *p >= '0' && *p <= '9'; p++)
- ;
- return p != evr && *p == ':' && p[1] ? p + 1 : evr;
-}
-
-static inline void
-repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
-{
- Id id;
- if (data->localpool)
- id = stringpool_strn2id(&data->spool, str, l, 1);
- else
- id = pool_strn2id(data->repo->pool, str, l, 1);
- repodata_set_id(data, solvid, keyname, id);
-}
-
-static inline void
-repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
-{
- if (!str[l])
- repodata_set_str(data, solvid, keyname, str);
- else
- {
- char *s = solv_strdup(str);
- s[l] = 0;
- repodata_set_str(data, solvid, keyname, s);
- free(s);
- }
-}
-
-void
-repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
-{
- Pool *pool = data->repo->pool;
- Solvable *s;
- const char *str, *fp;
- int l = 0;
-
- if (medianr)
- repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
- if (!dir)
- {
- if ((dir = strrchr(file, '/')) != 0)
- {
- l = dir - file;
- dir = file;
- file = dir + l + 1;
- if (!l)
- l++;
- }
- }
- else
- l = strlen(dir);
- if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
- {
- dir += 2;
- l -= 2;
- }
- if (l == 1 && dir[0] == '.')
- l = 0;
- s = pool->solvables + solvid;
- if (dir && l)
- {
- str = pool_id2str(pool, s->arch);
- if (!strncmp(dir, str, l) && !str[l])
- repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
- else
- repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
- }
- fp = file;
- str = pool_id2str(pool, s->name);
- l = strlen(str);
- if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
- {
- fp += l + 1;
- str = evrid2vrstr(pool, s->evr);
- l = strlen(str);
- if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
- {
- fp += l + 1;
- str = pool_id2str(pool, s->arch);
- l = strlen(str);
- if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
- {
- repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
- return;
- }
- }
- }
- repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
-}
-
-/* XXX: medianr is currently not stored */
-void
-repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
-{
- int l = 0;
- const char *evr, *suf, *s;
-
- if (!dir)
- {
- if ((dir = strrchr(file, '/')) != 0)
- {
- l = dir - file;
- dir = file;
- file = dir + l + 1;
- if (!l)
- l++;
- }
- }
- else
- l = strlen(dir);
- if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
- {
- dir += 2;
- l -= 2;
- }
- if (l == 1 && dir[0] == '.')
- l = 0;
- if (dir && l)
- repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
- evr = strchr(file, '-');
- if (evr)
- {
- for (s = evr - 1; s > file; s--)
- if (*s == '-')
- {
- evr = s;
- break;
- }
- }
- suf = strrchr(file, '.');
- if (suf)
- {
- for (s = suf - 1; s > file; s--)
- if (*s == '.')
- {
- suf = s;
- break;
- }
- if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
- {
- /* We accept one more item as suffix. */
- for (s = suf - 1; s > file; s--)
- if (*s == '.')
- {
- suf = s;
- break;
- }
- }
- }
- if (!evr)
- suf = 0;
- if (suf && evr && suf < evr)
- suf = 0;
- repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
- if (evr)
- repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
- if (suf)
- repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
-}
-
-void
-repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
-{
- Pool *pool = data->repo->pool;
- Solvable *s = pool->solvables + solvid;
- const char *p, *sevr, *sarch, *name, *evr;
-
- p = strrchr(sourcepkg, '.');
- if (!p || strcmp(p, ".rpm") != 0)
- {
- if (*sourcepkg)
- repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
- return;
- }
- p--;
- while (p > sourcepkg && *p != '.')
- p--;
- if (*p != '.' || p == sourcepkg)
- return;
- sarch = p-- + 1;
- while (p > sourcepkg && *p != '-')
- p--;
- if (*p != '-' || p == sourcepkg)
- return;
- p--;
- while (p > sourcepkg && *p != '-')
- p--;
- if (*p != '-' || p == sourcepkg)
- return;
- sevr = p + 1;
- pool = s->repo->pool;
-
- name = pool_id2str(pool, s->name);
- if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
- repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
- else
- repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
-
- evr = evrid2vrstr(pool, s->evr);
- if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
- repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
- else
- repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
-
- if (!strcmp(sarch, "src.rpm"))
- repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
- else if (!strcmp(sarch, "nosrc.rpm"))
- repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
- else
- repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
-}
-
-void
-repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
-{
- Repokey key;
- int i;
-
- key.name = keyname;
- key.type = REPOKEY_TYPE_IDARRAY;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- repodata_set(data, solvid, &key, data->attriddatalen);
- data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
- for (i = 0; i < q->count; i++)
- data->attriddata[data->attriddatalen++] = q->elements[i];
- data->attriddata[data->attriddatalen++] = 0;
-}
-
-void
-repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
-{
- assert(dir);
-#if 0
-fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
-#endif
- repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
- data->attriddata[data->attriddatalen++] = dir;
- data->attriddata[data->attriddatalen++] = num;
- data->attriddata[data->attriddatalen++] = num2;
- data->attriddata[data->attriddatalen++] = 0;
-}
-
-void
-repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
-{
- Id stroff;
- int l;
-
- assert(dir);
- l = strlen(str) + 1;
- data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
- memcpy(data->attrdata + data->attrdatalen, str, l);
- stroff = data->attrdatalen;
- data->attrdatalen += l;
-
-#if 0
-fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str, data->attriddatalen);
-#endif
- repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
- data->attriddata[data->attriddatalen++] = dir;
- data->attriddata[data->attriddatalen++] = stroff;
- data->attriddata[data->attriddatalen++] = 0;
-}
-
-void
-repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
-{
-#if 0
-fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
-#endif
- repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
- data->attriddata[data->attriddatalen++] = id;
- data->attriddata[data->attriddatalen++] = 0;
-}
-
-void
-repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
- const char *str)
-{
- Id id;
- if (data->localpool)
- id = stringpool_str2id(&data->spool, str, 1);
- else
- id = pool_str2id(data->repo->pool, str, 1);
- repodata_add_idarray(data, solvid, keyname, id);
-}
-
-void
-repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
-{
- repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
- data->attriddata[data->attriddatalen++] = ghandle;
- data->attriddata[data->attriddatalen++] = 0;
-}
-
-void
-repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
-{
- repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
- data->attriddata[data->attriddatalen++] = ghandle;
- data->attriddata[data->attriddatalen++] = 0;
-}
-
-void
-repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
-{
- Id *pp, *ap, **app;
- app = repodata_get_attrp(data, solvid);
- ap = *app;
- if (!ap)
- return;
- for (; *ap; ap += 2)
- if (data->keys[*ap].name == keyname)
- break;
- if (!*ap)
- return;
- pp = ap;
- ap += 2;
- for (; *ap; ap += 2)
- {
- if (data->keys[*ap].name == keyname)
- continue;
- *pp++ = ap[0];
- *pp++ = ap[1];
- }
- *pp = 0;
-}
-
-/* XXX: does not work correctly, needs fix in iterators! */
-void
-repodata_unset(Repodata *data, Id solvid, Id keyname)
-{
- Repokey key;
- key.name = keyname;
- key.type = REPOKEY_TYPE_DELETED;
- key.size = 0;
- key.storage = KEY_STORAGE_INCORE;
- repodata_set(data, solvid, &key, 0);
-}
-
-/* add all (uninternalized) attrs from src to dest */
-void
-repodata_merge_attrs(Repodata *data, Id dest, Id src)
-{
- Id *keyp;
- if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
- return;
- for (; *keyp; keyp += 2)
- repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
-}
-
-/* add some (uninternalized) attrs from src to dest */
-void
-repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
-{
- Id *keyp;
- if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
- return;
- for (; *keyp; keyp += 2)
- if (!keyidmap || MAPTST(keyidmap, keyp[0]))
- repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
-}
-
-/* swap (uninternalized) attrs from src and dest */
-void
-repodata_swap_attrs(Repodata *data, Id dest, Id src)
-{
- Id *tmpattrs;
- if (!data->attrs || dest == src)
- return;
- if (dest < data->start || dest >= data->end)
- repodata_extend(data, dest);
- if (src < data->start || src >= data->end)
- repodata_extend(data, src);
- tmpattrs = data->attrs[dest - data->start];
- data->attrs[dest - data->start] = data->attrs[src - data->start];
- data->attrs[src - data->start] = tmpattrs;
-}
-
-
-/**********************************************************************/
-
-/* TODO: unify with repo_write and repo_solv! */
-
-#define EXTDATA_BLOCK 1023
-
-struct extdata {
- unsigned char *buf;
- int len;
-};
-
-static void
-data_addid(struct extdata *xd, Id sx)
-{
- unsigned int x = (unsigned int)sx;
- unsigned char *dp;
-
- xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
- dp = xd->buf + xd->len;
-
- if (x >= (1 << 14))
- {
- if (x >= (1 << 28))
- *dp++ = (x >> 28) | 128;
- if (x >= (1 << 21))
- *dp++ = (x >> 21) | 128;
- *dp++ = (x >> 14) | 128;
- }
- if (x >= (1 << 7))
- *dp++ = (x >> 7) | 128;
- *dp++ = x & 127;
- xd->len = dp - xd->buf;
-}
-
-static void
-data_addid64(struct extdata *xd, unsigned long long x)
-{
- if (x >= 0x100000000)
- {
- if ((x >> 35) != 0)
- {
- data_addid(xd, (Id)(x >> 35));
- xd->buf[xd->len - 1] |= 128;
- }
- data_addid(xd, (Id)((unsigned int)x | 0x80000000));
- xd->buf[xd->len - 5] = (x >> 28) | 128;
- }
- else
- data_addid(xd, (Id)x);
-}
-
-static void
-data_addideof(struct extdata *xd, Id sx, int eof)
-{
- unsigned int x = (unsigned int)sx;
- unsigned char *dp;
-
- xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
- dp = xd->buf + xd->len;
-
- if (x >= (1 << 13))
- {
- if (x >= (1 << 27))
- *dp++ = (x >> 27) | 128;
- if (x >= (1 << 20))
- *dp++ = (x >> 20) | 128;
- *dp++ = (x >> 13) | 128;
- }
- if (x >= (1 << 6))
- *dp++ = (x >> 6) | 128;
- *dp++ = eof ? (x & 63) : (x & 63) | 64;
- xd->len = dp - xd->buf;
-}
-
-static void
-data_addblob(struct extdata *xd, unsigned char *blob, int len)
-{
- xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
- memcpy(xd->buf + xd->len, blob, len);
- xd->len += len;
-}
-
-/*********************************/
-
-/* this is to reduct memory usage when internalizing oversized repos */
-static void
-compact_attrdata(Repodata *data, int entry, int nentry)
-{
- int i;
- unsigned int attrdatastart = data->attrdatalen;
- unsigned int attriddatastart = data->attriddatalen;
- if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
- return;
- for (i = entry; i < nentry; i++)
- {
- Id v, *attrs = data->attrs[i];
- if (!attrs)
- continue;
- for (; *attrs; attrs += 2)
- {
- switch (data->keys[*attrs].type)
- {
- case REPOKEY_TYPE_STR:
- case REPOKEY_TYPE_BINARY:
- case_CHKSUM_TYPES:
- if ((unsigned int)attrs[1] < attrdatastart)
- attrdatastart = attrs[1];
- break;
- case REPOKEY_TYPE_DIRSTRARRAY:
- for (v = attrs[1]; data->attriddata[v] ; v += 2)
- if (data->attriddata[v + 1] < attrdatastart)
- attrdatastart = data->attriddata[v + 1];
- /* FALLTHROUGH */
- case REPOKEY_TYPE_IDARRAY:
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- if ((unsigned int)attrs[1] < attriddatastart)
- attriddatastart = attrs[1];
- break;
- case REPOKEY_TYPE_FIXARRAY:
- case REPOKEY_TYPE_FLEXARRAY:
- return;
- default:
- break;
- }
- }
- }
-#if 0
- printf("compact_attrdata %d %d\n", entry, nentry);
- printf("attrdatastart: %d\n", attrdatastart);
- printf("attriddatastart: %d\n", attriddatastart);
-#endif
- if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
- return;
- for (i = entry; i < nentry; i++)
- {
- Id v, *attrs = data->attrs[i];
- if (!attrs)
- continue;
- for (; *attrs; attrs += 2)
- {
- switch (data->keys[*attrs].type)
- {
- case REPOKEY_TYPE_STR:
- case REPOKEY_TYPE_BINARY:
- case_CHKSUM_TYPES:
- attrs[1] -= attrdatastart;
- break;
- case REPOKEY_TYPE_DIRSTRARRAY:
- for (v = attrs[1]; data->attriddata[v] ; v += 2)
- data->attriddata[v + 1] -= attrdatastart;
- /* FALLTHROUGH */
- case REPOKEY_TYPE_IDARRAY:
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- attrs[1] -= attriddatastart;
- break;
- default:
- break;
- }
- }
- }
- if (attrdatastart)
- {
- data->attrdatalen -= attrdatastart;
- memmove(data->attrdata, data->attrdata + attrdatastart, data->attrdatalen);
- data->attrdata = solv_extend_resize(data->attrdata, data->attrdatalen, 1, REPODATA_ATTRDATA_BLOCK);
- }
- if (attriddatastart)
- {
- data->attriddatalen -= attriddatastart;
- memmove(data->attriddata, data->attriddata + attriddatastart, data->attriddatalen * sizeof(Id));
- data->attriddata = solv_extend_resize(data->attriddata, data->attriddatalen, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
- }
-}
-
-/* internalalize some key into incore/vincore data */
-
-static void
-repodata_serialize_key(Repodata *data, struct extdata *newincore,
- struct extdata *newvincore,
- Id *schema,
- Repokey *key, Id val)
-{
- Id *ida;
- struct extdata *xd;
- unsigned int oldvincorelen = 0;
- Id schemaid, *sp;
-
- xd = newincore;
- if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- xd = newvincore;
- oldvincorelen = xd->len;
- }
- switch (key->type)
- {
- case REPOKEY_TYPE_VOID:
- case REPOKEY_TYPE_CONSTANT:
- case REPOKEY_TYPE_CONSTANTID:
- case REPOKEY_TYPE_DELETED:
- break;
- case REPOKEY_TYPE_STR:
- data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
- break;
- case REPOKEY_TYPE_MD5:
- data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
- break;
- case REPOKEY_TYPE_SHA1:
- data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
- break;
- case REPOKEY_TYPE_SHA224:
- data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
- break;
- case REPOKEY_TYPE_SHA256:
- data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
- break;
- case REPOKEY_TYPE_SHA384:
- data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
- break;
- case REPOKEY_TYPE_SHA512:
- data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
- break;
- case REPOKEY_TYPE_NUM:
- if (val & 0x80000000)
- {
- data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
- break;
- }
- /* FALLTHROUGH */
- case REPOKEY_TYPE_ID:
- case REPOKEY_TYPE_DIR:
- data_addid(xd, val);
- break;
- case REPOKEY_TYPE_BINARY:
- {
- Id len;
- unsigned char *dp = data_read_id(data->attrdata + val, &len);
- dp += (unsigned int)len;
- data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
- }
- break;
- case REPOKEY_TYPE_IDARRAY:
- for (ida = data->attriddata + val; *ida; ida++)
- data_addideof(xd, ida[0], ida[1] ? 0 : 1);
- break;
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- for (ida = data->attriddata + val; *ida; ida += 3)
- {
- data_addid(xd, ida[0]);
- data_addid(xd, ida[1]);
- data_addideof(xd, ida[2], ida[3] ? 0 : 1);
- }
- break;
- case REPOKEY_TYPE_DIRSTRARRAY:
- for (ida = data->attriddata + val; *ida; ida += 2)
- {
- data_addideof(xd, ida[0], ida[2] ? 0 : 1);
- data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
- }
- break;
- case REPOKEY_TYPE_FIXARRAY:
- {
- int num = 0;
- schemaid = 0;
- for (ida = data->attriddata + val; *ida; ida++)
- {
- Id *kp;
- sp = schema;
- kp = data->xattrs[-*ida];
- if (!kp)
- continue; /* ignore empty elements */
- num++;
- for (; *kp; kp += 2)
- *sp++ = *kp;
- *sp = 0;
- if (!schemaid)
- schemaid = repodata_schema2id(data, schema, 1);
- else if (schemaid != repodata_schema2id(data, schema, 0))
- {
- pool_debug(data->repo->pool, SOLV_ERROR, "repodata_serialize_key: fixarray substructs with different schemas\n");
- num = 0;
- break;
- }
- }
- data_addid(xd, num);
- if (!num)
- break;
- data_addid(xd, schemaid);
- for (ida = data->attriddata + val; *ida; ida++)
- {
- Id *kp = data->xattrs[-*ida];
- if (!kp)
- continue;
- for (; *kp; kp += 2)
- repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
- }
- break;
- }
- case REPOKEY_TYPE_FLEXARRAY:
- {
- int num = 0;
- for (ida = data->attriddata + val; *ida; ida++)
- num++;
- data_addid(xd, num);
- for (ida = data->attriddata + val; *ida; ida++)
- {
- Id *kp = data->xattrs[-*ida];
- if (!kp)
- {
- data_addid(xd, 0); /* XXX */
- continue;
- }
- sp = schema;
- for (;*kp; kp += 2)
- *sp++ = *kp;
- *sp = 0;
- schemaid = repodata_schema2id(data, schema, 1);
- data_addid(xd, schemaid);
- kp = data->xattrs[-*ida];
- for (;*kp; kp += 2)
- repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
- }
- break;
- }
- default:
- pool_debug(data->repo->pool, SOLV_FATAL, "repodata_serialize_key: don't know how to handle type %d\n", key->type);
- exit(1);
- }
- if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- /* put offset/len in incore */
- data_addid(newincore, data->lastverticaloffset + oldvincorelen);
- oldvincorelen = xd->len - oldvincorelen;
- data_addid(newincore, oldvincorelen);
- }
-}
-
-/* create a circular linked list of all keys that share
- * the same keyname */
-static Id *
-calculate_keylink(Repodata *data)
-{
- int i, j;
- Id *link;
- Id maxkeyname = 0, *keytable = 0;
- link = solv_calloc(data->nkeys, sizeof(Id));
- if (data->nkeys <= 2)
- return link;
- for (i = 1; i < data->nkeys; i++)
- {
- Id n = data->keys[i].name;
- if (n >= maxkeyname)
- {
- keytable = solv_realloc2(keytable, n + 128, sizeof(Id));
- memset(keytable + maxkeyname, 0, (n + 128 - maxkeyname) * sizeof(Id));
- maxkeyname = n + 128;
- }
- j = keytable[n];
- if (j)
- link[i] = link[j];
- else
- j = i;
- link[j] = i;
- keytable[n] = i;
- }
- /* remove links that just point to themselfs */
- for (i = 1; i < data->nkeys; i++)
- if (link[i] == i)
- link[i] = 0;
- solv_free(keytable);
- return link;
-}
-
-void
-repodata_internalize(Repodata *data)
-{
- Repokey *key, solvkey;
- Id entry, nentry;
- Id schemaid, keyid, *schema, *sp, oldschemaid, *keyp, *seen;
- Offset *oldincoreoffs = 0;
- int schemaidx;
- unsigned char *dp, *ndp;
- int neednewschema;
- struct extdata newincore;
- struct extdata newvincore;
- Id solvkeyid;
- Id *keylink;
- int haveoldkl;
-
- if (!data->attrs && !data->xattrs)
- return;
-
-#if 0
- printf("repodata_internalize %d\n", data->repodataid);
- printf(" attr data: %d K\n", data->attrdatalen / 1024);
- printf(" attrid data: %d K\n", data->attriddatalen / (1024 / 4));
-#endif
- newvincore.buf = data->vincore;
- newvincore.len = data->vincorelen;
-
- /* find the solvables key, create if needed */
- memset(&solvkey, 0, sizeof(solvkey));
- solvkey.name = REPOSITORY_SOLVABLES;
- solvkey.type = REPOKEY_TYPE_FLEXARRAY;
- solvkey.size = 0;
- solvkey.storage = KEY_STORAGE_INCORE;
- solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
-
- schema = solv_malloc2(data->nkeys, sizeof(Id));
- seen = solv_malloc2(data->nkeys, sizeof(Id));
-
- /* Merge the data already existing (in data->schemata, ->incoredata and
- friends) with the new attributes in data->attrs[]. */
- nentry = data->end - data->start;
- memset(&newincore, 0, sizeof(newincore));
- data_addid(&newincore, 0); /* start data at offset 1 */
-
- data->mainschema = 0;
- data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
-
- keylink = calculate_keylink(data);
- /* join entry data */
- /* we start with the meta data, entry -1 */
- for (entry = -1; entry < nentry; entry++)
- {
- oldschemaid = 0;
- dp = data->incoredata;
- if (dp)
- {
- dp += entry >= 0 ? data->incoreoffset[entry] : 1;
- dp = data_read_id(dp, &oldschemaid);
- }
- memset(seen, 0, data->nkeys * sizeof(Id));
-#if 0
-fprintf(stderr, "oldschemaid %d\n", oldschemaid);
-fprintf(stderr, "schemata %d\n", data->schemata[oldschemaid]);
-fprintf(stderr, "schemadata %p\n", data->schemadata);
-#endif
-
- /* seen: -1: old data, 0: skipped, >0: id + 1 */
- neednewschema = 0;
- sp = schema;
- haveoldkl = 0;
- for (keyp = data->schemadata + data->schemata[oldschemaid]; *keyp; keyp++)
- {
- if (seen[*keyp])
- {
- /* oops, should not happen */
- neednewschema = 1;
- continue;
- }
- seen[*keyp] = -1; /* use old marker */
- *sp++ = *keyp;
- if (keylink[*keyp])
- haveoldkl = 1; /* potential keylink conflict */
- }
-
- /* strip solvables key */
- if (entry < 0 && solvkeyid && seen[solvkeyid])
- {
- *sp = 0;
- for (sp = keyp = schema; *sp; sp++)
- if (*sp != solvkeyid)
- *keyp++ = *sp;
- sp = keyp;
- seen[solvkeyid] = 0;
- neednewschema = 1;
- }
-
- /* add new entries */
- if (entry >= 0)
- keyp = data->attrs ? data->attrs[entry] : 0;
- else
- keyp = data->xattrs ? data->xattrs[1] : 0;
- if (keyp)
- for (; *keyp; keyp += 2)
- {
- if (!seen[*keyp])
- {
- neednewschema = 1;
- *sp++ = *keyp;
- if (haveoldkl && keylink[*keyp]) /* this should be pretty rare */
- {
- Id kl;
- for (kl = keylink[*keyp]; kl != *keyp; kl = keylink[kl])
- if (seen[kl] == -1)
- {
- /* replacing old key kl, remove from schema and seen */
- Id *osp;
- for (osp = schema; osp < sp; osp++)
- if (*osp == kl)
- {
- memmove(osp, osp + 1, (sp - osp) * sizeof(Id));
- sp--;
- seen[kl] = 0;
- break;
- }
- }
- }
- }
- seen[*keyp] = keyp[1] + 1;
- }
-
- /* add solvables key if needed */
- if (entry < 0 && data->end != data->start)
- {
- *sp++ = solvkeyid; /* always last in schema */
- neednewschema = 1;
- }
-
- /* commit schema */
- *sp = 0;
- if (neednewschema)
- /* Ideally we'd like to sort the new schema here, to ensure
- schema equality independend of the ordering. */
- schemaid = repodata_schema2id(data, schema, 1);
- else
- schemaid = oldschemaid;
-
- if (entry < 0)
- {
- data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
- data->mainschema = schemaid;
- }
-
- /* find offsets in old incore data */
- if (oldschemaid)
- {
- Id *lastneeded = 0;
- for (sp = data->schemadata + data->schemata[oldschemaid]; *sp; sp++)
- if (seen[*sp] == -1)
- lastneeded = sp + 1;
- if (lastneeded)
- {
- if (!oldincoreoffs)
- oldincoreoffs = solv_malloc2(data->nkeys, 2 * sizeof(Offset));
- for (sp = data->schemadata + data->schemata[oldschemaid]; sp != lastneeded; sp++)
- {
- /* Skip the data associated with this old key. */
- key = data->keys + *sp;
- ndp = dp;
- if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
- {
- ndp = data_skip(ndp, REPOKEY_TYPE_ID);
- ndp = data_skip(ndp, REPOKEY_TYPE_ID);
- }
- else if (key->storage == KEY_STORAGE_INCORE)
- ndp = data_skip_key(data, ndp, key);
- oldincoreoffs[*sp * 2] = dp - data->incoredata;
- oldincoreoffs[*sp * 2 + 1] = ndp - dp;
- dp = ndp;
- }
- }
- }
-
- /* just copy over the complete old entry (including the schemaid) if there was no new data */
- if (entry >= 0 && !neednewschema && oldschemaid && (!data->attrs || !data->attrs[entry]) && dp)
- {
- ndp = data->incoredata + data->incoreoffset[entry];
- data->incoreoffset[entry] = newincore.len;
- data_addblob(&newincore, ndp, dp - ndp);
- goto entrydone;
- }
-
- /* Now create data blob. We walk through the (possibly new) schema
- and either copy over old data, or insert the new. */
- if (entry >= 0)
- data->incoreoffset[entry] = newincore.len;
- data_addid(&newincore, schemaid);
-
- /* we don't use a pointer to the schemadata here as repodata_serialize_key
- * may call repodata_schema2id() which might realloc our schemadata */
- for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
- {
- if (entry < 0)
- {
- data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
- if (keyid == solvkeyid)
- {
- /* add flexarray entry count */
- data_addid(&newincore, data->end - data->start);
- break; /* always the last entry */
- }
- }
- if (seen[keyid] == -1)
- {
- if (oldincoreoffs[keyid * 2 + 1])
- data_addblob(&newincore, data->incoredata + oldincoreoffs[keyid * 2], oldincoreoffs[keyid * 2 + 1]);
- }
- else if (seen[keyid])
- repodata_serialize_key(data, &newincore, &newvincore, schema, data->keys + keyid, seen[keyid] - 1);
- }
-
-entrydone:
- /* free memory */
- if (entry >= 0 && data->attrs)
- {
- if (data->attrs[entry])
- data->attrs[entry] = solv_free(data->attrs[entry]);
- if (entry && entry % 4096 == 0 && data->nxattrs <= 2 && entry + 64 < nentry)
- {
- compact_attrdata(data, entry + 1, nentry); /* try to free some memory */
-#if 0
- printf(" attr data: %d K\n", data->attrdatalen / 1024);
- printf(" attrid data: %d K\n", data->attriddatalen / (1024 / 4));
- printf(" incore data: %d K\n", newincore.len / 1024);
- printf(" sum: %d K\n", (newincore.len + data->attrdatalen + data->attriddatalen * 4) / 1024);
- /* malloc_stats(); */
-#endif
- }
- }
- }
- /* free all xattrs */
- for (entry = 0; entry < data->nxattrs; entry++)
- if (data->xattrs[entry])
- solv_free(data->xattrs[entry]);
- data->xattrs = solv_free(data->xattrs);
- data->nxattrs = 0;
-
- data->lasthandle = 0;
- data->lastkey = 0;
- data->lastdatalen = 0;
- solv_free(schema);
- solv_free(seen);
- solv_free(keylink);
- solv_free(oldincoreoffs);
- repodata_free_schemahash(data);
-
- solv_free(data->incoredata);
- data->incoredata = newincore.buf;
- data->incoredatalen = newincore.len;
- data->incoredatafree = 0;
-
- solv_free(data->vincore);
- data->vincore = newvincore.buf;
- data->vincorelen = newvincore.len;
-
- data->attrs = solv_free(data->attrs);
- data->attrdata = solv_free(data->attrdata);
- data->attriddata = solv_free(data->attriddata);
- data->attrnum64data = solv_free(data->attrnum64data);
- data->attrdatalen = 0;
- data->attriddatalen = 0;
- data->attrnum64datalen = 0;
-#if 0
- printf("repodata_internalize %d done\n", data->repodataid);
- printf(" incore data: %d K\n", data->incoredatalen / 1024);
-#endif
-}
-
-void
-repodata_disable_paging(Repodata *data)
-{
- if (maybe_load_repodata(data, 0))
- {
- repopagestore_disable_paging(&data->store);
- data->storestate++;
- }
-}
-
-static void
-repodata_load_stub(Repodata *data)
-{
- Repo *repo = data->repo;
- Pool *pool = repo->pool;
- int r, i;
- struct _Pool_tmpspace oldtmpspace;
- Datapos oldpos;
-
- if (!pool->loadcallback)
- {
- data->state = REPODATA_ERROR;
- return;
- }
- data->state = REPODATA_LOADING;
-
- /* save tmp space and pos */
- oldtmpspace = pool->tmpspace;
- memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
- oldpos = pool->pos;
-
- r = pool->loadcallback(pool, data, pool->loadcallbackdata);
-
- /* restore tmp space and pos */
- for (i = 0; i < POOL_TMPSPACEBUF; i++)
- solv_free(pool->tmpspace.buf[i]);
- pool->tmpspace = oldtmpspace;
- if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
- memset(&oldpos, 0, sizeof(oldpos));
- pool->pos = oldpos;
-
- data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
-}
-
-static inline void
-repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
-{
- Repokey xkey;
-
- xkey.name = keyname;
- xkey.type = keytype;
- xkey.storage = KEY_STORAGE_INCORE;
- xkey.size = 0;
- repodata_key2id(data, &xkey, 1);
-}
-
-static Repodata *
-repodata_add_stub(Repodata **datap)
-{
- Repodata *data = *datap;
- Repo *repo = data->repo;
- Id repodataid = data - repo->repodata;
- Repodata *sdata = repo_add_repodata(repo, 0);
- data = repo->repodata + repodataid;
- if (data->end > data->start)
- repodata_extend_block(sdata, data->start, data->end - data->start);
- sdata->state = REPODATA_STUB;
- sdata->loadcallback = repodata_load_stub;
- *datap = data;
- return sdata;
-}
-
-Repodata *
-repodata_create_stubs(Repodata *data)
-{
- Repo *repo = data->repo;
- Pool *pool = repo->pool;
- Repodata *sdata;
- int *stubdataids;
- Dataiterator di;
- Id xkeyname = 0;
- int i, cnt = 0;
-
- dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
- while (dataiterator_step(&di))
- if (di.data == data)
- cnt++;
- dataiterator_free(&di);
- if (!cnt)
- return data;
- stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
- for (i = 0; i < cnt; i++)
- {
- sdata = repodata_add_stub(&data);
- stubdataids[i] = sdata - repo->repodata;
- }
- i = 0;
- dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
- sdata = 0;
- while (dataiterator_step(&di))
- {
- if (di.data != data)
- continue;
- if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
- {
- dataiterator_entersub(&di);
- sdata = repo->repodata + stubdataids[i++];
- xkeyname = 0;
- continue;
- }
- switch (di.key->type)
- {
- case REPOKEY_TYPE_ID:
- repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
- break;
- case REPOKEY_TYPE_CONSTANTID:
- repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
- break;
- case REPOKEY_TYPE_STR:
- repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
- break;
- case REPOKEY_TYPE_VOID:
- repodata_set_void(sdata, SOLVID_META, di.key->name);
- break;
- case REPOKEY_TYPE_NUM:
- repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
- break;
- case_CHKSUM_TYPES:
- repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
- break;
- case REPOKEY_TYPE_IDARRAY:
- repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
- if (di.key->name == REPOSITORY_KEYS)
- {
- if (!xkeyname)
- {
- if (!di.kv.eof)
- xkeyname = di.kv.id;
- }
- else
- {
- repodata_add_stubkey(sdata, xkeyname, di.kv.id);
- xkeyname = 0;
- }
- }
- break;
- default:
- break;
- }
- }
- dataiterator_free(&di);
- for (i = 0; i < cnt; i++)
- repodata_internalize(repo->repodata + stubdataids[i]);
- solv_free(stubdataids);
- return data;
-}
-
-unsigned int
-repodata_memused(Repodata *data)
-{
- return data->incoredatalen + data->vincorelen;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repodata.h
- *
- */
-
-#ifndef LIBSOLV_REPODATA_H
-#define LIBSOLV_REPODATA_H
-
-#include <stdio.h>
-
-#include "pooltypes.h"
-#include "pool.h"
-#include "dirpool.h"
-
-#ifdef LIBSOLV_INTERNAL
-#include "repopage.h"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define SIZEOF_MD5 16
-#define SIZEOF_SHA1 20
-#define SIZEOF_SHA224 28
-#define SIZEOF_SHA256 32
-#define SIZEOF_SHA384 48
-#define SIZEOF_SHA512 64
-
-struct _Repo;
-struct _KeyValue;
-
-typedef struct _Repokey {
- Id name;
- Id type; /* REPOKEY_TYPE_xxx */
- unsigned int size;
- unsigned int storage; /* KEY_STORAGE_xxx */
-} Repokey;
-
-#define KEY_STORAGE_DROPPED 0
-#define KEY_STORAGE_SOLVABLE 1
-#define KEY_STORAGE_INCORE 2
-#define KEY_STORAGE_VERTICAL_OFFSET 3
-
-#ifdef LIBSOLV_INTERNAL
-struct dircache;
-#endif
-
-typedef struct _Repodata {
- Id repodataid; /* our id */
- struct _Repo *repo; /* back pointer to repo */
-
-#define REPODATA_AVAILABLE 0
-#define REPODATA_STUB 1
-#define REPODATA_ERROR 2
-#define REPODATA_STORE 3
-#define REPODATA_LOADING 4
-
- int state; /* available, stub or error */
-
- void (*loadcallback)(struct _Repodata *);
-
- int start; /* start of solvables this repodata is valid for */
- int end; /* last solvable + 1 of this repodata */
-
- Repokey *keys; /* keys, first entry is always zero */
- int nkeys; /* length of keys array */
- unsigned char keybits[32]; /* keyname hash */
-
- Id *schemata; /* schema -> offset into schemadata */
- int nschemata; /* number of schemata */
- Id *schemadata; /* schema storage */
-
- Stringpool spool; /* local string pool */
- int localpool; /* is local string pool used */
-
- Dirpool dirpool; /* local dir pool */
-
-#ifdef LIBSOLV_INTERNAL
- FILE *fp; /* file pointer of solv file */
- int error; /* corrupt solv file */
-
- unsigned int schemadatalen; /* schema storage size */
- Id *schematahash; /* unification helper */
-
- unsigned char *incoredata; /* in-core data */
- unsigned int incoredatalen; /* in-core data used */
- unsigned int incoredatafree; /* free data len */
-
- Id mainschema; /* SOLVID_META schema */
- Id *mainschemaoffsets; /* SOLVID_META offsets into incoredata */
-
- Id *incoreoffset; /* offset for all entries */
-
- Id *verticaloffset; /* offset for all verticals, nkeys elements */
- Id lastverticaloffset; /* end of verticals */
-
- Repopagestore store; /* our page store */
- Id storestate; /* incremented every time the store might change */
-
- unsigned char *vincore; /* internal vertical data */
- unsigned int vincorelen; /* data size */
-
- Id **attrs; /* un-internalized attributes */
- Id **xattrs; /* anonymous handles */
- int nxattrs; /* number of handles */
-
- unsigned char *attrdata; /* their string data space */
- unsigned int attrdatalen; /* its len */
- Id *attriddata; /* their id space */
- unsigned int attriddatalen; /* its len */
- unsigned long long *attrnum64data; /* their 64bit num data space */
- unsigned int attrnum64datalen; /* its len */
-
- /* array cache to speed up repodata_add functions*/
- Id lasthandle;
- Id lastkey;
- Id lastdatalen;
-
- /* directory cache to speed up repodata_str2dir */
- struct dircache *dircache;
-#endif
-
-} Repodata;
-
-#define SOLVID_META -1
-#define SOLVID_POS -2
-#define SOLVID_SUBSCHEMA -3 /* internal! */
-
-
-/*-----
- * management functions
- */
-void repodata_initdata(Repodata *data, struct _Repo *repo, int localpool);
-void repodata_freedata(Repodata *data);
-
-void repodata_free(Repodata *data);
-void repodata_empty(Repodata *data, int localpool);
-
-
-/*
- * key management functions
- */
-Id repodata_key2id(Repodata *data, Repokey *key, int create);
-
-static inline Repokey *
-repodata_id2key(Repodata *data, Id keyid)
-{
- return data->keys + keyid;
-}
-
-/*
- * schema management functions
- */
-Id repodata_schema2id(Repodata *data, Id *schema, int create);
-void repodata_free_schemahash(Repodata *data);
-
-static inline Id *
-repodata_id2schema(Repodata *data, Id schemaid)
-{
- return data->schemadata + data->schemata[schemaid];
-}
-
-/*
- * data search and access
- */
-
-/* check if there is a chance that the repodata contains data for
- * the specified keyname */
-static inline int
-repodata_precheck_keyname(Repodata *data, Id keyname)
-{
- unsigned char x = data->keybits[(keyname >> 3) & (sizeof(data->keybits) - 1)];
- return x && (x & (1 << (keyname & 7))) ? 1 : 0;
-}
-
-/* check if the repodata contains data for the specified keyname */
-static inline int
-repodata_has_keyname(Repodata *data, Id keyname)
-{
- int i;
- if (!repodata_precheck_keyname(data, keyname))
- return 0;
- for (i = 1; i < data->nkeys; i++)
- if (data->keys[i].name == keyname)
- return 1;
- return 0;
-}
-
-/* search key <keyname> (all keys, if keyname == 0) for Id <solvid>
- * Call <callback> for each match */
-void repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, struct _KeyValue *kv), void *cbdata);
-
-/* Make sure the found KeyValue has the "str" field set. Return "str"
- * if valid, NULL if not possible */
-const char *repodata_stringify(Pool *pool, Repodata *data, Repokey *key, struct _KeyValue *kv, int flags);
-
-int repodata_filelistfilter_matches(Repodata *data, const char *str);
-
-
-/* lookup functions */
-Id repodata_lookup_type(Repodata *data, Id solvid, Id keyname);
-Id repodata_lookup_id(Repodata *data, Id solvid, Id keyname);
-const char *repodata_lookup_str(Repodata *data, Id solvid, Id keyname);
-int repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value);
-int repodata_lookup_void(Repodata *data, Id solvid, Id keyname);
-const unsigned char *repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep);
-int repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q);
-const void *repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp);
-
-
-/*-----
- * data assignment functions
- */
-
-/*
- * extend the data so that it contains the specified solvables
- * (no longer needed, as the repodata_set functions autoextend)
- */
-void repodata_extend(Repodata *data, Id p);
-void repodata_extend_block(Repodata *data, Id p, int num);
-void repodata_shrink(Repodata *data, int end);
-
-/* internalize freshly set data, so that it is found by the search
- * functions and written out */
-void repodata_internalize(Repodata *data);
-
-/* create an anonymous handle. useful for substructures like
- * fixarray/flexarray */
-Id repodata_new_handle(Repodata *data);
-
-/* basic types: void, num, string, Id */
-void repodata_set_void(Repodata *data, Id solvid, Id keyname);
-void repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num);
-void repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id);
-void repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str);
-void repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len);
-/* create id from string, then set_id */
-void repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str);
-
-/* set numeric constant */
-void repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant);
-
-/* set Id constant */
-void repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id);
-
-/* checksum */
-void repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
- const unsigned char *buf);
-void repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
- const char *str);
-void repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q);
-
-
-/* directory (for package file list) */
-void repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2);
-void repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str);
-void repodata_free_dircache(Repodata *data);
-
-
-/* Arrays */
-void repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id);
-void repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname, const char *str);
-void repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle);
-void repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle);
-
-void repodata_unset(Repodata *data, Id solvid, Id keyname);
-void repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname);
-
-/*
- merge/swap attributes from one solvable to another
- works only if the data is not yet internalized
-*/
-void repodata_merge_attrs(Repodata *data, Id dest, Id src);
-void repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite);
-void repodata_swap_attrs(Repodata *data, Id dest, Id src);
-
-Repodata *repodata_create_stubs(Repodata *data);
-
-/*
- * load all paged data, used to speed up copying in repo_rpmdb
- */
-void repodata_disable_paging(Repodata *data);
-
-/* helper functions */
-Id repodata_globalize_id(Repodata *data, Id id, int create);
-Id repodata_localize_id(Repodata *data, Id id, int create);
-Id repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create);
-
-Id repodata_str2dir(Repodata *data, const char *dir, int create);
-const char *repodata_dir2str(Repodata *data, Id did, const char *suf);
-const char *repodata_chk2str(Repodata *data, Id type, const unsigned char *buf);
-void repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file);
-void repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file);
-void repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg);
-Id repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid);
-const char *repodata_lookup_dirstrarray_uninternalized(Repodata *data, Id solvid, Id keyname, Id *didp, Id *iterp);
-
-/* stats */
-unsigned int repodata_memused(Repodata *data);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_REPODATA_H */
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/* pack/unpack functions for key data */
-
-#ifndef LIBSOLV_REPOPACK_H
-#define LIBSOLV_REPOPACK_H
-
-static inline unsigned char *
-data_read_id(unsigned char *dp, Id *idp)
-{
- Id x;
- unsigned char c;
- if (!(dp[0] & 0x80))
- {
- *idp = dp[0];
- return dp + 1;
- }
- if (!(dp[1] & 0x80))
- {
- *idp = dp[0] << 7 ^ dp[1] ^ 0x4000;
- return dp + 2;
- }
- if (!(dp[2] & 0x80))
- {
- *idp = dp[0] << 14 ^ dp[1] << 7 ^ dp[2] ^ 0x204000;
- return dp + 3;
- }
- if (!(dp[3] & 0x80))
- {
- *idp = dp[0] << 21 ^ dp[1] << 14 ^ dp[2] << 7 ^ dp[3] ^ 0x10204000;
- return dp + 4;
- }
- x = dp[0] << 28 ^ dp[1] << 21 ^ dp[2] << 14 ^ dp[3] << 7 ^ dp[4] ^ 0x10204000;
- if (!(dp[4] & 0x80))
- {
- *idp = x;
- return dp + 5;
- }
- x ^= 80;
- dp += 5;
- for (;;)
- {
- c = *dp++;
- if (!(c & 0x80))
- {
- *idp = (x << 7) ^ c;
- return dp;
- }
- x = (x << 7) ^ (c ^ 128);
- }
-}
-
-static inline unsigned char *
-data_read_num64(unsigned char *dp, unsigned int *low, unsigned int *high)
-{
- unsigned long long int x;
- unsigned char c;
-
- *high = 0;
- if (!(dp[0] & 0x80))
- {
- *low = dp[0];
- return dp + 1;
- }
- if (!(dp[1] & 0x80))
- {
- *low = dp[0] << 7 ^ dp[1] ^ 0x4000;
- return dp + 2;
- }
- if (!(dp[2] & 0x80))
- {
- *low = dp[0] << 14 ^ dp[1] << 7 ^ dp[2] ^ 0x204000;
- return dp + 3;
- }
- if (!(dp[3] & 0x80))
- {
- *low = dp[0] << 21 ^ dp[1] << 14 ^ dp[2] << 7 ^ dp[3] ^ 0x10204000;
- return dp + 4;
- }
- if (!(dp[4] & 0x80))
- {
- *low = dp[0] << 28 ^ dp[1] << 21 ^ dp[2] << 14 ^ dp[3] << 7 ^ dp[4] ^ 0x10204000;
- *high = (dp[0] ^ 0x80) >> 4;
- return dp + 5;
- }
- x = (unsigned long long)(dp[0] ^ 0x80) << 28 ^ (unsigned int)(dp[1] << 21 ^ dp[2] << 14 ^ dp[3] << 7 ^ dp[4] ^ 0x10204080);
- dp += 5;
- for (;;)
- {
- c = *dp++;
- if (!(c & 0x80))
- {
- x = (x << 7) ^ c;
- *low = x;
- *high = x >> 32;
- return dp;
- }
- x = (x << 7) ^ (c ^ 128);
- }
-}
-
-static inline unsigned char *
-data_read_ideof(unsigned char *dp, Id *idp, int *eof)
-{
- Id x = 0;
- unsigned char c;
- for (;;)
- {
- c = *dp++;
- if (!(c & 0x80))
- {
- if (c & 0x40)
- {
- c ^= 0x40;
- *eof = 0;
- }
- else
- *eof = 1;
- *idp = (x << 6) ^ c;
- return dp;
- }
- x = (x << 7) ^ c ^ 128;
- }
-}
-
-static inline unsigned char *
-data_read_u32(unsigned char *dp, unsigned int *nump)
-{
- *nump = (dp[0] << 24) | (dp[1] << 16) | (dp[2] << 8) | dp[3];
- return dp + 4;
-}
-
-static inline unsigned char *
-data_fetch(unsigned char *dp, KeyValue *kv, Repokey *key)
-{
- kv->eof = 1;
- if (!dp)
- return 0;
- switch (key->type)
- {
- case REPOKEY_TYPE_VOID:
- return dp;
- case REPOKEY_TYPE_CONSTANT:
- kv->num2 = 0;
- kv->num = key->size;
- return dp;
- case REPOKEY_TYPE_CONSTANTID:
- kv->id = key->size;
- return dp;
- case REPOKEY_TYPE_STR:
- kv->str = (const char *)dp;
- return dp + strlen(kv->str) + 1;
- case REPOKEY_TYPE_ID:
- case REPOKEY_TYPE_DIR:
- return data_read_id(dp, &kv->id);
- case REPOKEY_TYPE_NUM:
- return data_read_num64(dp, &kv->num, &kv->num2);
- case REPOKEY_TYPE_U32:
- kv->num2 = 0;
- return data_read_u32(dp, &kv->num);
- case REPOKEY_TYPE_MD5:
- kv->num = 0; /* not stringified yet */
- kv->str = (const char *)dp;
- return dp + SIZEOF_MD5;
- case REPOKEY_TYPE_SHA1:
- kv->num = 0; /* not stringified yet */
- kv->str = (const char *)dp;
- return dp + SIZEOF_SHA1;
- case REPOKEY_TYPE_SHA224:
- kv->num = 0; /* not stringified yet */
- kv->str = (const char *)dp;
- return dp + SIZEOF_SHA224;
- case REPOKEY_TYPE_SHA256:
- kv->num = 0; /* not stringified yet */
- kv->str = (const char *)dp;
- return dp + SIZEOF_SHA256;
- case REPOKEY_TYPE_SHA384:
- kv->num = 0; /* not stringified yet */
- kv->str = (const char *)dp;
- return dp + SIZEOF_SHA384;
- case REPOKEY_TYPE_SHA512:
- kv->num = 0; /* not stringified yet */
- kv->str = (const char *)dp;
- return dp + SIZEOF_SHA512;
- case REPOKEY_TYPE_BINARY:
- dp = data_read_id(dp, (Id *)&kv->num);
- kv->str = (const char *)dp;
- return dp + kv->num;
- case REPOKEY_TYPE_IDARRAY:
- return data_read_ideof(dp, &kv->id, &kv->eof);
- case REPOKEY_TYPE_DIRSTRARRAY:
- dp = data_read_ideof(dp, &kv->id, &kv->eof);
- kv->num = 0; /* not stringified yet */
- kv->str = (const char *)dp;
- return dp + strlen(kv->str) + 1;
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- dp = data_read_id(dp, &kv->id);
- dp = data_read_id(dp, (Id *)&kv->num);
- return data_read_ideof(dp, (Id *)&kv->num2, &kv->eof);
- case REPOKEY_TYPE_FIXARRAY:
- dp = data_read_id(dp, (Id *)&kv->num);
- return data_read_id(dp, &kv->id);
- case REPOKEY_TYPE_FLEXARRAY:
- return data_read_id(dp, (Id *)&kv->num);
- default:
- return 0;
- }
-}
-
-static inline unsigned char *
-data_skip(unsigned char *dp, int type)
-{
- unsigned char x;
- switch (type)
- {
- case REPOKEY_TYPE_VOID:
- case REPOKEY_TYPE_CONSTANT:
- case REPOKEY_TYPE_CONSTANTID:
- case REPOKEY_TYPE_DELETED:
- return dp;
- case REPOKEY_TYPE_ID:
- case REPOKEY_TYPE_NUM:
- case REPOKEY_TYPE_DIR:
- while ((*dp & 0x80) != 0)
- dp++;
- return dp + 1;
- case REPOKEY_TYPE_U32:
- return dp + 4;
- case REPOKEY_TYPE_MD5:
- return dp + SIZEOF_MD5;
- case REPOKEY_TYPE_SHA1:
- return dp + SIZEOF_SHA1;
- case REPOKEY_TYPE_SHA224:
- return dp + SIZEOF_SHA224;
- case REPOKEY_TYPE_SHA256:
- return dp + SIZEOF_SHA256;
- case REPOKEY_TYPE_SHA384:
- return dp + SIZEOF_SHA384;
- case REPOKEY_TYPE_SHA512:
- return dp + SIZEOF_SHA512;
- case REPOKEY_TYPE_IDARRAY:
- case REPOKEY_TYPE_REL_IDARRAY:
- while ((*dp & 0xc0) != 0)
- dp++;
- return dp + 1;
- case REPOKEY_TYPE_STR:
- while ((*dp) != 0)
- dp++;
- return dp + 1;
- case REPOKEY_TYPE_BINARY:
- {
- unsigned int len;
- dp = data_read_id(dp, (Id *)&len);
- return dp + len;
- }
- case REPOKEY_TYPE_DIRSTRARRAY:
- for (;;)
- {
- while ((*dp & 0x80) != 0)
- dp++;
- x = *dp++;
- while ((*dp) != 0)
- dp++;
- dp++;
- if (!(x & 0x40))
- return dp;
- }
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- for (;;)
- {
- while ((*dp & 0x80) != 0)
- dp++;
- dp++;
- while ((*dp & 0x80) != 0)
- dp++;
- dp++;
- while ((*dp & 0x80) != 0)
- dp++;
- if (!(*dp & 0x40))
- return dp + 1;
- dp++;
- }
- default:
- return 0;
- }
-}
-
-static inline unsigned char *
-data_skip_verify(unsigned char *dp, int type, int maxid, int maxdir)
-{
- Id id;
- int eof;
-
- switch (type)
- {
- case REPOKEY_TYPE_VOID:
- case REPOKEY_TYPE_CONSTANT:
- case REPOKEY_TYPE_CONSTANTID:
- case REPOKEY_TYPE_DELETED:
- return dp;
- case REPOKEY_TYPE_NUM:
- while ((*dp & 0x80) != 0)
- dp++;
- return dp + 1;
- case REPOKEY_TYPE_U32:
- return dp + 4;
- case REPOKEY_TYPE_MD5:
- return dp + SIZEOF_MD5;
- case REPOKEY_TYPE_SHA1:
- return dp + SIZEOF_SHA1;
- case REPOKEY_TYPE_SHA224:
- return dp + SIZEOF_SHA224;
- case REPOKEY_TYPE_SHA256:
- return dp + SIZEOF_SHA256;
- case REPOKEY_TYPE_SHA384:
- return dp + SIZEOF_SHA384;
- case REPOKEY_TYPE_SHA512:
- return dp + SIZEOF_SHA512;
- case REPOKEY_TYPE_ID:
- dp = data_read_id(dp, &id);
- if (id >= maxid)
- return 0;
- return dp;
- case REPOKEY_TYPE_DIR:
- dp = data_read_id(dp, &id);
- if (id >= maxdir)
- return 0;
- return dp;
- case REPOKEY_TYPE_IDARRAY:
- for (;;)
- {
- dp = data_read_ideof(dp, &id, &eof);
- if (id >= maxid)
- return 0;
- if (eof)
- return dp;
- }
- case REPOKEY_TYPE_STR:
- while ((*dp) != 0)
- dp++;
- return dp + 1;
- case REPOKEY_TYPE_BINARY:
- {
- unsigned int len;
- dp = data_read_id(dp, (Id *)&len);
- return dp + len;
- }
- case REPOKEY_TYPE_DIRSTRARRAY:
- for (;;)
- {
- dp = data_read_ideof(dp, &id, &eof);
- if (id >= maxdir)
- return 0;
- while ((*dp) != 0)
- dp++;
- dp++;
- if (eof)
- return dp;
- }
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- for (;;)
- {
- dp = data_read_id(dp, &id);
- if (id >= maxdir)
- return 0;
- while ((*dp & 0x80) != 0)
- dp++;
- dp++;
- while ((*dp & 0x80) != 0)
- dp++;
- if (!(*dp & 0x40))
- return dp + 1;
- dp++;
- }
- default:
- return 0;
- }
-}
-
-#endif /* LIBSOLV_REPOPACK */
+++ /dev/null
-/*
- * Copyright (c) 2007-2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * repopage.c
- *
- * Paging and compression functions for the vertical repository data.
- * Vertical data is grouped by key, normal data is grouped by solvable.
- * This makes searching for a string in vertical data fast as there's
- * no need to skip over data if keys we're not interested in.
- *
- * The vertical data is split into pages, each page is compressed with a fast
- * compression algorithm. These pages are read in on demand, not recently used
- * pages automatically get dropped.
- */
-
-#define _XOPEN_SOURCE 500
-
-#include <sys/types.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <assert.h>
-#include <fcntl.h>
-#include <time.h>
-
-#include "repo.h"
-#include "repopage.h"
-
-
-
-#define BLOCK_SIZE (65536*1)
-#if BLOCK_SIZE <= 65536
-typedef uint16_t Ref;
-#else
-typedef uint32_t Ref;
-#endif
-
-/*
- The format is tailored for fast decompression (i.e. only byte based),
- and skewed to ASCII content (highest bit often not set):
-
- a 0LLLLLLL
- - self-describing ASCII character hex L
- b 100lllll <l+1 bytes>
- - literal run of length l+1
- c 101oolll <8o>
- - back ref of length l+2, at offset -(o+1) (o < 1 << 10)
- d 110lllll <8o>
- - back ref of length l+2+8, at offset -(o+1) (o < 1 << 8)
- e 1110llll <8o> <8o>
- - back ref of length l+3, at offset -(o+1) (o < 1 << 16)
- f1 1111llll <8l> <8o> <8o>
- - back ref, length l+19 (l < 1<<12), offset -(o+1) (o < 1<<16)
- f2 11110lll <8l> <8o> <8o>
- - back ref, length l+19 (l < 1<<11), offset -(o+1) (o < 1<<16)
- g 11111lll <8l> <8o> <8o> <8o>
- - back ref, length l+5 (l < 1<<11), offset -(o+1) (o < 1<<24)
-
- Generally for a literal of length L we need L+1 bytes, hence it is
- better to encode also very short backrefs (2 chars) as backrefs if
- their offset is small, as that only needs two bytes. Except if we
- already have a literal run, in that case it's better to append there,
- instead of breaking it for a backref. So given a potential backref
- at offset O, length L the strategy is as follows:
-
- L < 2 : encode as 1-literal
- L == 2, O > 1024 : encode as 1-literal
- L == 2, have already literals: encode as 1-literal
- O = O - 1
- L >= 2, L <= 9, O < 1024 : encode as c
- L >= 10, L <= 41, O < 256 : encode as d
- else we have either O >= 1024, or L >= 42:
- L < 3 : encode as 1-literal
- L >= 3, L <= 18, O < 65536 : encode as e
- L >= 19, L <= 4095+18, O < 65536 : encode as f
- else we have either L >= 4096+18 or O >= 65536.
- O >= 65536: encode as 1-literal, too bad
- (with the current block size this can't happen)
- L >= 4096+18, so reduce to 4095+18 : encode as f
-*/
-
-
-static unsigned int
-compress_buf(const unsigned char *in, unsigned int in_len,
- unsigned char *out, unsigned int out_len)
-{
- unsigned int oo = 0; /* out-offset */
- unsigned int io = 0; /* in-offset */
-#define HS (65536)
- Ref htab[HS];
- Ref hnext[BLOCK_SIZE];
- unsigned int litofs = 0;
- memset(htab, -1, sizeof (htab));
- memset(hnext, -1, sizeof (hnext));
- while (io + 2 < in_len)
- {
- /* Search for a match of the string starting at IN, we have at
- least three characters. */
- unsigned int hval = in[io] | in[io + 1] << 8 | in[io + 2] << 16;
- unsigned int try, mlen, mofs, tries;
- hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
- hval = hval & (HS - 1);
- try = htab[hval];
- hnext[io] = htab[hval];
- htab[hval] = io;
- mlen = 0;
- mofs = 0;
-
- for (tries = 0; try != -1 && tries < 12; tries++)
- {
- if (try < io
- && in[try] == in[io] && in[try + 1] == in[io + 1])
- {
- mlen = 2;
- mofs = (io - try) - 1;
- break;
- }
- try = hnext[try];
- }
- for (; try != -1 && tries < 12; tries++)
- {
- /* assert(mlen >= 2); */
- /* assert(io + mlen < in_len); */
- /* Try a match starting from [io] with the strings at [try].
- That's only sensible if TRY actually is before IO (can happen
- with uninit hash table). If we have a previous match already
- we're only going to take the new one if it's longer, hence
- check the potentially last character. */
- if (try < io && in[try + mlen] == in[io + mlen])
- {
- unsigned int this_len, this_ofs;
- if (memcmp(in + try, in + io, mlen))
- goto no_match;
- this_len = mlen + 1;
- /* Now try extending the match by more characters. */
- for (;
- io + this_len < in_len
- && in[try + this_len] == in[io + this_len]; this_len++)
- ;
-#if 0
- unsigned int testi;
- for (testi = 0; testi < this_len; testi++)
- assert(in[try + testi] == in[io + testi]);
-#endif
- this_ofs = (io - try) - 1;
- /*if (this_ofs > 65535)
- goto no_match; */
-#if 0
- assert(this_len >= 2);
- assert(this_len >= mlen);
- assert(this_len > mlen || (this_len == mlen && this_ofs > mofs));
-#endif
- mlen = this_len, mofs = this_ofs;
- /* If our match extends up to the end of input, no next
- match can become better. This is not just an
- optimization, it establishes a loop invariant
- (io + mlen < in_len). */
- if (io + mlen >= in_len)
- goto match_done;
- }
- no_match:
- try = hnext[try];
- /*if (io - try - 1 >= 65536)
- break;*/
- }
-
-match_done:
- if (mlen)
- {
- /*fprintf(stderr, "%d %d\n", mlen, mofs);*/
- if (mlen == 2 && (litofs || mofs >= 1024))
- mlen = 0;
- /*else if (mofs >= 65536)
- mlen = 0;*/
- else if (mofs >= 65536)
- {
- if (mlen >= 2048 + 5)
- mlen = 2047 + 5;
- else if (mlen < 5)
- mlen = 0;
- }
- else if (mlen < 3)
- mlen = 0;
- /*else if (mlen >= 4096 + 19)
- mlen = 4095 + 19;*/
- else if (mlen >= 2048 + 19)
- mlen = 2047 + 19;
- /* Skip this match if the next character would deliver a better one,
- but only do this if we have the chance to really extend the
- length (i.e. our current length isn't yet the (conservative)
- maximum). */
- if (mlen && mlen < (2048 + 5) && io + 3 < in_len)
- {
- unsigned int hval =
- in[io + 1] | in[io + 2] << 8 | in[io + 3] << 16;
- unsigned int try;
- hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
- hval = hval & (HS - 1);
- try = htab[hval];
- if (try < io + 1
- && in[try] == in[io + 1] && in[try + 1] == in[io + 2])
- {
- unsigned int this_len;
- this_len = 2;
- for (;
- io + 1 + this_len < in_len
- && in[try + this_len] == in[io + 1 + this_len];
- this_len++)
- ;
- if (this_len >= mlen)
- mlen = 0;
- }
- }
- }
- if (!mlen)
- {
- if (!litofs)
- litofs = io + 1;
- io++;
- }
- else
- {
- if (litofs)
- {
- unsigned litlen;
- litofs--;
- litlen = io - litofs;
- /* fprintf(stderr, "lit: %d\n", litlen); */
- while (litlen)
- {
- unsigned int easy_sz;
- /* Emit everything we can as self-describers. As soon as
- we hit a byte we can't emit as such we're going to emit
- a length descriptor anyway, so we can as well include
- bytes < 0x80 which might follow afterwards in that run. */
- for (easy_sz = 0;
- easy_sz < litlen && in[litofs + easy_sz] < 0x80;
- easy_sz++)
- ;
- if (easy_sz)
- {
- if (oo + easy_sz >= out_len)
- return 0;
- memcpy(out + oo, in + litofs, easy_sz);
- litofs += easy_sz;
- oo += easy_sz;
- litlen -= easy_sz;
- if (!litlen)
- break;
- }
- if (litlen <= 32)
- {
- if (oo + 1 + litlen >= out_len)
- return 0;
- out[oo++] = 0x80 | (litlen - 1);
- while (litlen--)
- out[oo++] = in[litofs++];
- break;
- }
- else
- {
- /* Literal length > 32, so chunk it. */
- if (oo + 1 + 32 >= out_len)
- return 0;
- out[oo++] = 0x80 | 31;
- memcpy(out + oo, in + litofs, 32);
- oo += 32;
- litofs += 32;
- litlen -= 32;
- }
- }
- litofs = 0;
- }
-
- /* fprintf(stderr, "ref: %d @ %d\n", mlen, mofs); */
-
- if (mlen >= 2 && mlen <= 9 && mofs < 1024)
- {
- if (oo + 2 >= out_len)
- return 0;
- out[oo++] = 0xa0 | ((mofs & 0x300) >> 5) | (mlen - 2);
- out[oo++] = mofs & 0xff;
- }
- else if (mlen >= 10 && mlen <= 41 && mofs < 256)
- {
- if (oo + 2 >= out_len)
- return 0;
- out[oo++] = 0xc0 | (mlen - 10);
- out[oo++] = mofs;
- }
- else if (mofs >= 65536)
- {
- assert(mlen >= 5 && mlen < 2048 + 5);
- if (oo + 5 >= out_len)
- return 0;
- out[oo++] = 0xf8 | ((mlen - 5) >> 8);
- out[oo++] = (mlen - 5) & 0xff;
- out[oo++] = mofs & 0xff;
- out[oo++] = (mofs >> 8) & 0xff;
- out[oo++] = mofs >> 16;
- }
- else if (mlen >= 3 && mlen <= 18)
- {
- assert(mofs < 65536);
- if (oo + 3 >= out_len)
- return 0;
- out[oo++] = 0xe0 | (mlen - 3);
- out[oo++] = mofs & 0xff;
- out[oo++] = mofs >> 8;
- }
- else
- {
- assert(mlen >= 19 && mlen <= 4095 + 19 && mofs < 65536);
- if (oo + 4 >= out_len)
- return 0;
- out[oo++] = 0xf0 | ((mlen - 19) >> 8);
- out[oo++] = (mlen - 19) & 0xff;
- out[oo++] = mofs & 0xff;
- out[oo++] = mofs >> 8;
- }
- /* Insert the hashes for the compressed run [io..io+mlen-1].
- For [io] we have it already done at the start of the loop.
- So it's from [io+1..io+mlen-1], and we need three chars per
- hash, so the accessed characters will be [io+1..io+mlen-1+2],
- ergo io+mlen+1 < in_len. */
- mlen--;
- io++;
- while (mlen--)
- {
- if (io + 2 < in_len)
- {
- unsigned int hval =
- in[io] | in[io + 1] << 8 | in[io + 2] << 16;
- hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
- hval = hval & (HS - 1);
- hnext[io] = htab[hval];
- htab[hval] = io;
- }
- io++;
- };
- }
- }
- /* We might have some characters left. */
- if (io < in_len && !litofs)
- litofs = io + 1;
- io = in_len;
- if (litofs)
- {
- unsigned litlen;
- litofs--;
- litlen = io - litofs;
- /* fprintf(stderr, "lit: %d\n", litlen); */
- while (litlen)
- {
- unsigned int easy_sz;
- /* Emit everything we can as self-describers. As soon as we hit a
- byte we can't emit as such we're going to emit a length
- descriptor anyway, so we can as well include bytes < 0x80 which
- might follow afterwards in that run. */
- for (easy_sz = 0; easy_sz < litlen && in[litofs + easy_sz] < 0x80;
- easy_sz++)
- ;
- if (easy_sz)
- {
- if (oo + easy_sz >= out_len)
- return 0;
- memcpy(out + oo, in + litofs, easy_sz);
- litofs += easy_sz;
- oo += easy_sz;
- litlen -= easy_sz;
- if (!litlen)
- break;
- }
- if (litlen <= 32)
- {
- if (oo + 1 + litlen >= out_len)
- return 0;
- out[oo++] = 0x80 | (litlen - 1);
- while (litlen--)
- out[oo++] = in[litofs++];
- break;
- }
- else
- {
- /* Literal length > 32, so chunk it. */
- if (oo + 1 + 32 >= out_len)
- return 0;
- out[oo++] = 0x80 | 31;
- memcpy(out + oo, in + litofs, 32);
- oo += 32;
- litofs += 32;
- litlen -= 32;
- }
- }
- litofs = 0;
- }
- return oo;
-}
-
-static unsigned int
-unchecked_decompress_buf(const unsigned char *in, unsigned int in_len,
- unsigned char *out,
- unsigned int out_len __attribute__((unused)))
-{
- unsigned char *orig_out = out;
- const unsigned char *in_end = in + in_len;
- while (in < in_end)
- {
- unsigned int first = *in++;
- int o;
- switch (first >> 4)
- {
- default:
- /* This default case can't happen, but GCCs VRP is not strong
- enough to see this, so make this explicitely not fall to
- the end of the switch, so that we don't have to initialize
- o above. */
- continue;
- case 0: case 1:
- case 2: case 3:
- case 4: case 5:
- case 6: case 7:
- /* a 0LLLLLLL */
- /* fprintf (stderr, "lit: 1\n"); */
- *out++ = first;
- continue;
- case 8: case 9:
- /* b 100lllll <l+1 bytes> */
- {
- unsigned int l = first & 31;
- /* fprintf (stderr, "lit: %d\n", l); */
- do
- *out++ = *in++;
- while (l--);
- continue;
- }
- case 10: case 11:
- /* c 101oolll <8o> */
- {
- o = first & (3 << 3);
- o = (o << 5) | *in++;
- first = (first & 7) + 2;
- break;
- }
- case 12: case 13:
- /* d 110lllll <8o> */
- {
- o = *in++;
- first = (first & 31) + 10;
- break;
- }
- case 14:
- /* e 1110llll <8o> <8o> */
- {
- o = in[0] | (in[1] << 8);
- in += 2;
- first = first & 31;
- first += 3;
- break;
- }
- case 15:
- /* f1 1111llll <8o> <8o> <8l> */
- /* f2 11110lll <8o> <8o> <8l> */
- /* g 11111lll <8o> <8o> <8o> <8l> */
- {
- first = first & 15;
- if (first >= 8)
- {
- first = (((first - 8) << 8) | in[0]) + 5;
- o = in[1] | (in[2] << 8) | (in[3] << 16);
- in += 4;
- }
- else
- {
- first = ((first << 8) | in[0]) + 19;
- o = in[1] | (in[2] << 8);
- in += 3;
- }
- break;
- }
- }
- /* fprintf(stderr, "ref: %d @ %d\n", first, o); */
- o++;
- o = -o;
-#if 0
- /* We know that first will not be zero, and this loop structure is
- better optimizable. */
- do
- {
- *out = *(out - o);
- out++;
- }
- while (--first);
-#else
- switch (first)
- {
- case 18: *out = *(out + o); out++;
- case 17: *out = *(out + o); out++;
- case 16: *out = *(out + o); out++;
- case 15: *out = *(out + o); out++;
- case 14: *out = *(out + o); out++;
- case 13: *out = *(out + o); out++;
- case 12: *out = *(out + o); out++;
- case 11: *out = *(out + o); out++;
- case 10: *out = *(out + o); out++;
- case 9: *out = *(out + o); out++;
- case 8: *out = *(out + o); out++;
- case 7: *out = *(out + o); out++;
- case 6: *out = *(out + o); out++;
- case 5: *out = *(out + o); out++;
- case 4: *out = *(out + o); out++;
- case 3: *out = *(out + o); out++;
- case 2: *out = *(out + o); out++;
- case 1: *out = *(out + o); out++;
- case 0: break;
- default:
- /* Duff duff :-) */
- switch (first & 15)
- {
- do
- {
- case 0: *out = *(out + o); out++;
- case 15: *out = *(out + o); out++;
- case 14: *out = *(out + o); out++;
- case 13: *out = *(out + o); out++;
- case 12: *out = *(out + o); out++;
- case 11: *out = *(out + o); out++;
- case 10: *out = *(out + o); out++;
- case 9: *out = *(out + o); out++;
- case 8: *out = *(out + o); out++;
- case 7: *out = *(out + o); out++;
- case 6: *out = *(out + o); out++;
- case 5: *out = *(out + o); out++;
- case 4: *out = *(out + o); out++;
- case 3: *out = *(out + o); out++;
- case 2: *out = *(out + o); out++;
- case 1: *out = *(out + o); out++;
- }
- while ((int)(first -= 16) > 0);
- }
- break;
- }
-#endif
- }
- return out - orig_out;
-}
-
-/**********************************************************************/
-
-void repopagestore_init(Repopagestore *store)
-{
- memset(store, 0, sizeof(*store));
- store->pagefd = -1;
-}
-
-void repopagestore_free(Repopagestore *store)
-{
- store->blob_store = solv_free(store->blob_store);
- store->file_pages = solv_free(store->file_pages);
- store->mapped_at = solv_free(store->mapped_at);
- store->mapped = solv_free(store->mapped);
- if (store->pagefd != -1)
- close(store->pagefd);
- store->pagefd = -1;
-}
-
-
-/**********************************************************************/
-
-unsigned char *
-repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigned int pend)
-{
-/* Make sure all pages from PSTART to PEND (inclusive) are loaded,
- and are consecutive. Return a pointer to the mapping of PSTART. */
- unsigned char buf[REPOPAGE_BLOBSIZE];
- unsigned int i, best, pnum;
-
- if (pstart == pend)
- {
- /* Quick check in case the requested page is already mapped */
- if (store->mapped_at[pstart] != -1)
- return store->blob_store + store->mapped_at[pstart];
- }
- else
- {
- /* Quick check in case all pages are already mapped and consecutive. */
- for (pnum = pstart; pnum <= pend; pnum++)
- if (store->mapped_at[pnum] == -1
- || (pnum > pstart
- && store->mapped_at[pnum]
- != store->mapped_at[pnum-1] + REPOPAGE_BLOBSIZE))
- break;
- if (pnum > pend)
- return store->blob_store + store->mapped_at[pstart];
- }
-
- if (store->pagefd == -1 || !store->file_pages)
- return 0; /* no backing file */
-
-#ifdef DEBUG_PAGING
- fprintf(stderr, "PAGE: want %d pages starting at %d\n", pend - pstart + 1, pstart);
-#endif
-
- /* Ensure that we can map the numbers of pages we need at all. */
- if (pend - pstart + 1 > store->nmapped)
- {
- unsigned int oldcan = store->nmapped;
- store->nmapped = pend - pstart + 1;
- if (store->nmapped < 4)
- store->nmapped = 4;
- store->mapped = solv_realloc2(store->mapped, store->nmapped, sizeof(store->mapped[0]));
- for (i = oldcan; i < store->nmapped; i++)
- store->mapped[i] = -1;
- store->blob_store = solv_realloc2(store->blob_store, store->nmapped, REPOPAGE_BLOBSIZE);
-#ifdef DEBUG_PAGING
- fprintf(stderr, "PAGE: can map %d pages\n", store->nmapped);
-#endif
- }
-
- if (store->mapped_at[pstart] != -1)
- {
- /* assume forward search */
- best = store->mapped_at[pstart] / REPOPAGE_BLOBSIZE;
- if (best + (pend - pstart) >= store->nmapped)
- best = 0;
- }
- else if (store->mapped_at[pend] != -1)
- {
- /* assume backward search */
- best = store->mapped_at[pend] / REPOPAGE_BLOBSIZE;
- if (best < pend - pstart)
- best = store->nmapped - 1;
- best -= pend - pstart;
- }
- else
- {
- /* choose some "random" location to avoid thrashing */
- best = (pstart + store->rr_counter++) % (store->nmapped - pend + pstart);
- }
-
- /* So we want to map our pages from [best] to [best+pend-pstart].
- Use a very simple strategy, which doesn't make the best use of
- our resources, but works. Throw away all pages in that range
- (even ours) then copy around ours or read them in. */
- for (i = best, pnum = pstart; pnum <= pend; i++, pnum++)
- {
- unsigned int pnum_mapped_at;
- unsigned int oldpnum = store->mapped[i];
- if (oldpnum != -1)
- {
- if (oldpnum == pnum)
- continue; /* already have the correct page */
- /* Evict this page. */
-#ifdef DEBUG_PAGING
- fprintf(stderr, "PAGE: evict page %d from %d\n", oldpnum, i);
-#endif
- store->mapped[i] = -1;
- store->mapped_at[oldpnum] = -1;
- }
- /* check if we can copy the correct content (before it gets evicted) */
- pnum_mapped_at = store->mapped_at[pnum];
- if (pnum_mapped_at != -1 && pnum_mapped_at != i * REPOPAGE_BLOBSIZE)
- {
- void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
-#ifdef DEBUG_PAGING
- fprintf(stderr, "PAGECOPY: %d from %d to %d\n", pnum, pnum_mapped_at / REPOPAGE_BLOBSIZE, i);
-#endif
- memcpy(dest, store->blob_store + pnum_mapped_at, REPOPAGE_BLOBSIZE);
- store->mapped[pnum_mapped_at / REPOPAGE_BLOBSIZE] = -1;
- store->mapped[i] = pnum;
- store->mapped_at[pnum] = i * REPOPAGE_BLOBSIZE;
- }
- }
-
- /* Everything is free now. Read in or copy the pages we want. */
- for (i = best, pnum = pstart; pnum <= pend; i++, pnum++)
- {
- void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
- if (store->mapped_at[pnum] != -1)
- {
- unsigned int pnum_mapped_at = store->mapped_at[pnum];
- if (pnum_mapped_at != i * REPOPAGE_BLOBSIZE)
- {
-#ifdef DEBUG_PAGING
- fprintf(stderr, "PAGECOPY: %d from %d to %d\n", pnum, pnum_mapped_at / REPOPAGE_BLOBSIZE, i);
-#endif
- /* Still mapped somewhere else, so just copy it from there. */
- memcpy(dest, store->blob_store + pnum_mapped_at, REPOPAGE_BLOBSIZE);
- store->mapped[pnum_mapped_at / REPOPAGE_BLOBSIZE] = -1;
- }
- }
- else
- {
- Attrblobpage *p = store->file_pages + pnum;
- unsigned int in_len = p->page_size;
- unsigned int compressed = in_len & 1;
- in_len >>= 1;
-#ifdef DEBUG_PAGING
- fprintf(stderr, "PAGEIN: %d to %d", pnum, i);
-#endif
- if (pread(store->pagefd, compressed ? buf : dest, in_len, store->file_offset + p->page_offset) != in_len)
- {
- perror("mapping pread");
- return 0;
- }
- if (compressed)
- {
- unsigned int out_len;
- out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE);
- if (out_len != REPOPAGE_BLOBSIZE && pnum < store->num_pages - 1)
- {
-#ifdef DEBUG_PAGING
- fprintf(stderr, "can't decompress\n");
-#endif
- return 0;
- }
-#ifdef DEBUG_PAGING
- fprintf(stderr, " (expand %d to %d)", in_len, out_len);
-#endif
- }
-#ifdef DEBUG_PAGING
- fprintf(stderr, "\n");
-#endif
- }
- store->mapped_at[pnum] = i * REPOPAGE_BLOBSIZE;
- store->mapped[i] = pnum;
- }
- return store->blob_store + best * REPOPAGE_BLOBSIZE;
-}
-
-unsigned int
-repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
-{
- return compress_buf(page, len, cpage, max);
-}
-
-#define SOLV_ERROR_EOF 3
-#define SOLV_ERROR_CORRUPT 6
-
-static inline 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)
- return 0;
- x = (x << 8) | c;
- }
- return x;
-}
-
-/* Try to either setup on-demand paging (using FP as backing
- file), or in case that doesn't work (FP not seekable) slurps in
- all pages and deactivates paging. */
-int
-repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int pagesz, unsigned int blobsz)
-{
- unsigned int npages;
- unsigned int i;
- unsigned int can_seek;
- unsigned int cur_page_ofs;
- unsigned char buf[REPOPAGE_BLOBSIZE];
-
- if (pagesz != REPOPAGE_BLOBSIZE)
- {
- /* We could handle this by slurping in everything. */
- return SOLV_ERROR_CORRUPT;
- }
- can_seek = 1;
- if ((store->file_offset = ftell(fp)) < 0)
- can_seek = 0;
- clearerr(fp);
- if (can_seek)
- store->pagefd = dup(fileno(fp));
- if (store->pagefd == -1)
- can_seek = 0;
- else
- fcntl(store->pagefd, F_SETFD, FD_CLOEXEC);
-
-#ifdef DEBUG_PAGING
- fprintf(stderr, "can %sseek\n", can_seek ? "" : "NOT ");
-#endif
- npages = (blobsz + REPOPAGE_BLOBSIZE - 1) / REPOPAGE_BLOBSIZE;
-
- store->num_pages = npages;
- store->mapped_at = solv_malloc2(npages, sizeof(*store->mapped_at));
-
- /* If we can't seek on our input we have to slurp in everything.
- * Otherwise set up file_pages containing offest/length of the
- * pages */
- if (can_seek)
- store->file_pages = solv_malloc2(npages, sizeof(*store->file_pages));
- else
- store->blob_store = solv_malloc2(npages, REPOPAGE_BLOBSIZE);
- cur_page_ofs = 0;
- for (i = 0; i < npages; i++)
- {
- unsigned int in_len = read_u32(fp);
- unsigned int compressed = in_len & 1;
- in_len >>= 1;
-#ifdef DEBUG_PAGING
- fprintf(stderr, "page %d: len %d (%scompressed)\n",
- i, in_len, compressed ? "" : "not ");
-#endif
- if (can_seek)
- {
- Attrblobpage *p = store->file_pages + i;
- cur_page_ofs += 4;
- store->mapped_at[i] = -1; /* not mapped yet */
- p->page_offset = cur_page_ofs;
- p->page_size = in_len * 2 + compressed;
- if (fseek(fp, in_len, SEEK_CUR) < 0)
- {
- /* We can't fall back to non-seeking behaviour as we already
- read over some data pages without storing them away. */
- close(store->pagefd);
- store->pagefd = -1;
- return SOLV_ERROR_EOF;
- }
- cur_page_ofs += in_len;
- }
- else
- {
- unsigned int out_len;
- void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
- store->mapped_at[i] = i * REPOPAGE_BLOBSIZE;
- /* We can't seek, so suck everything in. */
- if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
- {
- perror("fread");
- return SOLV_ERROR_EOF;
- }
- if (compressed)
- {
- out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE);
- if (out_len != REPOPAGE_BLOBSIZE && i < npages - 1)
- {
- return SOLV_ERROR_CORRUPT;
- }
- }
- }
- }
- return 0;
-}
-
-void
-repopagestore_disable_paging(Repopagestore *store)
-{
- if (store->num_pages)
- repopagestore_load_page_range(store, 0, store->num_pages - 1);
-}
-
-#ifdef STANDALONE
-
-static void
-transfer_file(FILE * from, FILE * to, int compress)
-{
- unsigned char inb[BLOCK_SIZE];
- unsigned char outb[BLOCK_SIZE];
- while (!feof (from) && !ferror (from))
- {
- unsigned int in_len, out_len;
- if (compress)
- {
- in_len = fread(inb, 1, BLOCK_SIZE, from);
- if (in_len)
- {
- unsigned char *b = outb;
- out_len = compress_buf(inb, in_len, outb, sizeof (outb));
- if (!out_len)
- b = inb, out_len = in_len;
- if (fwrite(&out_len, sizeof (out_len), 1, to) != 1)
- {
- perror("write size");
- exit (1);
- }
- if (fwrite(b, out_len, 1, to) != 1)
- {
- perror("write data");
- exit (1);
- }
- }
- }
- else
- {
- if (fread(&in_len, sizeof(in_len), 1, from) != 1)
- {
- if (feof(from))
- return;
- perror("can't read size");
- exit(1);
- }
- if (fread(inb, in_len, 1, from) != 1)
- {
- perror("can't read data");
- exit(1);
- }
- out_len =
- unchecked_decompress_buf(inb, in_len, outb, sizeof(outb));
- if (fwrite(outb, out_len, 1, to) != 1)
- {
- perror("can't write output");
- exit(1);
- }
- }
- }
-}
-
-/* Just for benchmarking purposes. */
-static void
-dumb_memcpy(void *dest, const void *src, unsigned int len)
-{
- char *d = dest;
- const char *s = src;
- while (len--)
- *d++ = *s++;
-}
-
-static void
-benchmark(FILE * from)
-{
- unsigned char inb[BLOCK_SIZE];
- unsigned char outb[BLOCK_SIZE];
- unsigned int in_len = fread(inb, 1, BLOCK_SIZE, from);
- unsigned int out_len;
- if (!in_len)
- {
- perror("can't read from input");
- exit(1);
- }
-
- unsigned int calib_loop;
- unsigned int per_loop;
- unsigned int i, j;
- clock_t start, end;
- float seconds;
-
-#if 0
- calib_loop = 1;
- per_loop = 0;
- start = clock();
- while ((clock() - start) < CLOCKS_PER_SEC / 4)
- {
- calib_loop *= 2;
- for (i = 0; i < calib_loop; i++)
- dumb_memcpy(outb, inb, in_len);
- per_loop += calib_loop;
- }
-
- fprintf(stderr, "memcpy:\nCalibrated to %d iterations per loop\n",
- per_loop);
-
- start = clock();
- for (i = 0; i < 10; i++)
- for (j = 0; j < per_loop; j++)
- dumb_memcpy(outb, inb, in_len);
- end = clock();
- seconds = (end - start) / (float) CLOCKS_PER_SEC;
- fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
- ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
-#endif
-
- calib_loop = 1;
- per_loop = 0;
- start = clock();
- while ((clock() - start) < CLOCKS_PER_SEC / 4)
- {
- calib_loop *= 2;
- for (i = 0; i < calib_loop; i++)
- compress_buf(inb, in_len, outb, sizeof(outb));
- per_loop += calib_loop;
- }
-
- fprintf(stderr, "compression:\nCalibrated to %d iterations per loop\n",
- per_loop);
-
- start = clock();
- for (i = 0; i < 10; i++)
- for (j = 0; j < per_loop; j++)
- compress_buf(inb, in_len, outb, sizeof(outb));
- end = clock();
- seconds = (end - start) / (float) CLOCKS_PER_SEC;
- fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
- ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
-
- out_len = compress_buf(inb, in_len, outb, sizeof(outb));
-
- calib_loop = 1;
- per_loop = 0;
- start = clock();
- while ((clock() - start) < CLOCKS_PER_SEC / 4)
- {
- calib_loop *= 2;
- for (i = 0; i < calib_loop; i++)
- unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
- per_loop += calib_loop;
- }
-
- fprintf(stderr, "decompression:\nCalibrated to %d iterations per loop\n",
- per_loop);
-
- start = clock();
- for (i = 0; i < 10; i++)
- for (j = 0; j < per_loop; j++)
- unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
- end = clock();
- seconds = (end - start) / (float) CLOCKS_PER_SEC;
- fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
- ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
-}
-
-int
-main(int argc, char *argv[])
-{
- int compress = 1;
- if (argc > 1 && !strcmp(argv[1], "-d"))
- compress = 0;
- if (argc > 1 && !strcmp(argv[1], "-b"))
- benchmark(stdin);
- else
- transfer_file(stdin, stdout, compress);
- return 0;
-}
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (c) 2007-2011, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef LIBSOLV_REPOPAGE_H
-#define LIBSOLV_REPOPAGE_H
-
-#define REPOPAGE_BLOBBITS 15
-#define REPOPAGE_BLOBSIZE (1 << REPOPAGE_BLOBBITS)
-
-typedef struct _Attrblobpage
-{
- /* page_size == 0 means the page is not backed by some file storage.
- Otherwise it is L*2+(compressed ? 1 : 0), with L being the data
- length. */
- unsigned int page_offset;
- unsigned int page_size;
-} Attrblobpage;
-
-typedef struct _Repopagestore {
- int pagefd; /* file descriptor we're paging from */
- long file_offset; /* pages in file start here */
-
- unsigned char *blob_store;
- unsigned int num_pages;
-
- /* mapped_at[page] == -1 --> not loaded, otherwise offset into
- store->blob_store. The size of the mapping is REPOPAGE_BLOBSIZE
- except for the last page. */
- unsigned int *mapped_at;
-
- Attrblobpage *file_pages;
-
- /* mapped[i] is -1 if nothing is mapped at logical page I,
- otherwise it contains the page number (of the mapped page). */
- unsigned int *mapped;
- unsigned int nmapped;
- unsigned int rr_counter;
-} Repopagestore;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void repopagestore_init(Repopagestore *store);
-void repopagestore_free(Repopagestore *store);
-
-/* load pages pstart..pend into consecutive memory, return address */
-unsigned char *repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigned int pend);
-
-/* compress a page, return compressed len */
-unsigned int repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max);
-
-/* setup page data for repodata_load_page_range */
-int repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int pagesz, unsigned int blobsz);
-
-void repopagestore_disable_paging(Repopagestore *store);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_REPOPAGE_H */
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * rules.c
- *
- * SAT based dependency solver
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-
-#include "solver.h"
-#include "solver_private.h"
-#include "bitmap.h"
-#include "pool.h"
-#include "poolarch.h"
-#include "util.h"
-#include "evr.h"
-#include "policy.h"
-#include "solverdebug.h"
-#include "linkedpkg.h"
-#include "cplxdeps.h"
-
-#define RULES_BLOCK 63
-
-static void addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep);
-static void solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded);
-
-/*-------------------------------------------------------------------
- * Check if dependency is possible
- *
- * mirrors solver_dep_fulfilled but uses map m instead of the decisionmap.
- * used in solver_addpkgrulesforweak and solver_createcleandepsmap.
- */
-
-static inline int
-dep_possible(Solver *solv, Id dep, Map *m)
-{
- Pool *pool = solv->pool;
- Id p, pp;
-
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags >= 8)
- {
- if (rd->flags == REL_COND)
- return 1;
- if (rd->flags == REL_AND)
- {
- if (!dep_possible(solv, rd->name, m))
- return 0;
- return dep_possible(solv, rd->evr, m);
- }
- if (rd->flags == REL_OR)
- {
- if (dep_possible(solv, rd->name, m))
- return 1;
- return dep_possible(solv, rd->evr, m);
- }
- if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
- return solver_splitprovides(solv, rd->evr, m);
- }
- }
- FOR_PROVIDES(p, pp, dep)
- {
- if (MAPTST(m, p))
- return 1;
- }
- return 0;
-}
-
-static inline int
-is_otherproviders_dep(Pool *pool, Id dep)
-{
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
- return 1;
- }
- return 0;
-}
-
-/********************************************************************
- *
- * Rule handling
- *
- * - unify rules, remove duplicates
- */
-
-/*-------------------------------------------------------------------
- *
- * compare rules for unification sort
- *
- */
-
-static int
-unifyrules_sortcmp(const void *ap, const void *bp, void *dp)
-{
- Pool *pool = dp;
- 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;
- while (*bd)
- if ((x = *ad++ - *bd++) != 0)
- return x;
- return *ad;
-}
-
-int
-solver_rulecmp(Solver *solv, Rule *r1, Rule *r2)
-{
- return unifyrules_sortcmp(r1, r2, solv->pool);
-}
-
-
-/*-------------------------------------------------------------------
- *
- * unify rules
- * go over all rules and remove duplicates
- */
-
-void
-solver_unifyrules(Solver *solv)
-{
- Pool *pool = solv->pool;
- int i, j;
- Rule *ir, *jr;
-
- if (solv->nrules <= 2) /* nothing to unify */
- return;
-
- /* sort rules first */
- solv_sort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp, solv->pool);
-
- /* prune rules
- * i = unpruned
- * j = pruned
- */
- jr = 0;
- for (i = j = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
- {
- if (jr && !unifyrules_sortcmp(ir, jr, pool))
- continue; /* prune! */
- jr = solv->rules + j++; /* keep! */
- if (ir != jr)
- *jr = *ir;
- }
-
- /* reduced count from nrules to j rules */
- POOL_DEBUG(SOLV_DEBUG_STATS, "pruned rules from %d to %d\n", solv->nrules, j);
-
- /* adapt rule buffer */
- solv->nrules = j;
- solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
-
- /*
- * debug: log rule statistics
- */
- IF_POOLDEBUG (SOLV_DEBUG_STATS)
- {
- int binr = 0;
- int lits = 0;
- Id *dp;
- Rule *r;
-
- for (i = 1; i < solv->nrules; i++)
- {
- r = solv->rules + i;
- if (r->d == 0)
- binr++;
- else
- {
- dp = solv->pool->whatprovidesdata + r->d;
- while (*dp++)
- lits++;
- }
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, " binary: %d\n", binr);
- POOL_DEBUG(SOLV_DEBUG_STATS, " normal: %d, %d literals\n", solv->nrules - 1 - binr, lits);
- }
-}
-
-#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
- *
- * A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3)
- *
- * p < 0 : pkg id of A
- * d > 0 : Offset in whatprovidesdata (list of providers of b)
- *
- * A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3)
- * p < 0 : pkg id of A
- * p2 < 0 : Id of solvable (e.g. B1)
- *
- * d == 0, p2 == 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 (also: uninstallable)
- * Requires: p < 0, d > 0 (-A|B1|B2|...) d: <list of providers for requirement of p>
- * Updates: p > 0, d > 0 (A|B1|B2|...) d: <list of updates for solvable p>
- * Conflicts: p < 0, p2 < 0 (-A|-B) either p (conflict issuer) or d (conflict provider) (binary rule)
- * also used for obsoletes
- * No-op ?: p = 0, d = 0 (null) (used as placeholder in update/feature rules)
- *
- * resulting watches:
- * ------------------
- * Direct assertion (no watch needed) --> d = 0, w1 = p, w2 = 0
- * Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p
- * every other : w1 = p, w2 = whatprovidesdata[d];
- *
- * always returns a rule for non-pkg rules
- */
-
-Rule *
-solver_addrule(Solver *solv, Id p, Id p2, Id d)
-{
- Pool *pool = solv->pool;
- Rule *r;
-
- if (d)
- {
- assert(!p2 && d > 0);
- if (!pool->whatprovidesdata[d])
- d = 0;
- else if (!pool->whatprovidesdata[d + 1])
- {
- p2 = pool->whatprovidesdata[d];
- d = 0;
- }
- }
-
- /* now we have two cases:
- * 1 or 2 literals: d = 0, p, p2 contain the literals
- * 3 or more literals: d > 0, p2 == 0, d is offset into whatprovidesdata
- */
-
- /* it often happenes that requires lead to adding the same pkg rule
- * multiple times, so we prune those duplicates right away to make
- * the work for unifyrules a bit easier */
- if (!solv->pkgrules_end) /* we add pkg rules */
- {
- r = solv->rules + solv->nrules - 1;
- if (d)
- {
- Id *dp;
- /* check if rule is identical */
- if (r->p == p)
- {
- Id *dp2;
- if (r->d == d)
- return r;
- dp2 = pool->whatprovidesdata + r->d;
- for (dp = pool->whatprovidesdata + d; *dp; dp++, dp2++)
- if (*dp != *dp2)
- break;
- if (*dp == *dp2)
- return r;
- }
- /* check if rule is self-fulfilling */
- for (dp = pool->whatprovidesdata + d; *dp; dp++)
- if (*dp == -p)
- return 0; /* rule is self-fulfilling */
- }
- else
- {
- if (p2 && p > p2)
- {
- Id o = p; /* switch p1 and p2 */
- p = p2;
- p2 = o;
- }
- if (r->p == p && !r->d && r->w2 == p2)
- return r;
- if (p == -p2)
- return 0; /* rule is self-fulfilling */
- }
- }
-
- solv->rules = solv_extend(solv->rules, solv->nrules, 1, sizeof(Rule), RULES_BLOCK);
- r = solv->rules + solv->nrules++; /* point to rule space */
- r->p = p;
- r->d = d;
- r->w1 = p;
- r->w2 = d ? pool->whatprovidesdata[d] : p2;
- r->n1 = 0;
- r->n2 = 0;
- IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
- {
- POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, " Add rule: ");
- solver_printrule(solv, SOLV_DEBUG_RULE_CREATION, r);
- }
- return r;
-}
-
-
-void
-solver_shrinkrules(Solver *solv, int nrules)
-{
- solv->nrules = nrules;
- solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
-}
-
-/******************************************************************************
- ***
- *** pkg rule part: create rules representing the package dependencies
- ***
- ***/
-
-/*
- * special multiversion patch conflict handling:
- * a patch conflict is also satisfied if some other
- * version with the same name/arch that doesn't conflict
- * gets installed. The generated rule is thus:
- * -patch|-cpack|opack1|opack2|...
- */
-static Id
-makemultiversionconflict(Solver *solv, Id n, Id con)
-{
- Pool *pool = solv->pool;
- Solvable *s, *sn;
- Queue q;
- Id p, pp, qbuf[64];
-
- sn = pool->solvables + n;
- queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
- queue_push(&q, -n);
- FOR_PROVIDES(p, pp, sn->name)
- {
- s = pool->solvables + p;
- if (s->name != sn->name || s->arch != sn->arch)
- continue;
- if (!MAPTST(&solv->multiversion, p))
- continue;
- if (pool_match_nevr(pool, pool->solvables + p, con))
- continue;
- /* here we have a multiversion solvable that doesn't conflict */
- /* thus we're not in conflict if it is installed */
- queue_push(&q, p);
- }
- if (q.count == 1)
- n = 0; /* no other package found, normal conflict handling */
- else
- n = pool_queuetowhatprovides(pool, &q);
- queue_free(&q);
- return n;
-}
-
-static inline void
-addpkgrule(Solver *solv, Id p, Id p2, Id d, int type, Id dep)
-{
- if (!solv->ruleinfoq)
- solver_addrule(solv, p, p2, d);
- else
- addpkgruleinfo(solv, p, p2, d, type, dep);
-}
-
-#ifdef ENABLE_LINKED_PKGS
-
-static void
-addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m, Queue *workq)
-{
- Pool *pool = solv->pool;
- int i;
- if (!qr->count)
- return;
-#if 0
- printf("ADDLINKS %s\n -> %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
- for (i = 0; i < qr->count; i++)
- printf(" - %s\n", pool_solvid2str(pool, qr->elements[i]));
- printf(" <- %s\n", pool_dep2str(pool, prv));
- for (i = 0; i < qp->count; i++)
- printf(" - %s\n", pool_solvid2str(pool, qp->elements[i]));
-#endif
-
- if (qr->count == 1)
- addpkgrule(solv, -(s - pool->solvables), qr->elements[0], 0, SOLVER_RULE_PKG_REQUIRES, req);
- else
- addpkgrule(solv, -(s - pool->solvables), 0, pool_queuetowhatprovides(pool, qr), SOLVER_RULE_PKG_REQUIRES, req);
- if (qp->count > 1)
- {
- Id d = pool_queuetowhatprovides(pool, qp);
- for (i = 0; i < qr->count; i++)
- addpkgrule(solv, -qr->elements[i], 0, d, SOLVER_RULE_PKG_REQUIRES, prv);
- }
- else if (qp->count)
- {
- for (i = 0; i < qr->count; i++)
- addpkgrule(solv, -qr->elements[i], qp->elements[0], 0, SOLVER_RULE_PKG_REQUIRES, prv);
- }
- if (!m)
- return; /* nothing more to do if called from getpkgruleinfos() */
- for (i = 0; i < qr->count; i++)
- if (!MAPTST(m, qr->elements[i]))
- queue_push(workq, qr->elements[i]);
- for (i = 0; i < qp->count; i++)
- if (!MAPTST(m, qp->elements[i]))
- queue_push(workq, qp->elements[i]);
- if (solv->installed && s->repo == solv->installed)
- {
- Repo *installed = solv->installed;
- /* record installed buddies */
- if (!solv->instbuddy)
- solv->instbuddy = solv_calloc(installed->end - installed->start, sizeof(Id));
- if (qr->count == 1)
- solv->instbuddy[s - pool->solvables - installed->start] = qr->elements[0];
- for (i = 0; i < qr->count; i++)
- {
- Id p = qr->elements[i];
- if (pool->solvables[p].repo != installed)
- continue; /* huh? */
- if (qp->count > 1 || (solv->instbuddy[p - installed->start] != 0 && solv->instbuddy[p - installed->start] != s - pool->solvables))
- solv->instbuddy[p - installed->start] = 1; /* 1: ambiguous buddy */
- else
- solv->instbuddy[p - installed->start] = s - pool->solvables;
- }
- }
-}
-
-static void
-add_package_link(Solver *solv, Solvable *s, Map *m, Queue *workq)
-{
- Queue qr, qp;
- Id req = 0, prv = 0;
- queue_init(&qr);
- queue_init(&qp);
- find_package_link(solv->pool, s, &req, &qr, &prv, &qp);
- if (qr.count)
- addlinks(solv, s, req, &qr, prv, &qp, m, workq);
- queue_free(&qr);
- queue_free(&qp);
-}
-
-#endif
-
-#ifdef ENABLE_COMPLEX_DEPS
-
-static void
-add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *workq, Map *m)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- int i, j, flags;
- Queue bq;
-
- queue_init(&bq);
- flags = dontfix ? CPLXDEPS_DONTFIX : 0;
- /* CNF expansion for requires, DNF + INVERT expansion for conflicts */
- if (type == SOLVER_RULE_PKG_CONFLICTS)
- flags |= CPLXDEPS_TODNF | CPLXDEPS_EXPAND | CPLXDEPS_INVERT;
-
- i = pool_normalize_complex_dep(pool, dep, &bq, flags);
- /* handle special cases */
- if (i == 0)
- {
- if (dontfix)
- {
- POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken dependency %s of installed package %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, p));
- }
- else
- {
- POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, p), p, pool_dep2str(pool, dep));
- addpkgrule(solv, -p, 0, 0, type == SOLVER_RULE_PKG_REQUIRES ? SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP : type, dep);
- }
- queue_free(&bq);
- return;
- }
- if (i == 1)
- {
- queue_free(&bq);
- return;
- }
-
- /* go through all blocks and add a rule for each block */
- for (i = 0; i < bq.count; i++)
- {
- if (!bq.elements[i])
- continue; /* huh? */
- if (bq.elements[i] == pool->nsolvables)
- {
- /* conventional requires (cannot be a conflicts as they have been expanded) */
- Id *dp = pool->whatprovidesdata + bq.elements[i + 1];
- i += 2;
- if (dontfix)
- {
- for (j = 0; dp[j] != 0; j++)
- if (pool->solvables[dp[j]].repo == installed)
- break; /* provider was installed */
- if (!dp[j])
- continue;
- }
- /* check if the rule contains both p and -p */
- for (j = 0; dp[j] != 0; j++)
- if (dp[j] == p)
- break;
- if (dp[j])
- continue;
- addpkgrule(solv, -p, 0, dp - pool->whatprovidesdata, SOLVER_RULE_PKG_REQUIRES, dep);
- /* push all non-visited providers on the work queue */
- if (m)
- for (; *dp; dp++)
- if (!MAPTST(m, *dp))
- queue_push(workq, *dp);
- continue;
- }
- if (!bq.elements[i + 1])
- {
- Id p2 = bq.elements[i++];
- /* simple rule with just two literals, we'll add a (-p, p2) rule */
- if (dontfix)
- {
- if (p2 < 0 && pool->solvables[-p2].repo == installed)
- continue;
- if (p2 > 0 && pool->solvables[p2].repo != installed)
- continue;
- }
- if (-p == p2)
- {
- if (type == SOLVER_RULE_PKG_CONFLICTS)
- {
- if (pool->forbidselfconflicts && !is_otherproviders_dep(pool, dep))
- addpkgrule(solv, -p, 0, 0, SOLVER_RULE_PKG_SELF_CONFLICT, dep);
- continue;
- }
- addpkgrule(solv, -p, 0, 0, type, dep);
- continue;
- }
- /* check if the rule contains both p and -p */
- if (p == p2)
- continue;
- addpkgrule(solv, -p, p2, 0, type, dep);
- if (m && p2 > 0 && !MAPTST(m, p2))
- queue_push(workq, p2);
- }
- else
- {
- Id *qele;
- int qcnt;
-
- qele = bq.elements + i;
- qcnt = i;
- while (bq.elements[i])
- i++;
- qcnt = i - qcnt;
- if (dontfix)
- {
- for (j = 0; j < qcnt; j++)
- {
- if (qele[j] > 0 && pool->solvables[qele[j]].repo == installed)
- break;
- if (qele[j] < 0 && pool->solvables[-qele[j]].repo != installed)
- break;
- }
- if (j == qcnt)
- continue;
- }
- /* add -p to (ordered) rule (overwriting the trailing zero) */
- for (j = 0; ; j++)
- {
- if (j == qcnt || qele[j] > -p)
- {
- if (j < qcnt)
- memmove(qele + j + 1, qele + j, (qcnt - j) * sizeof(Id));
- qele[j] = -p;
- qcnt++;
- break;
- }
- if (qele[j] == -p)
- break;
- }
- /* check if the rule contains both p and -p */
- for (j = 0; j < qcnt; j++)
- if (qele[j] == p)
- break;
- if (j < qcnt)
- continue;
- addpkgrule(solv, qele[0], 0, pool_ids2whatprovides(pool, qele + 1, qcnt - 1), type, dep);
- if (m)
- for (j = 0; j < qcnt; j++)
- if (qele[j] > 0 && !MAPTST(m, qele[j]))
- queue_push(workq, qele[j]);
- }
- }
- queue_free(&bq);
-}
-
-#endif
-
-/*-------------------------------------------------------------------
- *
- * add (install) rules for solvable
- *
- * s: Solvable for which to add rules
- * m: m[s] = 1 for solvables which have rules, prevent rule duplication
- *
- * Algorithm: 'visit all nodes of a graph'. The graph nodes are
- * solvables, the edges their dependencies.
- * Starting from an installed solvable, this will create all rules
- * representing the graph created by the solvables dependencies.
- *
- * for unfulfilled requirements, conflicts, obsoletes,....
- * add a negative assertion for solvables that are not installable
- *
- * It will also create rules for all solvables referenced by 's'
- * i.e. descend to all providers of requirements of 's'
- *
- */
-
-void
-solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
-
- Queue workq; /* list of solvables we still have to work on */
- Id workqbuf[64];
-
- int i;
- int dontfix; /* ignore dependency errors for installed solvables */
- Id req, *reqp;
- Id con, *conp;
- Id obs, *obsp;
- Id rec, *recp;
- Id sug, *sugp;
- Id p, pp; /* whatprovides loops */
- Id *dp; /* ptr to 'whatprovides' */
- Id n; /* Id for current solvable 's' */
-
- queue_init_buffer(&workq, workqbuf, sizeof(workqbuf)/sizeof(*workqbuf));
- queue_push(&workq, s - pool->solvables); /* push solvable Id to work queue */
-
- /* loop until there's no more work left */
- while (workq.count)
- {
- /*
- * n: Id of solvable
- * s: Pointer to solvable
- */
-
- n = queue_shift(&workq); /* 'pop' next solvable to work on from queue */
- if (m)
- {
- if (MAPTST(m, n)) /* continue if already visited */
- continue;
- MAPSET(m, n); /* mark as visited */
- }
-
- s = pool->solvables + n;
-
- dontfix = 0;
- if (installed /* Installed system available */
- && s->repo == installed /* solvable is installed */
- && !solv->fixmap_all /* NOT repair errors in dependency graph */
- && !(solv->fixmap.size && MAPTST(&solv->fixmap, n - installed->start)))
- {
- dontfix = 1; /* dont care about broken deps */
- }
-
- if (!dontfix)
- {
- if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC
- ? pool_disabled_solvable(pool, s)
- : !pool_installable(pool, s))
- {
- POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable\n", pool_solvid2str(pool, n), n);
- addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_NOT_INSTALLABLE, 0);
- }
- }
-
-#ifdef ENABLE_LINKED_PKGS
- /* add pseudo-package <-> real-package links */
- if (has_package_link(pool, s))
- add_package_link(solv, s, m, &workq);
-#endif
-
- /*-----------------------------------------
- * check requires of s
- */
-
- if (s->requires)
- {
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0) /* go through all requires */
- {
- if (req == SOLVABLE_PREREQMARKER) /* skip the marker */
- continue;
-
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, req))
- {
- /* we have AND/COND deps, normalize */
- add_complex_deprules(solv, n, req, SOLVER_RULE_PKG_REQUIRES, dontfix, &workq, m);
- continue;
- }
-#endif
-
- /* find list of solvables providing 'req' */
- dp = pool_whatprovides_ptr(pool, req);
-
- if (*dp == SYSTEMSOLVABLE) /* always installed */
- continue;
-
- if (dontfix)
- {
- /* the strategy here is to not insist on dependencies
- * that are already broken. so if we find one provider
- * that was already installed, we know that the
- * dependency was not broken before so we enforce it */
- for (i = 0; (p = dp[i]) != 0; i++)
- if (pool->solvables[p].repo == installed)
- break; /* found installed provider */
- if (!p)
- {
- /* didn't find an installed provider: previously broken dependency */
- POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken requires %s of installed package %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s));
- continue;
- }
- }
-
- if (!*dp)
- {
- POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, n), n, pool_dep2str(pool, req));
- addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP, req);
- continue;
- }
-
- for (i = 0; dp[i] != 0; i++)
- if (n == dp[i])
- break;
- if (dp[i])
- continue; /* provided by itself, no need to add rule */
-
- IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
- {
- POOL_DEBUG(SOLV_DEBUG_RULE_CREATION," %s requires %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
- for (i = 0; dp[i]; i++)
- POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, " provided by %s\n", pool_solvid2str(pool, dp[i]));
- }
-
- /* add 'requires' dependency */
- /* rule: (-requestor|provider1|provider2|...|providerN) */
- addpkgrule(solv, -n, 0, dp - pool->whatprovidesdata, SOLVER_RULE_PKG_REQUIRES, req);
-
- /* push all non-visited providers on the work queue */
- if (m)
- for (; *dp; dp++)
- if (!MAPTST(m, *dp))
- queue_push(&workq, *dp);
- }
- }
-
- /* that's all we check for src packages */
- if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
- continue;
-
- /*-----------------------------------------
- * check conflicts of s
- */
-
- if (s->conflicts)
- {
- int ispatch = 0;
-
- /* we treat conflicts in patches a bit differen:
- * - nevr matching
- * - multiversion handling
- * XXX: we should really handle this different, looking
- * at the name is a bad hack
- */
- if (!strncmp("patch:", pool_id2str(pool, s->name), 6))
- ispatch = 1;
- conp = s->repo->idarraydata + s->conflicts;
- /* foreach conflicts of 's' */
- while ((con = *conp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (!ispatch && pool_is_complex_dep(pool, con))
- {
- /* we have AND/COND deps, normalize */
- add_complex_deprules(solv, n, con, SOLVER_RULE_PKG_CONFLICTS, dontfix, &workq, m);
- continue;
- }
-#endif
- /* foreach providers of a conflict of 's' */
- FOR_PROVIDES(p, pp, con)
- {
- if (ispatch && !pool_match_nevr(pool, pool->solvables + p, con))
- continue;
- /* dontfix: dont care about conflicts with already installed packs */
- if (dontfix && pool->solvables[p].repo == installed)
- continue;
- if (p == n) /* p == n: self conflict */
- {
- if (!pool->forbidselfconflicts || is_otherproviders_dep(pool, con))
- continue;
- addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_SELF_CONFLICT, con);
- continue;
- }
- if (ispatch && solv->multiversion.size && MAPTST(&solv->multiversion, p) && ISRELDEP(con))
- {
- /* our patch conflicts with a multiversion package */
- Id d = makemultiversionconflict(solv, p, con);
- if (d)
- {
- addpkgrule(solv, -n, 0, d, SOLVER_RULE_PKG_CONFLICTS, con);
- continue;
- }
- }
- if (p == SYSTEMSOLVABLE)
- p = 0;
- /* rule: -n|-p: either solvable _or_ provider of conflict */
- addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_CONFLICTS, con);
- }
- }
- }
-
- /*-----------------------------------------
- * check obsoletes and implicit obsoletes of a package
- * if ignoreinstalledsobsoletes is not set, we're also checking
- * obsoletes of installed packages (like newer rpm versions)
- */
- if ((!installed || s->repo != installed) || !pool->noinstalledobsoletes)
- {
- int multi = solv->multiversion.size && MAPTST(&solv->multiversion, n);
- int isinstalled = (installed && s->repo == installed);
- if (s->obsoletes && (!multi || solv->keepexplicitobsoletes))
- {
- obsp = s->repo->idarraydata + s->obsoletes;
- /* foreach obsoletes */
- while ((obs = *obsp++) != 0)
- {
- /* foreach provider of an obsoletes of 's' */
- FOR_PROVIDES(p, pp, obs)
- {
- Solvable *ps = pool->solvables + p;
- if (p == n)
- continue;
- if (isinstalled && dontfix && ps->repo == installed)
- continue; /* don't repair installed/installed problems */
- if (!pool->obsoleteusesprovides /* obsoletes are matched names, not provides */
- && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- if (p == SYSTEMSOLVABLE)
- p = 0;
- if (!isinstalled)
- addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_OBSOLETES, obs);
- else
- addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_INSTALLED_OBSOLETES, obs);
- }
- }
- }
- /* check implicit obsoletes
- * for installed packages we only need to check installed/installed problems (and
- * only when dontfix is not set), as the others are picked up when looking at the
- * uninstalled package.
- */
- if (!isinstalled || !dontfix)
- {
- FOR_PROVIDES(p, pp, s->name)
- {
- Solvable *ps = pool->solvables + p;
- if (p == n)
- continue;
- if (isinstalled && ps->repo != installed)
- continue;
- /* we still obsolete packages with same nevra, like rpm does */
- /* (actually, rpm mixes those packages. yuck...) */
- if (multi && (s->name != ps->name || s->evr != ps->evr || s->arch != ps->arch))
- {
- if (isinstalled || ps->repo != installed)
- continue;
- /* also check the installed package for multi-ness */
- if (MAPTST(&solv->multiversion, p))
- continue;
- }
- if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
- continue;
- if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- if (p == SYSTEMSOLVABLE)
- p = 0;
- if (s->name == ps->name)
- {
- /* optimization: do not add the same-name conflict rule if it was
- * already added when we looked at the other package.
- * (this assumes pool_colormatch is symmetric) */
- if (p && m && ps->repo != installed && MAPTST(m, p) &&
- (ps->arch != ARCH_SRC && ps->arch != ARCH_NOSRC) &&
- !(solv->multiversion.size && MAPTST(&solv->multiversion, p)))
- continue;
- addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_SAME_NAME, 0);
- }
- else
- addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_IMPLICIT_OBSOLETES, s->name);
- }
- }
- }
-
- if (m && pool->implicitobsoleteusescolors && (s->arch > pool->lastarch || pool->id2arch[s->arch] != 1))
- {
- int a = pool->id2arch[s->arch];
- /* check lock-step candidates */
- FOR_PROVIDES(p, pp, s->name)
- {
- Solvable *ps = pool->solvables + p;
- if (s->name != ps->name || s->evr != ps->evr || MAPTST(m, p))
- continue;
- if (ps->arch > pool->lastarch || pool->id2arch[ps->arch] == 1 || pool->id2arch[ps->arch] >= a)
- continue;
- queue_push(&workq, p);
- }
- }
-
- /*-----------------------------------------
- * add recommends/suggests to the work queue
- */
- if (s->recommends && m)
- {
- recp = s->repo->idarraydata + s->recommends;
- while ((rec = *recp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, rec))
- {
- pool_add_pos_literals_complex_dep(pool, rec, &workq, m, 0);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, rec)
- if (!MAPTST(m, p))
- queue_push(&workq, p);
- }
- }
- if (s->suggests && m)
- {
- sugp = s->repo->idarraydata + s->suggests;
- while ((sug = *sugp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, sug))
- {
- pool_add_pos_literals_complex_dep(pool, sug, &workq, m, 0);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, sug)
- if (!MAPTST(m, p))
- queue_push(&workq, p);
- }
- }
- }
- queue_free(&workq);
-}
-
-#ifdef ENABLE_LINKED_PKGS
-void
-solver_addpkgrulesforlinked(Solver *solv, Map *m)
-{
- Pool *pool = solv->pool;
- Solvable *s;
- int i, j;
- Queue qr;
-
- queue_init(&qr);
- for (i = 1; i < pool->nsolvables; i++)
- {
- if (MAPTST(m, i))
- continue;
- s = pool->solvables + i;
- if (!s->repo || s->repo == solv->installed)
- continue;
- if (!strchr(pool_id2str(pool, s->name), ':'))
- continue;
- if (!pool_installable(pool, s))
- continue;
- find_package_link(pool, s, 0, &qr, 0, 0);
- if (qr.count)
- {
- for (j = 0; j < qr.count; j++)
- if (MAPTST(m, qr.elements[j]))
- {
- solver_addpkgrulesforsolvable(solv, s, m);
- break;
- }
- queue_empty(&qr);
- }
- }
- queue_free(&qr);
-}
-#endif
-
-/*-------------------------------------------------------------------
- *
- * Add rules for packages possibly selected in by weak dependencies
- *
- * m: already added solvables
- */
-
-void
-solver_addpkgrulesforweak(Solver *solv, Map *m)
-{
- Pool *pool = solv->pool;
- Solvable *s;
- Id sup, *supp;
- int i, n;
-
- /* foreach solvable in pool */
- for (i = n = 1; n < pool->nsolvables; i++, n++)
- {
- if (i == pool->nsolvables) /* wrap i */
- i = 1;
- if (MAPTST(m, i)) /* already added that one */
- continue;
-
- s = pool->solvables + i;
- if (!s->repo)
- continue;
- if (s->repo != pool->installed && !pool_installable(pool, s))
- continue; /* only look at installable ones */
-
- sup = 0;
- if (s->supplements)
- {
- /* find possible supplements */
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (dep_possible(solv, sup, m))
- break;
- }
-
- /* if nothing found, check for enhances */
- if (!sup && s->enhances)
- {
- supp = s->repo->idarraydata + s->enhances;
- while ((sup = *supp++) != 0)
- if (dep_possible(solv, sup, m))
- break;
- }
- /* if nothing found, goto next solvables */
- if (!sup)
- continue;
- solver_addpkgrulesforsolvable(solv, s, m);
- n = 0; /* check all solvables again because we added solvables to m */
- }
-}
-
-
-/*-------------------------------------------------------------------
- *
- * add package rules for possible updates
- *
- * s: solvable
- * m: map of already visited solvables
- * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
- */
-
-void
-solver_addpkgrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
-{
- Pool *pool = solv->pool;
- int i;
- /* queue and buffer for it */
- Queue qs;
- Id qsbuf[64];
-
- queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
- /* find update candidates for 's' */
- policy_findupdatepackages(solv, s, &qs, allow_all);
- /* add rule for 's' if not already done */
- if (!MAPTST(m, s - pool->solvables))
- solver_addpkgrulesforsolvable(solv, s, m);
- /* foreach update candidate, add rule if not already done */
- for (i = 0; i < qs.count; i++)
- if (!MAPTST(m, qs.elements[i]))
- solver_addpkgrulesforsolvable(solv, pool->solvables + qs.elements[i], m);
- queue_free(&qs);
-}
-
-
-/***********************************************************************
- ***
- *** Update/Feature rule part
- ***
- *** Those rules make sure an installed package isn't silently deleted
- ***
- ***/
-
-static Id
-finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
-{
- Pool *pool = solv->pool;
- int i;
-
- policy_findupdatepackages(solv, s, qs, allow_all ? allow_all : 2);
- if (!qs->count)
- {
- if (allow_all)
- return 0; /* orphaned, don't create feature rule */
- /* check if this is an orphaned package */
- policy_findupdatepackages(solv, s, qs, 1);
- if (!qs->count)
- return 0; /* orphaned, don't create update rule */
- qs->count = 0;
- return -SYSTEMSOLVABLE; /* supported but not installable */
- }
- if (allow_all)
- return s - pool->solvables;
- /* check if it is ok to keep the installed package */
- if (solv->dupmap.size && MAPTST(&solv->dupmap, s - pool->solvables))
- return s - pool->solvables;
- for (i = 0; i < qs->count; i++)
- {
- Solvable *ns = pool->solvables + qs->elements[i];
- if (s->evr == ns->evr && solvable_identical(s, ns))
- return s - pool->solvables;
- }
- /* nope, it must be some other package */
- return -SYSTEMSOLVABLE;
-}
-
-#if 0
-/* add packages from the dup repositories to the update candidates
- * this isn't needed for the global dup mode as all packages are
- * from dup repos in that case */
-static void
-addduppackages(Solver *solv, Solvable *s, Queue *qs)
-{
- Queue dupqs;
- Id p, dupqsbuf[64];
- int i;
- int oldnoupdateprovide = solv->noupdateprovide;
-
- queue_init_buffer(&dupqs, dupqsbuf, sizeof(dupqsbuf)/sizeof(*dupqsbuf));
- solv->noupdateprovide = 1;
- policy_findupdatepackages(solv, s, &dupqs, 2);
- solv->noupdateprovide = oldnoupdateprovide;
- for (i = 0; i < dupqs.count; i++)
- {
- p = dupqs.elements[i];
- if (MAPTST(&solv->dupmap, p))
- queue_pushunique(qs, p);
- }
- queue_free(&dupqs);
-}
-#endif
-
-/*-------------------------------------------------------------------
- *
- * add rule for update
- * (A|A1|A2|A3...) An = update candidates for A
- *
- * s = (installed) solvable
- */
-
-void
-solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
-{
- /* installed packages get a special upgrade allowed rule */
- Pool *pool = solv->pool;
- Id p, d;
- Queue qs;
- Id qsbuf[64];
- int isorphaned = 0;
-
- queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
- p = s - pool->solvables;
- /* find update candidates for 's' */
- if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
- p = finddistupgradepackages(solv, s, &qs, allow_all);
- else
- policy_findupdatepackages(solv, s, &qs, allow_all);
-
-#ifdef ENABLE_LINKED_PKGS
- if (solv->instbuddy && solv->instbuddy[s - pool->solvables - solv->installed->start])
- {
- const char *name = pool_id2str(pool, s->name);
- if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
- {
- /* a linked pseudo package. As it is linked, we do not need an update/feature rule */
- /* nevertheless we set specialupdaters so we can update */
- solver_addrule(solv, 0, 0, 0);
- if (!allow_all && qs.count)
- {
- if (p != -SYSTEMSOLVABLE)
- queue_unshift(&qs, p);
- if (!solv->specialupdaters)
- solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
- solv->specialupdaters[s - pool->solvables - solv->installed->start] = pool_queuetowhatprovides(pool, &qs);
- }
- queue_free(&qs);
- return;
- }
- }
-#endif
-
- if (!allow_all && !p) /* !p implies qs.count == 0 */
- {
- queue_push(&solv->orphaned, s - pool->solvables); /* an orphaned package */
- if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start))))
- p = s - pool->solvables; /* keep this orphaned package installed */
- queue_free(&qs);
- solver_addrule(solv, p, 0, 0);
- return;
- }
-
- if (!allow_all && qs.count && solv->multiversion.size)
- {
- int i, j;
-
- for (i = 0; i < qs.count; i++)
- if (MAPTST(&solv->multiversion, qs.elements[i]))
- break;
- if (i < qs.count)
- {
- /* filter out all multiversion packages as they don't update */
- d = pool_queuetowhatprovides(pool, &qs); /* save qs away */
- for (j = i; i < qs.count; i++)
- {
- if (MAPTST(&solv->multiversion, qs.elements[i]))
- {
- Solvable *ps = pool->solvables + qs.elements[i];
- /* if keepexplicitobsoletes is set and the name is different,
- * we assume that there is an obsoletes. XXX: not 100% correct */
- if (solv->keepexplicitobsoletes && ps->name != s->name)
- {
- qs.elements[j++] = qs.elements[i];
- continue;
- }
- /* it's ok if they have same nevra */
- if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
- continue;
- }
- qs.elements[j++] = qs.elements[i];
- }
- if (j < qs.count) /* filtered at least one package? */
- {
- if (j == 0 && p == -SYSTEMSOLVABLE)
- {
- /* this is a multiversion orphan */
- queue_push(&solv->orphaned, s - pool->solvables);
- if (!solv->specialupdaters)
- solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
- solv->specialupdaters[s - pool->solvables - solv->installed->start] = d;
- if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start))))
- {
- /* we need to keep the orphan */
- queue_free(&qs);
- solver_addrule(solv, s - pool->solvables, 0, 0);
- return;
- }
- /* we can drop it as long as we update */
- isorphaned = 1;
- j = qs.count; /* force the update */
- }
- qs.count = j;
- }
- else if (p != -SYSTEMSOLVABLE)
- {
- /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
- queue_free(&qs);
- solver_addrule(solv, s - pool->solvables, 0, d); /* allow update of s */
- return;
- }
- }
- }
- if (!isorphaned && p == -SYSTEMSOLVABLE && solv->dupmap.size)
- p = s - pool->solvables; /* let the dup rules sort it out */
- if (qs.count && p == -SYSTEMSOLVABLE)
- p = queue_shift(&qs);
- if (qs.count > 1)
- {
- d = pool_queuetowhatprovides(pool, &qs);
- queue_free(&qs);
- solver_addrule(solv, p, 0, d); /* allow update of s */
- }
- else
- {
- d = qs.count ? qs.elements[0] : 0;
- queue_free(&qs);
- solver_addrule(solv, p, d, 0); /* allow update of s */
- }
-}
-
-static inline void
-disableupdaterule(Solver *solv, Id p)
-{
- Rule *r;
-
- MAPSET(&solv->noupdate, p - solv->installed->start);
- r = solv->rules + solv->updaterules + (p - solv->installed->start);
- if (r->p && r->d >= 0)
- solver_disablerule(solv, r);
- r = solv->rules + solv->featurerules + (p - solv->installed->start);
- if (r->p && r->d >= 0)
- solver_disablerule(solv, r);
- if (solv->bestrules_pkg)
- {
- int i, ni;
- ni = solv->bestrules_end - solv->bestrules;
- for (i = 0; i < ni; i++)
- if (solv->bestrules_pkg[i] == p)
- solver_disablerule(solv, solv->rules + solv->bestrules + i);
- }
-}
-
-static inline void
-reenableupdaterule(Solver *solv, Id p)
-{
- Pool *pool = solv->pool;
- Rule *r;
-
- MAPCLR(&solv->noupdate, p - solv->installed->start);
- r = solv->rules + solv->updaterules + (p - solv->installed->start);
- if (r->p)
- {
- if (r->d < 0)
- {
- solver_enablerule(solv, r);
- IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
- solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
- }
- }
- }
- else
- {
- r = solv->rules + solv->featurerules + (p - solv->installed->start);
- if (r->p && r->d < 0)
- {
- solver_enablerule(solv, r);
- IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
- solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
- }
- }
- }
- if (solv->bestrules_pkg)
- {
- int i, ni;
- ni = solv->bestrules_end - solv->bestrules;
- for (i = 0; i < ni; i++)
- if (solv->bestrules_pkg[i] == p)
- solver_enablerule(solv, solv->rules + solv->bestrules + i);
- }
-}
-
-
-/***********************************************************************
- ***
- *** Infarch rule part
- ***
- *** Infarch rules make sure the solver uses the best architecture of
- *** a package if multiple archetectures are available
- ***
- ***/
-
-void
-solver_addinfarchrules(Solver *solv, Map *addedmap)
-{
- Pool *pool = solv->pool;
- Repo *installed = pool->installed;
- int first, i, j;
- Id p, pp, a, aa, bestarch;
- Solvable *s, *ps, *bests;
- Queue badq, allowedarchs;
- Queue lsq;
-
- queue_init(&badq);
- queue_init(&allowedarchs);
- queue_init(&lsq);
- solv->infarchrules = solv->nrules;
- for (i = 1; i < pool->nsolvables; i++)
- {
- if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
- continue;
- s = pool->solvables + i;
- first = i;
- bestarch = 0;
- bests = 0;
- queue_empty(&allowedarchs);
- FOR_PROVIDES(p, pp, s->name)
- {
- ps = pool->solvables + p;
- if (ps->name != s->name || !MAPTST(addedmap, p))
- continue;
- if (p == i)
- first = 0;
- if (first)
- break;
- a = ps->arch;
- a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
- if (a != 1 && installed && ps->repo == installed)
- {
- if (!solv->dupmap_all && !(solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
- queue_pushunique(&allowedarchs, ps->arch); /* also ok to keep this architecture */
- continue; /* ignore installed solvables when calculating the best arch */
- }
- if (a && a != 1 && (!bestarch || a < bestarch))
- {
- bestarch = a;
- bests = ps;
- }
- }
- if (first)
- continue;
-
- /* speed up common case where installed package already has best arch */
- if (allowedarchs.count == 1 && bests && allowedarchs.elements[0] == bests->arch)
- allowedarchs.count--; /* installed arch is best */
-
- if (allowedarchs.count && pool->implicitobsoleteusescolors && installed && bestarch)
- {
- /* need an extra pass for lockstep checking: we only allow to keep an inferior arch
- * if the corresponding installed package is not lock-stepped */
- queue_empty(&allowedarchs);
- FOR_PROVIDES(p, pp, s->name)
- {
- Id p2, pp2;
- ps = pool->solvables + p;
- if (ps->name != s->name || ps->repo != installed || !MAPTST(addedmap, p))
- continue;
- if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
- continue;
- a = ps->arch;
- a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
- if (!a)
- {
- queue_pushunique(&allowedarchs, ps->arch); /* strange arch, allow */
- continue;
- }
- if (a == 1 || ((a ^ bestarch) & 0xffff0000) == 0)
- continue;
- /* have installed package with inferior arch, check if lock-stepped */
- FOR_PROVIDES(p2, pp2, s->name)
- {
- Solvable *s2 = pool->solvables + p2;
- Id a2;
- if (p2 == p || s2->name != s->name || s2->evr != pool->solvables[p].evr || s2->arch == pool->solvables[p].arch)
- continue;
- a2 = s2->arch;
- a2 = (a2 <= pool->lastarch) ? pool->id2arch[a2] : 0;
- if (a2 && (a2 == 1 || ((a2 ^ bestarch) & 0xffff0000) == 0))
- break;
- }
- if (!p2)
- queue_pushunique(&allowedarchs, ps->arch);
- }
- }
-
- /* find all bad packages */
- queue_empty(&badq);
- FOR_PROVIDES(p, pp, s->name)
- {
- ps = pool->solvables + p;
- if (ps->name != s->name || !MAPTST(addedmap, p))
- continue;
- a = ps->arch;
- a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
- if (a != 1 && bestarch && ((a ^ bestarch) & 0xffff0000) != 0)
- {
- if (installed && ps->repo == installed)
- {
- if (pool->implicitobsoleteusescolors)
- queue_push(&badq, p); /* special lock-step handling, see below */
- continue; /* always ok to keep an installed package */
- }
- for (j = 0; j < allowedarchs.count; j++)
- {
- aa = allowedarchs.elements[j];
- if (ps->arch == aa)
- break;
- aa = (aa <= pool->lastarch) ? pool->id2arch[aa] : 0;
- if (aa && ((a ^ aa) & 0xffff0000) == 0)
- break; /* compatible */
- }
- if (j == allowedarchs.count)
- queue_push(&badq, p);
- }
- }
-
- /* block all solvables in the badq! */
- for (j = 0; j < badq.count; j++)
- {
- p = badq.elements[j];
- /* lock-step */
- if (pool->implicitobsoleteusescolors)
- {
- Id p2;
- int haveinstalled = 0;
- queue_empty(&lsq);
- FOR_PROVIDES(p2, pp, s->name)
- {
- Solvable *s2 = pool->solvables + p2;
- if (p2 == p || s2->name != s->name || s2->evr != pool->solvables[p].evr || s2->arch == pool->solvables[p].arch)
- continue;
- a = s2->arch;
- a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
- if (a && (a == 1 || ((a ^ bestarch) & 0xffff000) == 0))
- {
- queue_push(&lsq, p2);
- if (installed && s2->repo == installed)
- haveinstalled = 1;
- }
- }
- if (installed && pool->solvables[p].repo == installed && !haveinstalled)
- continue; /* installed package not in lock-step */
- }
- if (lsq.count < 2)
- solver_addrule(solv, -p, lsq.count ? lsq.elements[0] : 0, 0);
- else
- solver_addrule(solv, -p, 0, pool_queuetowhatprovides(pool, &lsq));
- }
- }
- queue_free(&lsq);
- queue_free(&badq);
- queue_free(&allowedarchs);
- solv->infarchrules_end = solv->nrules;
-}
-
-static inline void
-disableinfarchrule(Solver *solv, Id name)
-{
- Pool *pool = solv->pool;
- Rule *r;
- int i;
- for (i = solv->infarchrules, r = solv->rules + i; i < solv->infarchrules_end; i++, r++)
- {
- if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
- solver_disablerule(solv, r);
- }
-}
-
-static inline void
-reenableinfarchrule(Solver *solv, Id name)
-{
- Pool *pool = solv->pool;
- Rule *r;
- int i;
- for (i = solv->infarchrules, r = solv->rules + i; i < solv->infarchrules_end; i++, r++)
- {
- if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
- {
- solver_enablerule(solv, r);
- IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
- solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
- }
- }
- }
-}
-
-
-/***********************************************************************
- ***
- *** Dup rule part
- ***
- *** Dup rules make sure a package is selected from the specified dup
- *** repositories if an update candidate is included in one of them.
- ***
- ***/
-
-static inline void
-add_cleandeps_package(Solver *solv, Id p)
-{
- if (!solv->cleandeps_updatepkgs)
- {
- solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
- queue_init(solv->cleandeps_updatepkgs);
- }
- queue_pushunique(solv->cleandeps_updatepkgs, p);
-}
-
-static void
-solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
-{
- Pool *pool = solv->pool;
- Solvable *ps, *s = pool->solvables + p;
- Repo *installed = solv->installed;
- Id pi, pip, obs, *obsp;
-
- MAPSET(&solv->dupinvolvedmap, p);
- if (targeted)
- MAPSET(&solv->dupmap, p);
- FOR_PROVIDES(pi, pip, s->name)
- {
- ps = pool->solvables + pi;
- if (ps->name != s->name)
- continue;
- MAPSET(&solv->dupinvolvedmap, pi);
- if (targeted && ps->repo == installed && solv->obsoletes && solv->obsoletes[pi - installed->start])
- {
- Id *opp, pi2;
- for (opp = solv->obsoletes_data + solv->obsoletes[pi - installed->start]; (pi2 = *opp++) != 0;)
- if (pool->solvables[pi2].repo != installed)
- MAPSET(&solv->dupinvolvedmap, pi2);
- }
- if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0)
- {
- if (!solv->bestupdatemap.size)
- map_grow(&solv->bestupdatemap, installed->end - installed->start);
- MAPSET(&solv->bestupdatemap, pi - installed->start);
- }
- if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
- add_cleandeps_package(solv, pi);
- if (!targeted && ps->repo != installed)
- MAPSET(&solv->dupmap, pi);
- }
- if (s->repo == installed && solv->obsoletes && solv->obsoletes[p - installed->start])
- {
- Id *opp;
- for (opp = solv->obsoletes_data + solv->obsoletes[p - installed->start]; (pi = *opp++) != 0;)
- {
- ps = pool->solvables + pi;
- if (ps->repo == installed)
- continue;
- MAPSET(&solv->dupinvolvedmap, pi);
- if (!targeted)
- MAPSET(&solv->dupmap, pi);
- }
- }
- if (targeted && s->repo != installed && s->obsoletes)
- {
- /* XXX: check obsoletes/provides combination */
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(pi, pip, obs)
- {
- Solvable *ps = pool->solvables + pi;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- MAPSET(&solv->dupinvolvedmap, pi);
- if (targeted && ps->repo == installed && solv->obsoletes && solv->obsoletes[pi - installed->start])
- {
- Id *opp, pi2;
- for (opp = solv->obsoletes_data + solv->obsoletes[pi - installed->start]; (pi2 = *opp++) != 0;)
- if (pool->solvables[pi2].repo != installed)
- MAPSET(&solv->dupinvolvedmap, pi2);
- }
- if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0)
- {
- if (!solv->bestupdatemap.size)
- map_grow(&solv->bestupdatemap, installed->end - installed->start);
- MAPSET(&solv->bestupdatemap, pi - installed->start);
- }
- if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
- add_cleandeps_package(solv, pi);
- }
- }
- }
-}
-
-void
-solver_createdupmaps(Solver *solv)
-{
- Queue *job = &solv->job;
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- Id select, how, what, p, pp;
- Solvable *s;
- int i, targeted;
-
- map_init(&solv->dupmap, pool->nsolvables);
- map_init(&solv->dupinvolvedmap, pool->nsolvables);
- for (i = 0; i < job->count; i += 2)
- {
- how = job->elements[i];
- select = job->elements[i] & SOLVER_SELECTMASK;
- what = job->elements[i + 1];
- switch (how & SOLVER_JOBMASK)
- {
- case SOLVER_DISTUPGRADE:
- if (select == SOLVER_SOLVABLE_REPO)
- {
- Repo *repo;
- if (what <= 0 || what > pool->nrepos)
- break;
- repo = pool_id2repo(pool, what);
- if (!repo)
- break;
- if (repo != installed && !(how & SOLVER_TARGETED) && solv->noautotarget)
- break;
- targeted = repo != installed || (how & SOLVER_TARGETED) != 0;
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- if (repo != installed && !pool_installable(pool, s))
- continue;
- solver_addtodupmaps(solv, p, how, targeted);
- }
- }
- else if (select == SOLVER_SOLVABLE_ALL)
- {
- FOR_POOL_SOLVABLES(p)
- {
- MAPSET(&solv->dupinvolvedmap, p);
- if (installed && pool->solvables[p].repo != installed)
- MAPSET(&solv->dupmap, p);
- }
- }
- else
- {
- targeted = how & SOLVER_TARGETED ? 1 : 0;
- if (installed && !targeted && !solv->noautotarget)
- {
- FOR_JOB_SELECT(p, pp, select, what)
- if (pool->solvables[p].repo == installed)
- break;
- targeted = p == 0;
- }
- else if (!installed && !solv->noautotarget)
- targeted = 1;
- FOR_JOB_SELECT(p, pp, select, what)
- {
- Solvable *s = pool->solvables + p;
- if (!s->repo)
- continue;
- if (s->repo != installed && !targeted)
- continue;
- if (s->repo != installed && !pool_installable(pool, s))
- continue;
- solver_addtodupmaps(solv, p, how, targeted);
- }
- }
- break;
- default:
- break;
- }
- }
- MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE);
-}
-
-void
-solver_freedupmaps(Solver *solv)
-{
- map_free(&solv->dupmap);
- /* we no longer free solv->dupinvolvedmap as we need it in
- * policy's priority pruning code. sigh. */
-}
-
-void
-solver_addduprules(Solver *solv, Map *addedmap)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- Id p, pp;
- Solvable *s, *ps;
- int first, i;
- Rule *r;
-
- solv->duprules = solv->nrules;
- for (i = 1; i < pool->nsolvables; i++)
- {
- if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
- continue;
- s = pool->solvables + i;
- first = i;
- FOR_PROVIDES(p, pp, s->name)
- {
- ps = pool->solvables + p;
- if (ps->name != s->name || !MAPTST(addedmap, p))
- continue;
- if (p == i)
- first = 0;
- if (first)
- break;
- if (!MAPTST(&solv->dupinvolvedmap, p))
- continue;
- if (installed && ps->repo == installed)
- {
- if (!solv->updatemap.size)
- map_grow(&solv->updatemap, installed->end - installed->start);
- MAPSET(&solv->updatemap, p - installed->start);
- if (!MAPTST(&solv->dupmap, p))
- {
- Id ip, ipp;
- /* is installed identical to a good one? */
- FOR_PROVIDES(ip, ipp, ps->name)
- {
- Solvable *is = pool->solvables + ip;
- if (!MAPTST(&solv->dupmap, ip))
- continue;
- if (is->evr == ps->evr && solvable_identical(ps, is))
- break;
- }
- if (ip)
- {
- /* ok, found a good one. we may keep this package. */
- MAPSET(&solv->dupmap, p); /* for best rules processing */
- continue;
- }
- r = solv->rules + solv->updaterules + (p - installed->start);
- if (!r->p)
- r = solv->rules + solv->featurerules + (p - installed->start);
- if (r->p && solv->specialupdaters && solv->specialupdaters[p - installed->start])
- {
- /* this is a multiversion orphan, we're good if an update is installed */
- solver_addrule(solv, -p, 0, solv->specialupdaters[p - installed->start]);
- continue;
- }
- solver_addrule(solv, -p, 0, 0); /* no match, sorry */
- }
- }
- else if (!MAPTST(&solv->dupmap, p))
- solver_addrule(solv, -p, 0, 0);
- }
- }
- solv->duprules_end = solv->nrules;
-}
-
-
-static inline void
-disableduprule(Solver *solv, Id name)
-{
- Pool *pool = solv->pool;
- Rule *r;
- int i;
- for (i = solv->duprules, r = solv->rules + i; i < solv->duprules_end; i++, r++)
- {
- if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
- solver_disablerule(solv, r);
- }
-}
-
-static inline void
-reenableduprule(Solver *solv, Id name)
-{
- Pool *pool = solv->pool;
- Rule *r;
- int i;
- for (i = solv->duprules, r = solv->rules + i; i < solv->duprules_end; i++, r++)
- {
- if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
- {
- solver_enablerule(solv, r);
- IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
- solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
- }
- }
- }
-}
-
-
-/***********************************************************************
- ***
- *** Policy rule disabling/reenabling
- ***
- *** Disable all policy rules that conflict with our jobs. If a job
- *** gets disabled later on, reenable the involved policy rules again.
- ***
- ***/
-
-#define DISABLE_UPDATE 1
-#define DISABLE_INFARCH 2
-#define DISABLE_DUP 3
-
-/*
- * add all installed packages that package p obsoletes to Queue q.
- * Package p is not installed. Also, we know that if
- * solv->keepexplicitobsoletes is not set, p is not in the multiversion map.
- * Entries may get added multiple times.
- */
-static void
-add_obsoletes(Solver *solv, Id p, Queue *q)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- Id p2, pp2;
- Solvable *s = pool->solvables + p;
- Id obs, *obsp;
- Id lastp2 = 0;
-
- if (!solv->keepexplicitobsoletes || !(solv->multiversion.size && MAPTST(&solv->multiversion, p)))
- {
- FOR_PROVIDES(p2, pp2, s->name)
- {
- Solvable *ps = pool->solvables + p2;
- if (ps->repo != installed)
- continue;
- if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
- continue;
- if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- queue_push(q, p2);
- lastp2 = p2;
- }
- }
- if (!s->obsoletes)
- return;
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- FOR_PROVIDES(p2, pp2, obs)
- {
- Solvable *ps = pool->solvables + p2;
- if (ps->repo != installed)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- if (p2 == lastp2)
- continue;
- queue_push(q, p2);
- lastp2 = p2;
- }
-}
-
-/*
- * Call add_obsoletes and intersect the result with the
- * elements in Queue q starting at qstart.
- * Assumes that it's the first call if qstart == q->count.
- * May use auxillary map m for the intersection process, all
- * elements of q starting at qstart must have their bit cleared.
- * (This is also true after the function returns.)
- */
-static void
-intersect_obsoletes(Solver *solv, Id p, Queue *q, int qstart, Map *m)
-{
- int i, j;
- int qcount = q->count;
-
- add_obsoletes(solv, p, q);
- if (qcount == qstart)
- return; /* first call */
- if (qcount == q->count)
- j = qstart;
- else if (qcount == qstart + 1)
- {
- /* easy if there's just one element */
- j = qstart;
- for (i = qcount; i < q->count; i++)
- if (q->elements[i] == q->elements[qstart])
- {
- j++; /* keep the element */
- break;
- }
- }
- else if (!m->size && q->count - qstart <= 8)
- {
- /* faster than a map most of the time */
- int k;
- for (i = j = qstart; i < qcount; i++)
- {
- Id ip = q->elements[i];
- for (k = qcount; k < q->count; k++)
- if (q->elements[k] == ip)
- {
- q->elements[j++] = ip;
- break;
- }
- }
- }
- else
- {
- /* for the really pathologic cases we use the map */
- Repo *installed = solv->installed;
- if (!m->size)
- map_init(m, installed->end - installed->start);
- for (i = qcount; i < q->count; i++)
- MAPSET(m, q->elements[i] - installed->start);
- for (i = j = qstart; i < qcount; i++)
- if (MAPTST(m, q->elements[i] - installed->start))
- {
- MAPCLR(m, q->elements[i] - installed->start);
- q->elements[j++] = q->elements[i];
- }
- }
- queue_truncate(q, j);
-}
-
-static void
-jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
-{
- Pool *pool = solv->pool;
- Id select, p, pp;
- Repo *installed;
- Solvable *s;
- int i, j, set, qstart;
- Map omap;
-
- installed = solv->installed;
- select = how & SOLVER_SELECTMASK;
- switch (how & SOLVER_JOBMASK)
- {
- case SOLVER_INSTALL:
- set = how & SOLVER_SETMASK;
- if (!(set & SOLVER_NOAUTOSET))
- {
- /* automatically add set bits by analysing the job */
- if (select == SOLVER_SOLVABLE_NAME)
- set |= SOLVER_SETNAME;
- if (select == SOLVER_SOLVABLE)
- set |= SOLVER_SETNAME | SOLVER_SETARCH | SOLVER_SETVENDOR | SOLVER_SETREPO | SOLVER_SETEVR;
- else if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(what))
- {
- Reldep *rd = GETRELDEP(pool, what);
- if (rd->flags == REL_EQ && select == SOLVER_SOLVABLE_NAME)
- {
- if (pool->disttype != DISTTYPE_DEB)
- {
- const char *rel = strrchr(pool_id2str(pool, rd->evr), '-');
- set |= rel ? SOLVER_SETEVR : SOLVER_SETEV;
- }
- else
- set |= SOLVER_SETEVR;
- }
- if (rd->flags <= 7 && ISRELDEP(rd->name))
- rd = GETRELDEP(pool, rd->name);
- if (rd->flags == REL_ARCH)
- set |= SOLVER_SETARCH;
- }
- }
- else
- set &= ~SOLVER_NOAUTOSET;
- if (!set)
- return;
- if ((set & SOLVER_SETARCH) != 0 && solv->infarchrules != solv->infarchrules_end)
- {
- if (select == SOLVER_SOLVABLE)
- queue_push2(q, DISABLE_INFARCH, pool->solvables[what].name);
- else
- {
- int qcnt = q->count;
- /* does not work for SOLVER_SOLVABLE_ALL and SOLVER_SOLVABLE_REPO, but
- they are not useful for SOLVER_INSTALL jobs anyway */
- FOR_JOB_SELECT(p, pp, select, what)
- {
- s = pool->solvables + p;
- /* unify names */
- for (i = qcnt; i < q->count; i += 2)
- if (q->elements[i + 1] == s->name)
- break;
- if (i < q->count)
- continue;
- queue_push2(q, DISABLE_INFARCH, s->name);
- }
- }
- }
- if ((set & SOLVER_SETREPO) != 0 && solv->duprules != solv->duprules_end)
- {
- if (select == SOLVER_SOLVABLE)
- queue_push2(q, DISABLE_DUP, pool->solvables[what].name);
- else
- {
- int qcnt = q->count;
- FOR_JOB_SELECT(p, pp, select, what)
- {
- s = pool->solvables + p;
- /* unify names */
- for (i = qcnt; i < q->count; i += 2)
- if (q->elements[i + 1] == s->name)
- break;
- if (i < q->count)
- continue;
- queue_push2(q, DISABLE_DUP, s->name);
- }
- }
- }
- if (!installed || installed->end == installed->start)
- return;
- /* now the hard part: disable some update rules */
-
- /* first check if we have multiversion or installed packages in the job */
- i = j = 0;
- FOR_JOB_SELECT(p, pp, select, what)
- {
- if (pool->solvables[p].repo == installed)
- j = p;
- else if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes)
- return;
- i++;
- }
- if (j) /* have installed packages */
- {
- /* this is for dupmap_all jobs, it can go away if we create
- * duprules for them */
- if (i == 1 && (set & SOLVER_SETREPO) != 0)
- queue_push2(q, DISABLE_UPDATE, j);
- return;
- }
-
- omap.size = 0;
- qstart = q->count;
- FOR_JOB_SELECT(p, pp, select, what)
- {
- intersect_obsoletes(solv, p, q, qstart, &omap);
- if (q->count == qstart)
- break;
- }
- if (omap.size)
- map_free(&omap);
-
- if (qstart == q->count)
- return; /* nothing to prune */
-
- /* convert result to (DISABLE_UPDATE, p) pairs */
- i = q->count;
- for (j = qstart; j < i; j++)
- queue_push(q, q->elements[j]);
- for (j = qstart; j < q->count; j += 2)
- {
- q->elements[j] = DISABLE_UPDATE;
- q->elements[j + 1] = q->elements[i++];
- }
-
- /* now that we know which installed packages are obsoleted check each of them */
- if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
- return; /* all is set, nothing to do */
-
- for (i = j = qstart; i < q->count; i += 2)
- {
- Solvable *is = pool->solvables + q->elements[i + 1];
- FOR_JOB_SELECT(p, pp, select, what)
- {
- int illegal = 0;
- s = pool->solvables + p;
- if ((set & SOLVER_SETEVR) != 0)
- illegal |= POLICY_ILLEGAL_DOWNGRADE; /* ignore */
- if ((set & SOLVER_SETNAME) != 0)
- illegal |= POLICY_ILLEGAL_NAMECHANGE; /* ignore */
- if ((set & SOLVER_SETARCH) != 0)
- illegal |= POLICY_ILLEGAL_ARCHCHANGE; /* ignore */
- if ((set & SOLVER_SETVENDOR) != 0)
- illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore */
- illegal = policy_is_illegal(solv, is, s, illegal);
- if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0)
- {
- /* it's ok if the EV is different */
- if (pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0)
- illegal = 0;
- }
- if (illegal)
- break;
- }
- if (!p)
- {
- /* no package conflicts with the update rule */
- /* thus keep the DISABLE_UPDATE */
- q->elements[j + 1] = q->elements[i + 1];
- j += 2;
- }
- }
- queue_truncate(q, j);
- return;
-
- case SOLVER_ERASE:
- if (!installed)
- break;
- if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
- FOR_REPO_SOLVABLES(installed, p, s)
- queue_push2(q, DISABLE_UPDATE, p);
- FOR_JOB_SELECT(p, pp, select, what)
- if (pool->solvables[p].repo == installed)
- {
- queue_push2(q, DISABLE_UPDATE, p);
-#ifdef ENABLE_LINKED_PKGS
- if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
- queue_push2(q, DISABLE_UPDATE, solv->instbuddy[p - installed->start]);
-#endif
- }
- return;
- default:
- return;
- }
-}
-
-/* disable all policy rules that are in conflict with our job list */
-void
-solver_disablepolicyrules(Solver *solv)
-{
- Queue *job = &solv->job;
- int i, j;
- Queue allq;
- Rule *r;
- Id lastjob = -1;
- Id allqbuf[128];
-
- queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
-
- for (i = solv->jobrules; i < solv->jobrules_end; i++)
- {
- r = solv->rules + i;
- if (r->d < 0) /* disabled? */
- continue;
- j = solv->ruletojob.elements[i - solv->jobrules];
- if (j == lastjob)
- continue;
- lastjob = j;
- jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
- }
- if (solv->cleandepsmap.size)
- {
- solver_createcleandepsmap(solv, &solv->cleandepsmap, 0);
- for (i = solv->installed->start; i < solv->installed->end; i++)
- if (MAPTST(&solv->cleandepsmap, i - solv->installed->start))
- queue_push2(&allq, DISABLE_UPDATE, i);
- }
- MAPZERO(&solv->noupdate);
- for (i = 0; i < allq.count; i += 2)
- {
- Id type = allq.elements[i], arg = allq.elements[i + 1];
- switch(type)
- {
- case DISABLE_UPDATE:
- disableupdaterule(solv, arg);
- break;
- case DISABLE_INFARCH:
- disableinfarchrule(solv, arg);
- break;
- case DISABLE_DUP:
- disableduprule(solv, arg);
- break;
- default:
- break;
- }
- }
- queue_free(&allq);
-}
-
-/* we just disabled job #jobidx, now reenable all policy rules that were
- * disabled because of this job */
-void
-solver_reenablepolicyrules(Solver *solv, int jobidx)
-{
- Queue *job = &solv->job;
- int i, j, k, ai;
- Queue q, allq;
- Rule *r;
- Id lastjob = -1;
- Id qbuf[32], allqbuf[32];
-
- queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
- jobtodisablelist(solv, job->elements[jobidx - 1], job->elements[jobidx], &q);
- if (!q.count)
- {
- queue_free(&q);
- return;
- }
- /* now remove everything from q that is disabled by other jobs */
-
- /* first remove cleandeps packages, they count as DISABLE_UPDATE */
- if (solv->cleandepsmap.size)
- {
- solver_createcleandepsmap(solv, &solv->cleandepsmap, 0);
- for (j = k = 0; j < q.count; j += 2)
- {
- if (q.elements[j] == DISABLE_UPDATE)
- {
- Id p = q.elements[j + 1];
- if (p >= solv->installed->start && p < solv->installed->end && MAPTST(&solv->cleandepsmap, p - solv->installed->start))
- continue; /* remove element from q */
- }
- q.elements[k++] = q.elements[j];
- q.elements[k++] = q.elements[j + 1];
- }
- q.count = k;
- if (!q.count)
- {
- queue_free(&q);
- return;
- }
- }
-
- /* now go through the disable list of all other jobs */
- queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
- for (i = solv->jobrules; i < solv->jobrules_end; i++)
- {
- r = solv->rules + i;
- if (r->d < 0) /* disabled? */
- continue;
- j = solv->ruletojob.elements[i - solv->jobrules];
- if (j == lastjob)
- continue;
- lastjob = j;
- jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
- if (!allq.count)
- continue;
- /* remove all elements in allq from q */
- for (j = k = 0; j < q.count; j += 2)
- {
- Id type = q.elements[j], arg = q.elements[j + 1];
- for (ai = 0; ai < allq.count; ai += 2)
- if (allq.elements[ai] == type && allq.elements[ai + 1] == arg)
- break;
- if (ai < allq.count)
- continue; /* found it in allq, remove element from q */
- q.elements[k++] = q.elements[j];
- q.elements[k++] = q.elements[j + 1];
- }
- q.count = k;
- if (!q.count)
- {
- queue_free(&q);
- queue_free(&allq);
- return;
- }
- queue_empty(&allq);
- }
- queue_free(&allq);
-
- /* now re-enable anything that's left in q */
- for (j = 0; j < q.count; j += 2)
- {
- Id type = q.elements[j], arg = q.elements[j + 1];
- switch(type)
- {
- case DISABLE_UPDATE:
- reenableupdaterule(solv, arg);
- break;
- case DISABLE_INFARCH:
- reenableinfarchrule(solv, arg);
- break;
- case DISABLE_DUP:
- reenableduprule(solv, arg);
- break;
- }
- }
- queue_free(&q);
-}
-
-/* we just removed a package from the cleandeps map, now reenable all policy rules that were
- * disabled because of this */
-void
-solver_reenablepolicyrules_cleandeps(Solver *solv, Id pkg)
-{
- Queue *job = &solv->job;
- int i, j;
- Queue allq;
- Rule *r;
- Id lastjob = -1;
- Id allqbuf[128];
-
- queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
- for (i = solv->jobrules; i < solv->jobrules_end; i++)
- {
- r = solv->rules + i;
- if (r->d < 0) /* disabled? */
- continue;
- j = solv->ruletojob.elements[i - solv->jobrules];
- if (j == lastjob)
- continue;
- lastjob = j;
- jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
- }
- for (i = 0; i < allq.count; i += 2)
- if (allq.elements[i] == DISABLE_UPDATE && allq.elements[i + 1] == pkg)
- break;
- if (i == allq.count)
- reenableupdaterule(solv, pkg);
- queue_free(&allq);
-}
-
-
-/***********************************************************************
- ***
- *** Rule info part, tell the user what the rule is about.
- ***
- ***/
-
-static void
-addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep)
-{
- Pool *pool = solv->pool;
- Rule *r;
-
- if (d)
- {
- assert(!p2 && d > 0);
- if (!pool->whatprovidesdata[d])
- d = 0;
- else if (!pool->whatprovidesdata[d + 1])
- {
- p2 = pool->whatprovidesdata[d];
- d = 0;
- }
- }
-
- /* check if this creates the rule we're searching for */
- r = solv->rules + solv->ruleinfoq->elements[0];
- if (d)
- {
- /* three or more literals */
- Id od = r->d < 0 ? -r->d - 1 : r->d;
- if (p != r->p && !od)
- return;
- if (d != od)
- {
- Id *dp = pool->whatprovidesdata + d;
- Id *odp = pool->whatprovidesdata + od;
- while (*dp)
- if (*dp++ != *odp++)
- return;
- if (*odp)
- return;
- }
- if (p < 0 && pool->whatprovidesdata[d] < 0 && type == SOLVER_RULE_PKG_CONFLICTS)
- p2 = pool->whatprovidesdata[d];
- }
- else
- {
- /* one or two literals */
- Id op = p, op2 = p2;
- if (op2 && op > op2) /* normalize */
- {
- Id o = op;
- op = op2;
- op2 = o;
- }
- if (r->p != op || r->w2 != op2 || (r->d && r->d != -1))
- return;
- if (type == SOLVER_RULE_PKG_CONFLICTS && !p2)
- p2 = -SYSTEMSOLVABLE;
- if (type == SOLVER_RULE_PKG_SAME_NAME)
- {
- p = op; /* we normalize same name order */
- p2 = op2;
- }
- }
- /* yep, rule matches. record info */
- queue_push(solv->ruleinfoq, type);
- queue_push(solv->ruleinfoq, p < 0 ? -p : 0);
- queue_push(solv->ruleinfoq, p2 < 0 ? -p2 : 0);
- queue_push(solv->ruleinfoq, dep);
-}
-
-static int
-solver_allruleinfos_cmp(const void *ap, const void *bp, void *dp)
-{
- const Id *a = ap, *b = bp;
- int r;
-
- r = a[0] - b[0];
- if (r)
- return r;
- r = a[1] - b[1];
- if (r)
- return r;
- r = a[2] - b[2];
- if (r)
- return r;
- r = a[3] - b[3];
- if (r)
- return r;
- return 0;
-}
-
-static void
-getpkgruleinfos(Solver *solv, Rule *r, Queue *rq)
-{
- Pool *pool = solv->pool;
- Id l, pp;
- if (r->p >= 0)
- return;
- queue_push(rq, r - solv->rules); /* push the rule we're interested in */
- solv->ruleinfoq = rq;
- FOR_RULELITERALS(l, pp, r)
- {
- if (l >= 0)
- break;
- solver_addpkgrulesforsolvable(solv, pool->solvables - l, 0);
- }
-#ifdef ENABLE_LINKED_PKGS
- FOR_RULELITERALS(l, pp, r)
- {
- if (l < 0)
- {
- if (l == r->p)
- continue;
- break;
- }
- if (!strchr(pool_id2str(pool, pool->solvables[l].name), ':') || !has_package_link(pool, pool->solvables + l))
- break;
- add_package_link(solv, pool->solvables + l, 0, 0);
- }
-#endif
- solv->ruleinfoq = 0;
- queue_shift(rq);
-}
-
-int
-solver_allruleinfos(Solver *solv, Id rid, Queue *rq)
-{
- Rule *r = solv->rules + rid;
- int i, j;
-
- queue_empty(rq);
- if (rid <= 0 || rid >= solv->pkgrules_end)
- {
- Id type, from, to, dep;
- type = solver_ruleinfo(solv, rid, &from, &to, &dep);
- queue_push(rq, type);
- queue_push(rq, from);
- queue_push(rq, to);
- queue_push(rq, dep);
- return 1;
- }
- getpkgruleinfos(solv, r, rq);
- /* now sort & unify em */
- if (!rq->count)
- return 0;
- solv_sort(rq->elements, rq->count / 4, 4 * sizeof(Id), solver_allruleinfos_cmp, 0);
- /* throw out identical entries */
- for (i = j = 0; i < rq->count; i += 4)
- {
- if (j)
- {
- if (rq->elements[i] == rq->elements[j - 4] &&
- rq->elements[i + 1] == rq->elements[j - 3] &&
- rq->elements[i + 2] == rq->elements[j - 2] &&
- rq->elements[i + 3] == rq->elements[j - 1])
- continue;
- }
- rq->elements[j++] = rq->elements[i];
- rq->elements[j++] = rq->elements[i + 1];
- rq->elements[j++] = rq->elements[i + 2];
- rq->elements[j++] = rq->elements[i + 3];
- }
- rq->count = j;
- return j / 4;
-}
-
-SolverRuleinfo
-solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
-{
- Pool *pool = solv->pool;
- Rule *r = solv->rules + rid;
- SolverRuleinfo type = SOLVER_RULE_UNKNOWN;
-
- if (fromp)
- *fromp = 0;
- if (top)
- *top = 0;
- if (depp)
- *depp = 0;
- if (rid > 0 && rid < solv->pkgrules_end)
- {
- Queue rq;
- int i;
-
- if (r->p >= 0)
- return SOLVER_RULE_PKG;
- if (fromp)
- *fromp = -r->p;
- queue_init(&rq);
- getpkgruleinfos(solv, r, &rq);
- type = SOLVER_RULE_PKG;
- for (i = 0; i < rq.count; i += 4)
- {
- Id qt, qo, qp, qd;
- qt = rq.elements[i];
- qp = rq.elements[i + 1];
- qo = rq.elements[i + 2];
- qd = rq.elements[i + 3];
- if (type == SOLVER_RULE_PKG || type > qt)
- {
- type = qt;
- if (fromp)
- *fromp = qp;
- if (top)
- *top = qo;
- if (depp)
- *depp = qd;
- }
- }
- queue_free(&rq);
- return type;
- }
- if (rid >= solv->jobrules && rid < solv->jobrules_end)
- {
- Id jidx = solv->ruletojob.elements[rid - solv->jobrules];
- if (fromp)
- *fromp = jidx;
- if (top)
- *top = solv->job.elements[jidx];
- if (depp)
- *depp = solv->job.elements[jidx + 1];
- if ((r->d == 0 || r->d == -1) && r->w2 == 0 && r->p == -SYSTEMSOLVABLE)
- {
- Id how = solv->job.elements[jidx];
- if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_INSTALL|SOLVER_SOLVABLE_NAME))
- return SOLVER_RULE_JOB_UNKNOWN_PACKAGE;
- if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES))
- return SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP;
- if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_ERASE|SOLVER_SOLVABLE_NAME))
- return SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM;
- if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_ERASE|SOLVER_SOLVABLE_PROVIDES))
- return SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM;
- return SOLVER_RULE_JOB_UNSUPPORTED;
- }
- return SOLVER_RULE_JOB;
- }
- if (rid >= solv->updaterules && rid < solv->updaterules_end)
- {
- if (fromp)
- *fromp = solv->installed->start + (rid - solv->updaterules);
- return SOLVER_RULE_UPDATE;
- }
- if (rid >= solv->featurerules && rid < solv->featurerules_end)
- {
- if (fromp)
- *fromp = solv->installed->start + (rid - solv->featurerules);
- return SOLVER_RULE_FEATURE;
- }
- if (rid >= solv->duprules && rid < solv->duprules_end)
- {
- if (fromp)
- *fromp = -r->p;
- if (depp)
- *depp = pool->solvables[-r->p].name;
- return SOLVER_RULE_DISTUPGRADE;
- }
- if (rid >= solv->infarchrules && rid < solv->infarchrules_end)
- {
- if (fromp)
- *fromp = -r->p;
- if (depp)
- *depp = pool->solvables[-r->p].name;
- return SOLVER_RULE_INFARCH;
- }
- if (rid >= solv->bestrules && rid < solv->bestrules_end)
- {
- if (fromp && solv->bestrules_pkg[rid - solv->bestrules] > 0)
- *fromp = solv->bestrules_pkg[rid - solv->bestrules];
- return SOLVER_RULE_BEST;
- }
- if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
- {
- if (fromp)
- *fromp = -r->p;
- if (top)
- {
- /* first solvable is enough, we just need it for the name */
- if (!r->d || r->d == -1)
- *top = r->w2;
- else
- *top = pool->whatprovidesdata[r->d < 0 ? -r->d : r->d];
- }
- if (depp)
- *depp = solv->yumobsrules_info[rid - solv->yumobsrules];
- return SOLVER_RULE_YUMOBS;
- }
- if (rid >= solv->choicerules && rid < solv->choicerules_end)
- {
- return SOLVER_RULE_CHOICE;
- }
- if (rid >= solv->learntrules)
- {
- return SOLVER_RULE_LEARNT;
- }
- return SOLVER_RULE_UNKNOWN;
-}
-
-SolverRuleinfo
-solver_ruleclass(Solver *solv, Id rid)
-{
- if (rid <= 0)
- return SOLVER_RULE_UNKNOWN;
- if (rid > 0 && rid < solv->pkgrules_end)
- return SOLVER_RULE_PKG;
- if (rid >= solv->jobrules && rid < solv->jobrules_end)
- return SOLVER_RULE_JOB;
- if (rid >= solv->updaterules && rid < solv->updaterules_end)
- return SOLVER_RULE_UPDATE;
- if (rid >= solv->featurerules && rid < solv->featurerules_end)
- return SOLVER_RULE_FEATURE;
- if (rid >= solv->duprules && rid < solv->duprules_end)
- return SOLVER_RULE_DISTUPGRADE;
- if (rid >= solv->infarchrules && rid < solv->infarchrules_end)
- return SOLVER_RULE_INFARCH;
- if (rid >= solv->bestrules && rid < solv->bestrules_end)
- return SOLVER_RULE_BEST;
- if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
- return SOLVER_RULE_YUMOBS;
- if (rid >= solv->choicerules && rid < solv->choicerules_end)
- return SOLVER_RULE_CHOICE;
- if (rid >= solv->learntrules && rid < solv->nrules)
- return SOLVER_RULE_LEARNT;
- return SOLVER_RULE_UNKNOWN;
-}
-
-void
-solver_ruleliterals(Solver *solv, Id rid, Queue *q)
-{
- Pool *pool = solv->pool;
- Id p, pp;
- Rule *r;
-
- queue_empty(q);
- r = solv->rules + rid;
- FOR_RULELITERALS(p, pp, r)
- if (p != -SYSTEMSOLVABLE)
- queue_push(q, p);
- if (!q->count)
- queue_push(q, -SYSTEMSOLVABLE); /* hmm, better to return an empty result? */
-}
-
-int
-solver_rule2jobidx(Solver *solv, Id rid)
-{
- if (rid < solv->jobrules || rid >= solv->jobrules_end)
- return 0;
- return solv->ruletojob.elements[rid - solv->jobrules] + 1;
-}
-
-/* job rule introspection */
-Id
-solver_rule2job(Solver *solv, Id rid, Id *whatp)
-{
- int idx;
- if (rid < solv->jobrules || rid >= solv->jobrules_end)
- {
- if (whatp)
- *whatp = 0;
- return 0;
- }
- idx = solv->ruletojob.elements[rid - solv->jobrules];
- if (whatp)
- *whatp = solv->job.elements[idx + 1];
- return solv->job.elements[idx];
-}
-
-/* update/feature rule introspection */
-Id
-solver_rule2solvable(Solver *solv, Id rid)
-{
- if (rid >= solv->updaterules && rid < solv->updaterules_end)
- return rid - solv->updaterules;
- if (rid >= solv->featurerules && rid < solv->featurerules_end)
- return rid - solv->featurerules;
- return 0;
-}
-
-Id
-solver_rule2pkgrule(Solver *solv, Id rid)
-{
- if (rid >= solv->choicerules && rid < solv->choicerules_end)
- return solv->choicerules_ref[rid - solv->choicerules];
- return 0;
-}
-
-static void
-solver_rule2rules_rec(Solver *solv, Id rid, Queue *q, Map *seen)
-{
- int i;
- Id rid2;
-
- if (seen)
- MAPSET(seen, rid);
- for (i = solv->learnt_why.elements[rid - solv->learntrules]; (rid2 = solv->learnt_pool.elements[i]) != 0; i++)
- {
- if (seen)
- {
- if (MAPTST(seen, rid2))
- continue;
- if (rid2 >= solv->learntrules)
- solver_rule2rules_rec(solv, rid2, q, seen);
- continue;
- }
- queue_push(q, rid2);
- }
-}
-
-/* learnt rule introspection */
-void
-solver_rule2rules(Solver *solv, Id rid, Queue *q, int recursive)
-{
- queue_empty(q);
- if (rid < solv->learntrules || rid >= solv->nrules)
- return;
- if (recursive)
- {
- Map seen;
- map_init(&seen, solv->nrules);
- solver_rule2rules_rec(solv, rid, q, &seen);
- map_free(&seen);
- }
- else
- solver_rule2rules_rec(solv, rid, q, 0);
-}
-
-
-/* check if the newest versions of pi still provides the dependency we're looking for */
-static int
-solver_choicerulecheck(Solver *solv, Id pi, Rule *r, Map *m, Queue *q)
-{
- Pool *pool = solv->pool;
- Rule *ur;
- Id p, pp;
- int i;
-
- if (!q->count || q->elements[0] != pi)
- {
- if (q->count)
- queue_empty(q);
- ur = solv->rules + solv->updaterules + (pi - pool->installed->start);
- if (!ur->p)
- ur = solv->rules + solv->featurerules + (pi - pool->installed->start);
- if (!ur->p)
- return 0;
- queue_push2(q, pi, 0);
- FOR_RULELITERALS(p, pp, ur)
- if (p > 0)
- queue_push(q, p);
- }
- if (q->count == 2)
- return 1;
- if (q->count == 3)
- {
- p = q->elements[2];
- return MAPTST(m, p) ? 0 : 1;
- }
- if (!q->elements[1])
- {
- for (i = 2; i < q->count; i++)
- if (!MAPTST(m, q->elements[i]))
- break;
- if (i == q->count)
- return 0; /* all provide it, no need to filter */
- /* some don't provide it, have to filter */
- queue_deleten(q, 0, 2);
- policy_filter_unwanted(solv, q, POLICY_MODE_CHOOSE);
- queue_unshift(q, 1); /* filter mark */
- queue_unshift(q, pi);
- }
- for (i = 2; i < q->count; i++)
- if (MAPTST(m, q->elements[i]))
- return 0; /* at least one provides it */
- return 1; /* none of the new packages provided it */
-}
-
-static inline void
-queue_removeelement(Queue *q, Id el)
-{
- int i, j;
- for (i = 0; i < q->count; i++)
- if (q->elements[i] == el)
- break;
- if (i < q->count)
- {
- for (j = i++; i < q->count; i++)
- if (q->elements[i] != el)
- q->elements[j++] = q->elements[i];
- queue_truncate(q, j);
- }
-}
-
-void
-solver_addchoicerules(Solver *solv)
-{
- Pool *pool = solv->pool;
- Map m, mneg;
- Rule *r;
- Queue q, qi, qcheck;
- int i, j, rid, havechoice;
- Id p, d, pp;
- Id p2, pp2;
- Solvable *s, *s2;
- Id lastaddedp, lastaddedd;
- int lastaddedcnt;
- unsigned int now;
-
- solv->choicerules = solv->nrules;
- if (!pool->installed)
- {
- solv->choicerules_end = solv->nrules;
- return;
- }
- now = solv_timems(0);
- solv->choicerules_ref = solv_calloc(solv->pkgrules_end, sizeof(Id));
- queue_init(&q);
- queue_init(&qi);
- queue_init(&qcheck);
- map_init(&m, pool->nsolvables);
- map_init(&mneg, pool->nsolvables);
- /* set up negative assertion map from infarch and dup rules */
- for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
- if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
- MAPSET(&mneg, -r->p);
- for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++)
- if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
- MAPSET(&mneg, -r->p);
- lastaddedp = 0;
- lastaddedd = 0;
- lastaddedcnt = 0;
- for (rid = 1; rid < solv->pkgrules_end ; rid++)
- {
- r = solv->rules + rid;
- if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 <= 0))
- continue; /* only look at requires rules */
- /* solver_printrule(solv, SOLV_DEBUG_RESULT, r); */
- queue_empty(&q);
- queue_empty(&qi);
- havechoice = 0;
- FOR_RULELITERALS(p, pp, r)
- {
- if (p < 0)
- continue;
- s = pool->solvables + p;
- if (!s->repo)
- continue;
- if (s->repo == pool->installed)
- {
- queue_push(&q, p);
- continue;
- }
- /* check if this package is "blocked" by a installed package */
- s2 = 0;
- FOR_PROVIDES(p2, pp2, s->name)
- {
- s2 = pool->solvables + p2;
- if (s2->repo != pool->installed)
- continue;
- if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
- continue;
- if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
- continue;
- break;
- }
- if (p2)
- {
- /* found installed package p2 that we can update to p */
- if (MAPTST(&mneg, p))
- continue;
- if (policy_is_illegal(solv, s2, s, 0))
- continue;
-#if 0
- if (solver_choicerulecheck(solv, p2, r, &m))
- continue;
- queue_push(&qi, p2);
-#else
- queue_push2(&qi, p2, p);
-#endif
- queue_push(&q, p);
- continue;
- }
- if (s->obsoletes)
- {
- Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
- s2 = 0;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(p2, pp2, obs)
- {
- s2 = pool->solvables + p2;
- if (s2->repo != pool->installed)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
- continue;
- break;
- }
- if (p2)
- break;
- }
- if (obs)
- {
- /* found installed package p2 that we can update to p */
- if (MAPTST(&mneg, p))
- continue;
- if (policy_is_illegal(solv, s2, s, 0))
- continue;
-#if 0
- if (solver_choicerulecheck(solv, p2, r, &m))
- continue;
- queue_push(&qi, p2);
-#else
- queue_push2(&qi, p2, p);
-#endif
- queue_push(&q, p);
- continue;
- }
- }
- /* package p is independent of the installed ones */
- havechoice = 1;
- }
- if (!havechoice || !q.count || !qi.count)
- continue; /* no choice */
-
- FOR_RULELITERALS(p, pp, r)
- if (p > 0)
- MAPSET(&m, p);
-
- /* do extra checking */
- for (i = j = 0; i < qi.count; i += 2)
- {
- p2 = qi.elements[i];
- if (!p2)
- continue;
- if (solver_choicerulecheck(solv, p2, r, &m, &qcheck))
- {
- /* oops, remove element p from q */
- queue_removeelement(&q, qi.elements[i + 1]);
- continue;
- }
- qi.elements[j++] = p2;
- }
- queue_truncate(&qi, j);
-
- if (!q.count || !qi.count)
- {
- FOR_RULELITERALS(p, pp, r)
- if (p > 0)
- MAPCLR(&m, p);
- continue;
- }
-
-
- /* now check the update rules of the installed package.
- * if all packages of the update rules are contained in
- * the dependency rules, there's no need to set up the choice rule */
- for (i = 0; i < qi.count; i++)
- {
- Rule *ur;
- if (!qi.elements[i])
- continue;
- ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start);
- if (!ur->p)
- ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start);
- if (!ur->p)
- continue;
- FOR_RULELITERALS(p, pp, ur)
- if (!MAPTST(&m, p))
- break;
- if (p)
- break;
- for (j = i + 1; j < qi.count; j++)
- if (qi.elements[i] == qi.elements[j])
- qi.elements[j] = 0;
- }
- /* empty map again */
- FOR_RULELITERALS(p, pp, r)
- if (p > 0)
- MAPCLR(&m, p);
- if (i == qi.count)
- {
-#if 0
- printf("skipping choice ");
- solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + rid);
-#endif
- continue;
- }
-
- /* don't add identical rules */
- if (lastaddedp == r->p && lastaddedcnt == q.count)
- {
- for (i = 0; i < q.count; i++)
- if (q.elements[i] != pool->whatprovidesdata[lastaddedd + i])
- break;
- if (i == q.count)
- continue; /* already added that one */
- }
- d = q.count ? pool_queuetowhatprovides(pool, &q) : 0;
-
- lastaddedp = r->p;
- lastaddedd = d;
- lastaddedcnt = q.count;
-
- solver_addrule(solv, r->p, 0, d);
- queue_push(&solv->weakruleq, solv->nrules - 1);
- solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
-#if 0
- printf("OLD ");
- solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + rid);
- printf("WEAK CHOICE ");
- solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + solv->nrules - 1);
-#endif
- }
- queue_free(&q);
- queue_free(&qi);
- queue_free(&qcheck);
- map_free(&m);
- map_free(&mneg);
- solv->choicerules_end = solv->nrules;
- /* shrink choicerules_ref */
- solv->choicerules_ref = solv_realloc2(solv->choicerules_ref, solv->choicerules_end - solv->choicerules, sizeof(Id));
- POOL_DEBUG(SOLV_DEBUG_STATS, "choice rule creation took %d ms\n", solv_timems(now));
-}
-
-/* called when a choice rule is disabled by analyze_unsolvable. We also
- * have to disable all other choice rules so that the best packages get
- * picked */
-void
-solver_disablechoicerules(Solver *solv, Rule *r)
-{
- Id rid, p, pp;
- Pool *pool = solv->pool;
- Map m;
- Rule *or;
-
- or = solv->rules + solv->choicerules_ref[(r - solv->rules) - solv->choicerules];
- map_init(&m, pool->nsolvables);
- FOR_RULELITERALS(p, pp, or)
- if (p > 0)
- MAPSET(&m, p);
- FOR_RULELITERALS(p, pp, r)
- if (p > 0)
- MAPCLR(&m, p);
- for (rid = solv->choicerules; rid < solv->choicerules_end; rid++)
- {
- r = solv->rules + rid;
- if (r->d < 0)
- continue;
- or = solv->rules + solv->choicerules_ref[(r - solv->rules) - solv->choicerules];
- FOR_RULELITERALS(p, pp, or)
- if (p > 0 && MAPTST(&m, p))
- break;
- if (p)
- solver_disablerule(solv, r);
- }
-}
-
-static void
-prune_to_update_targets(Solver *solv, Id *cp, Queue *q)
-{
- int i, j;
- Id p, *cp2;
- for (i = j = 0; i < q->count; i++)
- {
- p = q->elements[i];
- for (cp2 = cp; *cp2; cp2++)
- if (*cp2 == p)
- {
- q->elements[j++] = p;
- break;
- }
- }
- queue_truncate(q, j);
-}
-
-static void
-prune_to_dup_packages(Solver *solv, Id p, Queue *q)
-{
- int i, j;
- for (i = j = 0; i < q->count; i++)
- {
- Id p = q->elements[i];
- if (MAPTST(&solv->dupmap, p))
- q->elements[j++] = p;
- }
- queue_truncate(q, j);
-}
-
-void
-solver_addbestrules(Solver *solv, int havebestinstalljobs)
-{
- Pool *pool = solv->pool;
- Id p;
- Solvable *s;
- Repo *installed = solv->installed;
- Queue q, q2;
- Rule *r;
- Queue r2pkg;
- int i, oldcnt;
-
- solv->bestrules = solv->nrules;
- if (!installed)
- {
- solv->bestrules_end = solv->nrules;
- return;
- }
- queue_init(&q);
- queue_init(&q2);
- queue_init(&r2pkg);
-
- if (havebestinstalljobs)
- {
- for (i = 0; i < solv->job.count; i += 2)
- {
- if ((solv->job.elements[i] & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST))
- {
- int j;
- Id p2, pp2;
- for (j = 0; j < solv->ruletojob.count; j++)
- if (solv->ruletojob.elements[j] == i)
- break;
- if (j == solv->ruletojob.count)
- continue;
- r = solv->rules + solv->jobrules + j;
- queue_empty(&q);
- FOR_RULELITERALS(p2, pp2, r)
- if (p2 > 0)
- queue_push(&q, p2);
- if (!q.count)
- continue; /* orphaned */
- /* select best packages, just look at prio and version */
- oldcnt = q.count;
- policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
- if (q.count == oldcnt)
- continue; /* nothing filtered */
- p2 = queue_shift(&q);
- if (q.count < 2)
- solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
- else
- solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
- queue_push(&r2pkg, -(solv->jobrules + j));
- }
- }
- }
-
- if (solv->bestupdatemap_all || solv->bestupdatemap.size)
- {
- FOR_REPO_SOLVABLES(installed, p, s)
- {
- Id d, p2, pp2;
- if (!solv->updatemap_all && (!solv->updatemap.size || !MAPTST(&solv->updatemap, p - installed->start)))
- continue;
- if (!solv->bestupdatemap_all && (!solv->bestupdatemap.size || !MAPTST(&solv->bestupdatemap, p - installed->start)))
- continue;
- queue_empty(&q);
- if (solv->bestobeypolicy)
- r = solv->rules + solv->updaterules + (p - installed->start);
- else
- {
- r = solv->rules + solv->featurerules + (p - installed->start);
- if (!r->p) /* identical to update rule? */
- r = solv->rules + solv->updaterules + (p - installed->start);
- }
- if (solv->specialupdaters && (d = solv->specialupdaters[p - installed->start]) != 0 && r == solv->rules + solv->updaterules + (p - installed->start))
- {
- /* need to check specialupdaters */
- if (r->p == p) /* be careful with the dup case */
- queue_push(&q, p);
- while ((p2 = pool->whatprovidesdata[d++]) != 0)
- queue_push(&q, p2);
- }
- else
- {
- FOR_RULELITERALS(p2, pp2, r)
- if (p2 > 0)
- queue_push(&q, p2);
- }
- if (solv->update_targets && solv->update_targets->elements[p - installed->start])
- prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q);
- if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
- prune_to_dup_packages(solv, p, &q);
- /* select best packages, just look at prio and version */
- policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
- if (!q.count)
- continue; /* orphaned */
- if (solv->bestobeypolicy)
- {
- /* also filter the best of the feature rule packages and add them */
- r = solv->rules + solv->featurerules + (p - installed->start);
- if (r->p)
- {
- int j;
- queue_empty(&q2);
- FOR_RULELITERALS(p2, pp2, r)
- if (p2 > 0)
- queue_push(&q2, p2);
- if (solv->update_targets && solv->update_targets->elements[p - installed->start])
- prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q2);
- if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
- prune_to_dup_packages(solv, p, &q2);
- policy_filter_unwanted(solv, &q2, POLICY_MODE_RECOMMEND);
- for (j = 0; j < q2.count; j++)
- queue_pushunique(&q, q2.elements[j]);
- }
- }
- p2 = queue_shift(&q);
- if (q.count < 2)
- solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
- else
- solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
- queue_push(&r2pkg, p);
- }
- }
- if (r2pkg.count)
- solv->bestrules_pkg = solv_memdup2(r2pkg.elements, r2pkg.count, sizeof(Id));
- solv->bestrules_end = solv->nrules;
- queue_free(&q);
- queue_free(&q2);
- queue_free(&r2pkg);
-}
-
-
-
-
-/* yumobs rule handling */
-
-static void
-find_obsolete_group(Solver *solv, Id obs, Queue *q)
-{
- Pool *pool = solv->pool;
- Queue qn;
- Id p2, pp2, op, *opp, opp2;
- int i, j, qnc, ncnt;
-
- queue_empty(q);
- FOR_PROVIDES(p2, pp2, obs)
- {
- Solvable *s2 = pool->solvables + p2;
- if (s2->repo != pool->installed)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
- continue;
- /* we obsolete installed package s2 with obs. now find all other packages that have the same dep */
- for (opp = solv->obsoletes_data + solv->obsoletes[p2 - solv->installed->start]; (op = *opp++) != 0;)
- {
- Solvable *os = pool->solvables + op;
- Id obs2, *obsp2;
- if (!os->obsoletes)
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
- continue;
- obsp2 = os->repo->idarraydata + os->obsoletes;
- while ((obs2 = *obsp2++) != 0)
- if (obs2 == obs)
- break;
- if (obs2)
- queue_pushunique(q, op);
- }
- /* also search packages with the same name */
- FOR_PROVIDES(op, opp2, s2->name)
- {
- Solvable *os = pool->solvables + op;
- Id obs2, *obsp2;
- if (os->name != s2->name)
- continue;
- if (!os->obsoletes)
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
- continue;
- obsp2 = os->repo->idarraydata + os->obsoletes;
- while ((obs2 = *obsp2++) != 0)
- if (obs2 == obs)
- break;
- if (obs2)
- queue_pushunique(q, op);
- }
- }
- /* find names so that we can build groups */
- queue_init_clone(&qn, q);
- prune_to_best_version(solv->pool, &qn);
-#if 0
-{
- for (i = 0; i < qn.count; i++)
- printf(" + %s\n", pool_solvid2str(pool, qn.elements[i]));
-}
-#endif
- /* filter into name groups */
- qnc = qn.count;
- if (qnc == 1)
- {
- queue_free(&qn);
- queue_empty(q);
- return;
- }
- ncnt = 0;
- for (i = 0; i < qnc; i++)
- {
- Id n = pool->solvables[qn.elements[i]].name;
- int got = 0;
- for (j = 0; j < q->count; j++)
- {
- Id p = q->elements[j];
- if (pool->solvables[p].name == n)
- {
- queue_push(&qn, p);
- got = 1;
- }
- }
- if (got)
- {
- queue_push(&qn, 0);
- ncnt++;
- }
- }
- if (ncnt <= 1)
- {
- queue_empty(q);
- }
- else
- {
- queue_empty(q);
- queue_insertn(q, 0, qn.count - qnc, qn.elements + qnc);
- }
- queue_free(&qn);
-}
-
-void
-solver_addyumobsrules(Solver *solv)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- Id p, op, *opp;
- Solvable *s;
- Queue qo, qq, yumobsinfoq;
- int i, j, k;
- unsigned int now;
-
- solv->yumobsrules = solv->nrules;
- if (!installed || !solv->obsoletes)
- {
- solv->yumobsrules_end = solv->nrules;
- return;
- }
- now = solv_timems(0);
- queue_init(&qo);
- FOR_REPO_SOLVABLES(installed, p, s)
- {
- if (!solv->obsoletes[p - installed->start])
- continue;
-#if 0
-printf("checking yumobs for %s\n", pool_solvable2str(pool, s));
-#endif
- queue_empty(&qo);
- for (opp = solv->obsoletes_data + solv->obsoletes[p - installed->start]; (op = *opp++) != 0;)
- {
- Solvable *os = pool->solvables + op;
- Id obs, *obsp = os->repo->idarraydata + os->obsoletes;
- Id p2, pp2;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(p2, pp2, obs)
- {
- Solvable *s2 = pool->solvables + p2;
- if (s2->repo != installed)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
- continue;
- queue_pushunique(&qo, obs);
- break;
- }
- }
- }
- }
- if (!qo.count)
- {
- queue_free(&qo);
- return;
- }
- queue_init(&yumobsinfoq);
- queue_init(&qq);
- for (i = 0; i < qo.count; i++)
- {
- int group, groupk, groupstart;
- queue_empty(&qq);
-#if 0
-printf("investigating %s\n", pool_dep2str(pool, qo.elements[i]));
-#endif
- find_obsolete_group(solv, qo.elements[i], &qq);
-#if 0
-printf("result:\n");
-for (j = 0; j < qq.count; j++)
- if (qq.elements[j] == 0)
- printf("---\n");
- else
- printf("%s\n", pool_solvid2str(pool, qq.elements[j]));
-#endif
-
- if (!qq.count)
- continue;
- /* at least two goups, build rules */
- group = 0;
- for (j = 0; j < qq.count; j++)
- {
- p = qq.elements[j];
- if (!p)
- {
- group++;
- continue;
- }
- if (pool->solvables[p].repo == installed)
- continue;
- groupk = 0;
- groupstart = 0;
- for (k = 0; k < qq.count; k++)
- {
- Id pk = qq.elements[k];
- if (pk)
- continue;
- if (group != groupk && k > groupstart)
- {
- /* add the rule */
- if (k - groupstart == 1)
- solver_addrule(solv, -p, qq.elements[groupstart], 0);
- else
- solver_addrule(solv, -p, 0, pool_ids2whatprovides(pool, qq.elements + groupstart, k - groupstart));
- queue_push(&yumobsinfoq, qo.elements[i]);
- }
- groupstart = k + 1;
- groupk++;
- }
- }
- }
- if (yumobsinfoq.count)
- solv->yumobsrules_info = solv_memdup2(yumobsinfoq.elements, yumobsinfoq.count, sizeof(Id));
- queue_free(&yumobsinfoq);
- queue_free(&qq);
- queue_free(&qo);
- solv->yumobsrules_end = solv->nrules;
- POOL_DEBUG(SOLV_DEBUG_STATS, "yumobs rule creation took %d ms\n", solv_timems(now));
-}
-
-#undef CLEANDEPSDEBUG
-
-/*
- * This functions collects all packages that are looked at
- * when a dependency is checked. We need it to "pin" installed
- * packages when removing a supplemented package in createcleandepsmap.
- * Here's an not uncommon example:
- * A contains "Supplements: packageand(B, C)"
- * B contains "Requires: A"
- * Now if we remove C, the supplements is no longer true,
- * thus we also remove A. Without the dep_pkgcheck function, we
- * would now also remove B, but this is wrong, as adding back
- * C doesn't make the supplements true again. Thus we "pin" B
- * when we remove A.
- * There's probably a better way to do this, but I haven't come
- * up with it yet ;)
- */
-static inline void
-dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q)
-{
- Pool *pool = solv->pool;
- Id p, pp;
-
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags >= 8)
- {
- if (rd->flags == REL_AND)
- {
- dep_pkgcheck(solv, rd->name, m, q);
- dep_pkgcheck(solv, rd->evr, m, q);
- return;
- }
- if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
- return;
- }
- }
- FOR_PROVIDES(p, pp, dep)
- if (!m || MAPTST(m, p))
- queue_push(q, p);
-}
-
-static int
-check_xsupp(Solver *solv, Queue *depq, Id dep)
-{
- Pool *pool = solv->pool;
- Id p, pp;
-
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags >= 8)
- {
- if (rd->flags == REL_AND)
- {
- if (!check_xsupp(solv, depq, rd->name))
- return 0;
- return check_xsupp(solv, depq, rd->evr);
- }
- if (rd->flags == REL_OR)
- {
- if (check_xsupp(solv, depq, rd->name))
- return 1;
- return check_xsupp(solv, depq, rd->evr);
- }
- if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
-#if 0
- return solver_splitprovides(solv, rd->evr);
-#else
- return 0;
-#endif
- }
- if (depq && rd->flags == REL_NAMESPACE)
- {
- int i;
- for (i = 0; i < depq->count; i++)
- if (depq->elements[i] == dep || depq->elements[i] == rd->name)
- return 1;
- }
- }
- FOR_PROVIDES(p, pp, dep)
- if (p == SYSTEMSOLVABLE || pool->solvables[p].repo == solv->installed)
- return 1;
- return 0;
-}
-
-static inline int
-queue_contains(Queue *q, Id id)
-{
- int i;
- for (i = 0; i < q->count; i++)
- if (q->elements[i] == id)
- return 1;
- return 0;
-}
-
-#ifdef ENABLE_COMPLEX_DEPS
-static void
-complex_cleandeps_remove(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq)
-{
- int i;
- Queue dq;
- Id p;
-
- queue_init(&dq);
- i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
- if (i == 0 || i == 1)
- {
- queue_free(&dq);
- return;
- }
- for (i = 0; i < dq.count; i++)
- {
- for (; (p = dq.elements[i]) != 0; i++)
- {
- if (p < 0)
- {
- if (!MAPTST(installedm, -p))
- break;
- continue;
- }
- if (p != SYSTEMSOLVABLE && MAPTST(im, p))
- {
-#ifdef CLEANDEPSDEBUG
- printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
- queue_push(iq, p);
- }
- }
- while (dq.elements[i])
- i++;
- }
- queue_free(&dq);
-}
-
-static void
-complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq, Map *userinstalled)
-{
- int i, blk;
- Queue dq;
- Id p;
-
- queue_init(&dq);
- i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
- if (i == 0 || i == 1)
- {
- queue_free(&dq);
- return;
- }
- for (i = 0; i < dq.count; i++)
- {
- blk = i;
- for (; (p = dq.elements[i]) != 0; i++)
- {
- if (p < 0)
- {
- if (!MAPTST(installedm, -p))
- break;
- continue;
- }
- if (MAPTST(im, p))
- break;
- }
- if (!p)
- {
- for (i = blk; (p = dq.elements[i]) != 0; i++)
- {
- if (p < 0)
- continue;
- if (!MAPTST(installedm, p))
- continue;
- if (p == ip || MAPTST(userinstalled, p - pool->installed->start))
- continue;
-#ifdef CLEANDEPSDEBUG
- printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
- MAPSET(im, p);
- queue_push(iq, p);
- }
- }
- while (dq.elements[i])
- i++;
- }
- queue_free(&dq);
-}
-
-#endif
-
-/*
- * Find all installed packages that are no longer
- * needed regarding the current solver job.
- *
- * The algorithm is:
- * - remove pass: remove all packages that could have
- * been dragged in by the obsoleted packages.
- * i.e. if package A is obsolete and contains "Requires: B",
- * also remove B, as installing A will have pulled in B.
- * after this pass, we have a set of still installed packages
- * with broken dependencies.
- * - add back pass:
- * now add back all packages that the still installed packages
- * require.
- *
- * The cleandeps packages are the packages removed in the first
- * pass and not added back in the second pass.
- *
- * If we search for unneeded packages (unneeded is true), we
- * simply remove all packages except the userinstalled ones in
- * the first pass.
- */
-static void
-solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- Queue *job = &solv->job;
- Map userinstalled;
- Map im;
- Map installedm;
- Rule *r;
- Id rid, how, what, select;
- Id p, pp, ip, jp;
- Id req, *reqp, sup, *supp;
- Solvable *s;
- Queue iq, iqcopy, xsuppq;
- int i;
-
- map_empty(cleandepsmap);
- if (!installed || installed->end == installed->start)
- return;
- map_init(&userinstalled, installed->end - installed->start);
- map_init(&im, pool->nsolvables);
- map_init(&installedm, pool->nsolvables);
- queue_init(&iq);
- queue_init(&xsuppq);
-
- for (i = 0; i < job->count; i += 2)
- {
- how = job->elements[i];
- if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
- {
- what = job->elements[i + 1];
- select = how & SOLVER_SELECTMASK;
- if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
- FOR_REPO_SOLVABLES(installed, p, s)
- MAPSET(&userinstalled, p - installed->start);
- FOR_JOB_SELECT(p, pp, select, what)
- if (pool->solvables[p].repo == installed)
- MAPSET(&userinstalled, p - installed->start);
- }
- if ((how & (SOLVER_JOBMASK | SOLVER_SELECTMASK)) == (SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES))
- {
- what = job->elements[i + 1];
- if (ISRELDEP(what))
- {
- Reldep *rd = GETRELDEP(pool, what);
- if (rd->flags != REL_NAMESPACE)
- continue;
- if (rd->evr == 0)
- {
- queue_pushunique(&iq, rd->name);
- continue;
- }
- FOR_PROVIDES(p, pp, what)
- if (p)
- break;
- if (p)
- continue;
- queue_pushunique(&iq, what);
- }
- }
- }
-
- /* have special namespace cleandeps erases */
- if (iq.count)
- {
- for (ip = installed->start; ip < installed->end; ip++)
- {
- s = pool->solvables + ip;
- if (s->repo != installed)
- continue;
- if (!s->supplements)
- continue;
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (ISRELDEP(sup) && check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup))
- {
-#ifdef CLEANDEPSDEBUG
- printf("xsupp %s from %s\n", pool_dep2str(pool, sup), pool_solvid2str(pool, ip));
-#endif
- queue_pushunique(&xsuppq, sup);
- }
- }
- queue_empty(&iq);
- }
-
- /* also add visible patterns to userinstalled for openSUSE */
- if (1)
- {
- Dataiterator di;
- dataiterator_init(&di, pool, 0, 0, SOLVABLE_ISVISIBLE, 0, 0);
- while (dataiterator_step(&di))
- {
- Id *dp;
- if (di.solvid <= 0)
- continue;
- s = pool->solvables + di.solvid;
- if (!s->repo || !s->requires)
- continue;
- if (s->repo != installed && !pool_installable(pool, s))
- continue;
- if (strncmp(pool_id2str(pool, s->name), "pattern:", 8) != 0)
- continue;
- dp = s->repo->idarraydata + s->requires;
- for (dp = s->repo->idarraydata + s->requires; *dp; dp++)
- FOR_PROVIDES(p, pp, *dp)
- if (pool->solvables[p].repo == installed)
- {
- if (strncmp(pool_id2str(pool, pool->solvables[p].name), "pattern", 7) != 0)
- continue;
- MAPSET(&userinstalled, p - installed->start);
- }
- }
- dataiterator_free(&di);
- }
- if (1)
- {
- /* all products and their buddies are userinstalled */
- for (p = installed->start; p < installed->end; p++)
- {
- Solvable *s = pool->solvables + p;
- if (s->repo != installed)
- continue;
- if (!strncmp("product:", pool_id2str(pool, s->name), 8))
- {
- MAPSET(&userinstalled, p - installed->start);
-#ifdef ENABLE_LINKED_PKGS
- if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
- {
- Id buddy = solv->instbuddy[p - installed->start];
- if (buddy >= installed->start && buddy < installed->end)
- MAPSET(&userinstalled, buddy - installed->start);
- }
-#endif
- }
- }
- }
-
- /* add all positive elements (e.g. locks) to "userinstalled" */
- for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
- {
- r = solv->rules + rid;
- if (r->d < 0)
- continue;
- i = solv->ruletojob.elements[rid - solv->jobrules];
- if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
- continue;
- FOR_RULELITERALS(p, jp, r)
- if (p > 0 && pool->solvables[p].repo == installed)
- MAPSET(&userinstalled, p - installed->start);
- }
-
- /* add all cleandeps candidates to iq */
- for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
- {
- r = solv->rules + rid;
- if (r->d < 0) /* disabled? */
- continue;
- if (r->d == 0 && r->p < 0 && r->w2 == 0) /* negative assertion (erase job)? */
- {
- p = -r->p;
- if (pool->solvables[p].repo != installed)
- continue;
- MAPCLR(&userinstalled, p - installed->start);
- if (unneeded)
- continue;
- i = solv->ruletojob.elements[rid - solv->jobrules];
- how = job->elements[i];
- if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS))
- queue_push(&iq, p);
- }
- else if (r->p > 0) /* install job */
- {
- if (unneeded)
- continue;
- i = solv->ruletojob.elements[rid - solv->jobrules];
- if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
- {
- /* check if the literals all obsolete some installed package */
- Map om;
- int iqstart;
-
- /* just one installed literal */
- if (r->d == 0 && r->w2 == 0 && pool->solvables[r->p].repo == installed)
- continue;
- /* multiversion is bad */
- if (solv->multiversion.size && !solv->keepexplicitobsoletes)
- {
- FOR_RULELITERALS(p, jp, r)
- if (MAPTST(&solv->multiversion, p))
- break;
- if (p)
- continue;
- }
-
- om.size = 0;
- iqstart = iq.count;
- FOR_RULELITERALS(p, jp, r)
- {
- if (p < 0)
- {
- queue_truncate(&iq, iqstart); /* abort */
- break;
- }
- if (pool->solvables[p].repo == installed)
- {
- if (iq.count == iqstart)
- queue_push(&iq, p);
- else
- {
- for (i = iqstart; i < iq.count; i++)
- if (iq.elements[i] == p)
- break;
- queue_truncate(&iq, iqstart);
- if (i < iq.count)
- queue_push(&iq, p);
- }
- }
- else
- intersect_obsoletes(solv, p, &iq, iqstart, &om);
- if (iq.count == iqstart)
- break;
- }
- if (om.size)
- map_free(&om);
- }
- }
- }
- queue_init_clone(&iqcopy, &iq);
-
- if (!unneeded)
- {
- if (solv->cleandeps_updatepkgs)
- for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
- queue_push(&iq, solv->cleandeps_updatepkgs->elements[i]);
- }
-
- if (unneeded)
- queue_empty(&iq); /* just in case... */
-
- /* clear userinstalled bit for the packages we really want to delete/update */
- for (i = 0; i < iq.count; i++)
- {
- p = iq.elements[i];
- if (pool->solvables[p].repo != installed)
- continue;
- MAPCLR(&userinstalled, p - installed->start);
- }
-
- for (p = installed->start; p < installed->end; p++)
- {
- if (pool->solvables[p].repo != installed)
- continue;
- MAPSET(&installedm, p);
- if (unneeded && !MAPTST(&userinstalled, p - installed->start))
- continue;
- MAPSET(&im, p);
- }
- MAPSET(&installedm, SYSTEMSOLVABLE);
- MAPSET(&im, SYSTEMSOLVABLE);
-
-#ifdef CLEANDEPSDEBUG
- printf("REMOVE PASS\n");
-#endif
-
- for (;;)
- {
- if (!iq.count)
- {
- if (unneeded)
- break;
- /* supplements pass */
- for (ip = installed->start; ip < installed->end; ip++)
- {
- if (!MAPTST(&installedm, ip))
- continue;
- s = pool->solvables + ip;
- if (!s->supplements)
- continue;
- if (!MAPTST(&im, ip))
- continue;
- if (MAPTST(&userinstalled, ip - installed->start))
- continue;
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (dep_possible(solv, sup, &im))
- break;
- if (!sup)
- {
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (dep_possible(solv, sup, &installedm) || (xsuppq.count && queue_contains(&xsuppq, sup)))
- {
- /* no longer supplemented, also erase */
- int iqcount = iq.count;
- /* pin packages, see comment above dep_pkgcheck */
- dep_pkgcheck(solv, sup, &im, &iq);
- for (i = iqcount; i < iq.count; i++)
- {
- Id pqp = iq.elements[i];
- if (pool->solvables[pqp].repo == installed)
- MAPSET(&userinstalled, pqp - installed->start);
- }
- queue_truncate(&iq, iqcount);
-#ifdef CLEANDEPSDEBUG
- printf("%s supplemented [%s]\n", pool_solvid2str(pool, ip), pool_dep2str(pool, sup));
-#endif
- queue_push(&iq, ip);
- }
- }
- }
- if (!iq.count)
- break; /* no supplementing package found, we're done */
- }
- ip = queue_shift(&iq);
- s = pool->solvables + ip;
- if (!MAPTST(&im, ip))
- continue;
- if (!MAPTST(&installedm, ip))
- continue;
- if (s->repo == installed && MAPTST(&userinstalled, ip - installed->start))
- continue;
- MAPCLR(&im, ip);
-#ifdef CLEANDEPSDEBUG
- printf("removing %s\n", pool_solvable2str(pool, s));
-#endif
- if (s->requires)
- {
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- continue;
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, req))
- {
- complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, req)
- {
- if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
- {
-#ifdef CLEANDEPSDEBUG
- printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
- queue_push(&iq, p);
- }
- }
- }
- }
- if (s->recommends)
- {
- reqp = s->repo->idarraydata + s->recommends;
- while ((req = *reqp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, req))
- {
- complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, req)
- {
- if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
- {
-#ifdef CLEANDEPSDEBUG
- printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
- queue_push(&iq, p);
- }
- }
- }
- }
- }
-
- /* turn userinstalled into remove set for pruning */
- map_empty(&userinstalled);
- for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
- {
- r = solv->rules + rid;
- if (r->p >= 0 || r->d != 0 || r->w2 != 0)
- continue; /* disabled or not erase */
- p = -r->p;
- MAPCLR(&im, p);
- if (pool->solvables[p].repo == installed)
- MAPSET(&userinstalled, p - installed->start);
- }
- if (!unneeded && solv->cleandeps_updatepkgs)
- {
- for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
- {
- p = solv->cleandeps_updatepkgs->elements[i];
- if (pool->solvables[p].repo == installed)
- MAPSET(&userinstalled, p - installed->start);
- }
- }
- MAPSET(&im, SYSTEMSOLVABLE); /* in case we cleared it above */
- for (p = installed->start; p < installed->end; p++)
- if (MAPTST(&im, p))
- queue_push(&iq, p);
- for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
- {
- r = solv->rules + rid;
- if (r->d < 0)
- continue;
- FOR_RULELITERALS(p, jp, r)
- if (p > 0)
- queue_push(&iq, p);
- }
- /* also put directly addressed packages on the install queue
- * so we can mark patterns as installed */
- for (i = 0; i < job->count; i += 2)
- {
- how = job->elements[i];
- if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
- {
- what = job->elements[i + 1];
- select = how & SOLVER_SELECTMASK;
- if (select == SOLVER_SOLVABLE && pool->solvables[what].repo != installed)
- queue_push(&iq, what);
- }
- }
-
-#ifdef CLEANDEPSDEBUG
- printf("ADDBACK PASS\n");
-#endif
- for (;;)
- {
- if (!iq.count)
- {
- /* supplements pass */
- for (ip = installed->start; ip < installed->end; ip++)
- {
- if (!MAPTST(&installedm, ip))
- continue;
- if (MAPTST(&userinstalled, ip - installed->start))
- continue;
- s = pool->solvables + ip;
- if (!s->supplements)
- continue;
- if (MAPTST(&im, ip))
- continue;
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (dep_possible(solv, sup, &im))
- break;
- if (sup)
- {
-#ifdef CLEANDEPSDEBUG
- printf("%s supplemented\n", pool_solvid2str(pool, ip));
-#endif
- MAPSET(&im, ip);
- queue_push(&iq, ip);
- }
- }
- if (!iq.count)
- break;
- }
- ip = queue_shift(&iq);
- s = pool->solvables + ip;
-#ifdef CLEANDEPSDEBUG
- printf("adding back %s\n", pool_solvable2str(pool, s));
-#endif
- if (s->requires)
- {
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, req))
- {
- complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, req)
- if (MAPTST(&im, p))
- break;
- if (p)
- continue;
- FOR_PROVIDES(p, pp, req)
- {
- if (MAPTST(&installedm, p))
- {
- if (p == ip)
- continue;
- if (MAPTST(&userinstalled, p - installed->start))
- continue;
-#ifdef CLEANDEPSDEBUG
- printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
- MAPSET(&im, p);
- queue_push(&iq, p);
- }
- }
- }
- }
- if (s->recommends)
- {
- reqp = s->repo->idarraydata + s->recommends;
- while ((req = *reqp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, req))
- {
- complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, req)
- if (MAPTST(&im, p))
- break;
- if (p)
- continue;
- FOR_PROVIDES(p, pp, req)
- {
- if (MAPTST(&installedm, p))
- {
- if (p == ip)
- continue;
- if (MAPTST(&userinstalled, p - installed->start))
- continue;
-#ifdef CLEANDEPSDEBUG
- printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
- MAPSET(&im, p);
- queue_push(&iq, p);
- }
- }
- }
- }
- }
-
- queue_free(&iq);
- /* make sure the updatepkgs and mistakes are not in the cleandeps map */
- if (solv->cleandeps_updatepkgs)
- for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
- MAPSET(&im, solv->cleandeps_updatepkgs->elements[i]);
- if (solv->cleandeps_mistakes)
- for (i = 0; i < solv->cleandeps_mistakes->count; i++)
- MAPSET(&im, solv->cleandeps_mistakes->elements[i]);
- /* also remove original iq packages */
- for (i = 0; i < iqcopy.count; i++)
- MAPSET(&im, iqcopy.elements[i]);
- queue_free(&iqcopy);
- for (p = installed->start; p < installed->end; p++)
- {
- if (pool->solvables[p].repo != installed)
- continue;
- if (!MAPTST(&im, p))
- MAPSET(cleandepsmap, p - installed->start);
- }
- map_free(&im);
- map_free(&installedm);
- map_free(&userinstalled);
- queue_free(&xsuppq);
-#ifdef CLEANDEPSDEBUG
- printf("=== final cleandeps map:\n");
- for (p = installed->start; p < installed->end; p++)
- if (MAPTST(cleandepsmap, p - installed->start))
- printf(" - %s\n", pool_solvid2str(pool, p));
-#endif
-}
-
-
-struct trj_data {
- Queue *edges;
- Id *low;
- Id idx;
- Id nstack;
- Id firstidx;
-};
-
-/* Tarjan's SCC algorithm, slightly modifed */
-static void
-trj_visit(struct trj_data *trj, Id node)
-{
- Id *low = trj->low;
- Queue *edges = trj->edges;
- Id nnode, myidx, stackstart;
- int i;
-
- low[node] = myidx = trj->idx++;
- low[(stackstart = trj->nstack++)] = node;
- for (i = edges->elements[node]; (nnode = edges->elements[i]) != 0; i++)
- {
- Id l = low[nnode];
- if (!l)
- {
- if (!edges->elements[edges->elements[nnode]])
- {
- trj->idx++;
- low[nnode] = -1;
- continue;
- }
- trj_visit(trj, nnode);
- l = low[nnode];
- }
- if (l < 0)
- continue;
- if (l < trj->firstidx)
- {
- int k;
- for (k = l; low[low[k]] == l; k++)
- low[low[k]] = -1;
- }
- else if (l < low[node])
- low[node] = l;
- }
- if (low[node] == myidx)
- {
- if (myidx != trj->firstidx)
- myidx = -1;
- for (i = stackstart; i < trj->nstack; i++)
- low[low[i]] = myidx;
- trj->nstack = stackstart;
- }
-}
-
-#ifdef ENABLE_COMPLEX_DEPS
-static void
-complex_unneeded(Pool *pool, Id ip, Id req, Queue *edges, Map *cleandepsmap, Queue *unneededq)
-{
- int i, j;
- Queue dq;
- Id p;
-
- queue_init(&dq);
- i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
- if (i == 0 || i == 1)
- {
- queue_free(&dq);
- return;
- }
- for (i = 0; i < dq.count; i++)
- {
- for (; (p = dq.elements[i]) != 0; i++)
- {
- if (p < 0)
- {
- if (pool->solvables[-p].repo != pool->installed)
- break;
- continue;
- }
- if (p == ip || pool->solvables[p].repo != pool->installed || !MAPTST(cleandepsmap, p - pool->installed->start))
- continue;
- for (j = 0; j < unneededq->count; j++)
- if (p == unneededq->elements[j])
- {
- if (edges->elements[edges->count - 1] != j + 1)
- queue_push(edges, j + 1);
- break;
- }
- }
- while (dq.elements[i])
- i++;
- }
- queue_free(&dq);
-}
-#endif
-
-void
-solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
-{
- Repo *installed = solv->installed;
- int i;
- Map cleandepsmap;
-
- queue_empty(unneededq);
- if (!installed || installed->end == installed->start)
- return;
-
- map_init(&cleandepsmap, installed->end - installed->start);
- solver_createcleandepsmap(solv, &cleandepsmap, 1);
- for (i = installed->start; i < installed->end; i++)
- if (MAPTST(&cleandepsmap, i - installed->start))
- queue_push(unneededq, i);
-
- if (filtered && unneededq->count > 1)
- {
- Pool *pool = solv->pool;
- Queue edges;
- Id *nrequires;
- Map installedm;
- int j, pass, count = unneededq->count;
- Id *low;
-
- map_init(&installedm, pool->nsolvables);
- for (i = installed->start; i < installed->end; i++)
- if (pool->solvables[i].repo == installed)
- MAPSET(&installedm, i);
-
- nrequires = solv_calloc(count, sizeof(Id));
- queue_init(&edges);
- queue_prealloc(&edges, count * 4 + 10); /* pre-size */
-
- /*
- * Go through the solvables in the nodes queue and create edges for
- * all requires/recommends/supplements between the nodes.
- * The edges are stored in the edges queue, we add 1 to the node
- * index so that nodes in the edges queue are != 0 and we can
- * terminate the edge list with 0.
- * Thus for node element 5, the edges are stored starting at
- * edges.elements[6] and are 0-terminated.
- */
- /* leave first element zero to make things easier */
- /* also add trailing zero */
- queue_insertn(&edges, 0, 1 + count + 1, 0);
-
- /* first requires and recommends */
- for (i = 0; i < count; i++)
- {
- Solvable *s = pool->solvables + unneededq->elements[i];
- int oldcount = edges.count;
- edges.elements[i + 1] = oldcount;
- for (pass = 0; pass < 2; pass++)
- {
- unsigned int off = pass == 0 ? s->requires : s->recommends;
- Id p, pp, dep, *dp;
- if (off)
- for (dp = s->repo->idarraydata + off; (dep = *dp) != 0; dp++)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, dep))
- {
- complex_unneeded(pool, s - pool->solvables, dep, &edges, &cleandepsmap, unneededq);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, dep)
- {
- Solvable *sp = pool->solvables + p;
- if (s == sp || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
- continue;
- for (j = 0; j < count; j++)
- if (p == unneededq->elements[j])
- {
- if (edges.elements[edges.count - 1] != j + 1)
- queue_push(&edges, j + 1);
- }
- }
- }
- if (pass == 0)
- nrequires[i] = edges.count - oldcount;
- }
- queue_push(&edges, 0);
- }
-#if 0
- printf("requires + recommends\n");
- for (i = 0; i < count; i++)
- {
- int j;
- printf(" %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
- for (j = edges.elements[i + 1]; edges.elements[j]; j++)
- printf(" - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
- }
-#endif
-
- /* then add supplements */
- for (i = 0; i < count; i++)
- {
- Solvable *s = pool->solvables + unneededq->elements[i];
- if (s->supplements)
- {
- Id *dp;
- int k;
- for (dp = s->repo->idarraydata + s->supplements; *dp; dp++)
- if (dep_possible(solv, *dp, &installedm))
- {
- Queue iq;
- Id iqbuf[16];
- queue_init_buffer(&iq, iqbuf, sizeof(iqbuf)/sizeof(*iqbuf));
- dep_pkgcheck(solv, *dp, 0, &iq);
- for (k = 0; k < iq.count; k++)
- {
- Id p = iq.elements[k];
- Solvable *sp = pool->solvables + p;
- if (p == unneededq->elements[i] || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
- continue;
- for (j = 0; j < count; j++)
- if (p == unneededq->elements[j])
- break;
- /* now add edge from j + 1 to i + 1 */
- queue_insert(&edges, edges.elements[j + 1] + nrequires[j], i + 1);
- /* addapt following edge pointers */
- for (j = j + 2; j < count + 1; j++)
- edges.elements[j]++;
- }
- queue_free(&iq);
- }
- }
- }
-#if 0
- /* print result */
- printf("+ supplements\n");
- for (i = 0; i < count; i++)
- {
- int j;
- printf(" %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
- for (j = edges.elements[i + 1]; edges.elements[j]; j++)
- printf(" - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
- }
-#endif
- map_free(&installedm);
-
- /* now run SCC algo two times, first with requires+recommends+supplements,
- * then again without the requires. We run it the second time to get rid
- * of packages that got dragged in via recommends/supplements */
- /*
- * low will contain the result of the SCC search.
- * it must be of at least size 2 * (count + 1) and
- * must be zero initialized.
- * The layout is:
- * 0 low low ... low stack stack ...stack 0
- * count count
- */
- low = solv_calloc(count + 1, 2 * sizeof(Id));
- for (pass = 0; pass < 2; pass++)
- {
- struct trj_data trj;
- if (pass)
- {
- memset(low, 0, (count + 1) * (2 * sizeof(Id)));
- for (i = 0; i < count; i++)
- {
- edges.elements[i + 1] += nrequires[i];
- if (!unneededq->elements[i])
- low[i + 1] = -1; /* ignore this node */
- }
- }
- trj.edges = &edges;
- trj.low = low;
- trj.idx = count + 1; /* stack starts here */
- for (i = 1; i <= count; i++)
- {
- if (low[i])
- continue;
- if (edges.elements[edges.elements[i]])
- {
- trj.firstidx = trj.nstack = trj.idx;
- trj_visit(&trj, i);
- }
- else
- {
- Id myidx = trj.idx++;
- low[i] = myidx;
- low[myidx] = i;
- }
- }
- /* prune packages */
- for (i = 0; i < count; i++)
- if (low[i + 1] <= 0)
- unneededq->elements[i] = 0;
- }
- solv_free(low);
- solv_free(nrequires);
- queue_free(&edges);
-
- /* finally remove all pruned entries from unneededq */
- for (i = j = 0; i < count; i++)
- if (unneededq->elements[i])
- unneededq->elements[j++] = unneededq->elements[i];
- queue_truncate(unneededq, j);
- }
- map_free(&cleandepsmap);
-}
-
-
-void
-solver_breakorphans(Solver *solv)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- int i, rid;
- Map m;
-
- if (!installed || solv->droporphanedmap_all)
- return;
- solv->brokenorphanrules = solv_calloc(1, sizeof(Queue));
- queue_init(solv->brokenorphanrules);
- map_init(&m, installed->end - installed->start);
- for (i = 0; i < solv->orphaned.count; i++)
- {
- Id p = solv->orphaned.elements[i];
- if (pool->solvables[p].repo != installed)
- continue;
- if (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - installed->start))
- continue;
- MAPSET(&m, p - installed->start);
- }
- for (rid = 1; rid < solv->pkgrules_end ; rid++)
- {
- Id p, *dp;
- Rule *r = solv->rules + rid;
- /* ignore non-deps and simple conflicts */
- if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
- continue;
- p = -r->p;
- if (p < installed->start || p >= installed->end || !MAPTST(&m, p - installed->start))
- {
- /* need to check other literals */
- if (r->d == 0 || r->d == -1)
- continue;
- for (dp = pool->whatprovidesdata + (r->d < 0 ? -r->d - 1 : r->d); *dp < 0; dp++)
- {
- p = -*dp;
- if (p >= installed->start && p < installed->end && MAPTST(&m, p - installed->start))
- break;
- }
- if (*dp >= 0)
- continue;
- }
- /* ok, disable this rule */
- queue_push(solv->brokenorphanrules, rid);
- if (r->d >= 0)
- solver_disablerule(solv, r);
- }
- map_free(&m);
- if (!solv->brokenorphanrules->count)
- {
- queue_free(solv->brokenorphanrules);
- solv->brokenorphanrules = solv_free(solv->brokenorphanrules);
- }
-}
-
-void
-solver_check_brokenorphanrules(Solver *solv, Queue *dq)
-{
- Pool *pool = solv->pool;
- int i;
- Id l, pp;
-
- queue_empty(dq);
- if (!solv->brokenorphanrules)
- return;
- for (i = 0; i < solv->brokenorphanrules->count; i++)
- {
- int rid = solv->brokenorphanrules->elements[i];
- Rule *r = solv->rules + rid;
- FOR_RULELITERALS(l, pp, r)
- {
- if (l < 0)
- {
- if (solv->decisionmap[-l] <= 0)
- break;
- }
- else
- {
- if (solv->decisionmap[l] > 0 && pool->solvables[l].repo != solv->installed)
- break;
- }
- }
- if (l)
- continue;
- FOR_RULELITERALS(l, pp, r)
- if (l > 0 && solv->decisionmap[l] == 0 && pool->solvables[l].repo != solv->installed)
- queue_pushunique(dq, l);
- }
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * rules.h
- *
- */
-
-#ifndef LIBSOLV_RULES_H
-#define LIBSOLV_RULES_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* ----------------------------------------------
- * 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
- *
- * There are a lot of rules, so the struct is kept as small as
- * possible. Do not add new members unless there is no other way.
- */
-
-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 */
- /* in case of disabled rules: ~d, aka -d - 1 */
- Id w1, w2; /* watches, literals not-yet-decided */
- /* if !w2, assertion, not rule */
- Id n1, n2; /* next rules in linked list, corresponding to w1, w2 */
-} Rule;
-
-
-typedef enum {
- SOLVER_RULE_UNKNOWN = 0,
- SOLVER_RULE_PKG = 0x100,
- SOLVER_RULE_PKG_NOT_INSTALLABLE,
- SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP,
- SOLVER_RULE_PKG_REQUIRES,
- SOLVER_RULE_PKG_SELF_CONFLICT,
- SOLVER_RULE_PKG_CONFLICTS,
- SOLVER_RULE_PKG_SAME_NAME,
- SOLVER_RULE_PKG_OBSOLETES,
- SOLVER_RULE_PKG_IMPLICIT_OBSOLETES,
- SOLVER_RULE_PKG_INSTALLED_OBSOLETES,
- SOLVER_RULE_UPDATE = 0x200,
- SOLVER_RULE_FEATURE = 0x300,
- SOLVER_RULE_JOB = 0x400,
- SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP,
- SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM,
- SOLVER_RULE_JOB_UNKNOWN_PACKAGE,
- SOLVER_RULE_JOB_UNSUPPORTED,
- SOLVER_RULE_DISTUPGRADE = 0x500,
- SOLVER_RULE_INFARCH = 0x600,
- SOLVER_RULE_CHOICE = 0x700,
- SOLVER_RULE_LEARNT = 0x800,
- SOLVER_RULE_BEST = 0x900,
- SOLVER_RULE_YUMOBS = 0xa00
-} SolverRuleinfo;
-
-#define SOLVER_RULE_TYPEMASK 0xff00
-
-struct _Solver;
-
-/*-------------------------------------------------------------------
- * disable rule
- */
-
-static inline void
-solver_disablerule(struct _Solver *solv, Rule *r)
-{
- if (r->d >= 0)
- r->d = -r->d - 1;
-}
-
-/*-------------------------------------------------------------------
- * enable rule
- */
-
-static inline void
-solver_enablerule(struct _Solver *solv, Rule *r)
-{
- if (r->d < 0)
- r->d = -r->d - 1;
-}
-
-extern Rule *solver_addrule(struct _Solver *solv, Id p, Id p2, Id d);
-extern void solver_unifyrules(struct _Solver *solv);
-extern int solver_rulecmp(struct _Solver *solv, Rule *r1, Rule *r2);
-extern void solver_shrinkrules(struct _Solver *solv, int nrules);
-
-/* pkg rules */
-extern void solver_addpkgrulesforsolvable(struct _Solver *solv, Solvable *s, Map *m);
-extern void solver_addpkgrulesforweak(struct _Solver *solv, Map *m);
-extern void solver_addpkgrulesforlinked(struct _Solver *solv, Map *m);
-extern void solver_addpkgrulesforupdaters(struct _Solver *solv, Solvable *s, Map *m, int allow_all);
-
-/* update/feature rules */
-extern void solver_addupdaterule(struct _Solver *solv, Solvable *s, int allow_all);
-
-/* infarch rules */
-extern void solver_addinfarchrules(struct _Solver *solv, Map *addedmap);
-
-/* dup rules */
-extern void solver_createdupmaps(struct _Solver *solv);
-extern void solver_freedupmaps(struct _Solver *solv);
-extern void solver_addduprules(struct _Solver *solv, Map *addedmap);
-
-/* choice rules */
-extern void solver_addchoicerules(struct _Solver *solv);
-extern void solver_disablechoicerules(struct _Solver *solv, Rule *r);
-
-/* best rules */
-extern void solver_addbestrules(struct _Solver *solv, int havebestinstalljobs);
-
-/* yumobs rules */
-extern void solver_addyumobsrules(struct _Solver *solv);
-
-/* policy rule disabling/reenabling */
-extern void solver_disablepolicyrules(struct _Solver *solv);
-extern void solver_reenablepolicyrules(struct _Solver *solv, int jobidx);
-extern void solver_reenablepolicyrules_cleandeps(struct _Solver *solv, Id pkg);
-
-/* rule info */
-extern int solver_allruleinfos(struct _Solver *solv, Id rid, Queue *rq);
-extern SolverRuleinfo solver_ruleinfo(struct _Solver *solv, Id rid, Id *fromp, Id *top, Id *depp);
-extern SolverRuleinfo solver_ruleclass(struct _Solver *solv, Id rid);
-extern void solver_ruleliterals(struct _Solver *solv, Id rid, Queue *q);
-extern int solver_rule2jobidx(struct _Solver *solv, Id rid);
-extern Id solver_rule2job(struct _Solver *solv, Id rid, Id *whatp);
-extern Id solver_rule2solvable(struct _Solver *solv, Id rid);
-extern void solver_rule2rules(struct _Solver *solv, Id rid, Queue *q, int recursive);
-extern Id solver_rule2pkgrule(struct _Solver *solv, Id rid);
-
-/* orphan handling */
-extern void solver_breakorphans(struct _Solver *solv);
-extern void solver_check_brokenorphanrules(struct _Solver *solv, Queue *dq);
-
-
-/* legacy */
-#define SOLVER_RULE_RPM SOLVER_RULE_PKG
-#define SOLVER_RULE_RPM_NOT_INSTALLABLE SOLVER_RULE_PKG_NOT_INSTALLABLE
-#define SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP
-#define SOLVER_RULE_RPM_PACKAGE_REQUIRES SOLVER_RULE_PKG_REQUIRES
-#define SOLVER_RULE_RPM_SELF_CONFLICT SOLVER_RULE_PKG_SELF_CONFLICT
-#define SOLVER_RULE_RPM_PACKAGE_CONFLICT SOLVER_RULE_PKG_CONFLICTS
-#define SOLVER_RULE_RPM_SAME_NAME SOLVER_RULE_PKG_SAME_NAME
-#define SOLVER_RULE_RPM_PACKAGE_OBSOLETES SOLVER_RULE_PKG_OBSOLETES
-#define SOLVER_RULE_RPM_IMPLICIT_OBSOLETES SOLVER_RULE_PKG_IMPLICIT_OBSOLETES
-#define SOLVER_RULE_RPM_INSTALLEDPKG_OBSOLETES SOLVER_RULE_PKG_INSTALLED_OBSOLETES
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * selection.c
- *
- */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <fnmatch.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "selection.h"
-#include "solver.h"
-#include "evr.h"
-
-
-static int
-str2archid(Pool *pool, const char *arch)
-{
- Id id;
- if (!*arch)
- return 0;
- id = pool_str2id(pool, arch, 0);
- if (!id || id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
- return id;
- if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
- return 0;
- return id;
-}
-
-static void
-selection_prune(Pool *pool, Queue *selection)
-{
- int i, j;
- Id p, pp;
- for (i = j = 0; i < selection->count; i += 2)
- {
- Id select = selection->elements[i] & SOLVER_SELECTMASK;
- p = 0;
- if (select == SOLVER_SOLVABLE_ALL)
- p = 1;
- else if (select == SOLVER_SOLVABLE_REPO)
- {
- Solvable *s;
- Repo *repo = pool_id2repo(pool, selection->elements[i + 1]);
- if (repo)
- FOR_REPO_SOLVABLES(repo, p, s)
- break;
- }
- else
- {
- FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1])
- break;
- }
- if (!p)
- continue;
- selection->elements[j] = selection->elements[i];
- selection->elements[j + 1] = selection->elements[i + 1];
- j += 2;
- }
- queue_truncate(selection, j);
-}
-
-
-static int
-selection_solvables_sortcmp(const void *ap, const void *bp, void *dp)
-{
- return *(const Id *)ap - *(const Id *)bp;
-}
-
-void
-selection_solvables(Pool *pool, Queue *selection, Queue *pkgs)
-{
- int i, j;
- Id p, pp, lastid;
- queue_empty(pkgs);
- for (i = 0; i < selection->count; i += 2)
- {
- Id select = selection->elements[i] & SOLVER_SELECTMASK;
- if (select == SOLVER_SOLVABLE_ALL)
- {
- FOR_POOL_SOLVABLES(p)
- queue_push(pkgs, p);
- }
- if (select == SOLVER_SOLVABLE_REPO)
- {
- Solvable *s;
- Repo *repo = pool_id2repo(pool, selection->elements[i + 1]);
- if (repo)
- FOR_REPO_SOLVABLES(repo, p, s)
- queue_push(pkgs, p);
- }
- else
- {
- FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1])
- queue_push(pkgs, p);
- }
- }
- if (pkgs->count < 2)
- return;
- /* sort and unify */
- solv_sort(pkgs->elements, pkgs->count, sizeof(Id), selection_solvables_sortcmp, NULL);
- lastid = pkgs->elements[0];
- for (i = j = 1; i < pkgs->count; i++)
- if (pkgs->elements[i] != lastid)
- pkgs->elements[j++] = lastid = pkgs->elements[i];
- queue_truncate(pkgs, j);
-}
-
-static void
-selection_flatten(Pool *pool, Queue *selection)
-{
- Queue q;
- int i;
- if (selection->count <= 2)
- return;
- for (i = 0; i < selection->count; i += 2)
- if ((selection->elements[i] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
- {
- selection->elements[0] = selection->elements[i];
- selection->elements[1] = selection->elements[i + 1];
- queue_truncate(selection, 2);
- return;
- }
- queue_init(&q);
- selection_solvables(pool, selection, &q);
- if (!q.count)
- {
- queue_empty(selection);
- return;
- }
- queue_truncate(selection, 2);
- if (q.count > 1)
- {
- selection->elements[0] = SOLVER_SOLVABLE_ONE_OF;
- selection->elements[1] = pool_queuetowhatprovides(pool, &q);
- }
- else
- {
- selection->elements[0] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
- selection->elements[1] = q.elements[0];
- }
-}
-
-static void
-selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
-{
- int i;
-
- for (i = 0; i < selection->count; i += 2)
- {
- Id select = selection->elements[i] & SOLVER_SELECTMASK;
- Id id = selection->elements[i + 1];
- if (select == SOLVER_SOLVABLE || select == SOLVER_SOLVABLE_ONE_OF)
- {
- /* done by selection_addsrc, currently implies SELECTION_NAME */
- Queue q;
- Id p, pp;
- Id rel = 0, relname = 0;
- int miss = 0;
-
- queue_init(&q);
- FOR_JOB_SELECT(p, pp, select, id)
- {
- Solvable *s = pool->solvables + p;
- if (!rel || s->name != relname)
- {
- relname = s->name;
- rel = pool_rel2id(pool, relname, relevr, relflags, 1);
- }
- if (pool_match_nevr(pool, s, rel))
- queue_push(&q, p);
- else
- miss = 1;
- }
- if (miss)
- {
- if (q.count == 1)
- {
- selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
- selection->elements[i + 1] = q.elements[0];
- }
- else
- {
- selection->elements[i] = SOLVER_SOLVABLE_ONE_OF;
- selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q);
- }
- }
- queue_free(&q);
- }
- else if (select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES)
- {
- /* don't stack src reldeps */
- if (relflags == REL_ARCH && (relevr == ARCH_SRC || relevr == ARCH_NOSRC) && ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- if (rd->flags == REL_ARCH && rd->evr == ARCH_SRC)
- id = rd->name;
- }
- selection->elements[i + 1] = pool_rel2id(pool, id, relevr, relflags, 1);
- }
- else
- continue; /* actually internal error */
- if (relflags == REL_ARCH)
- selection->elements[i] |= SOLVER_SETARCH;
- if (relflags == REL_EQ && select != SOLVER_SOLVABLE_PROVIDES)
- {
- if (pool->disttype == DISTTYPE_DEB)
- selection->elements[i] |= SOLVER_SETEVR; /* debian can't match version only like rpm */
- else
- {
- const char *rel = strrchr(pool_id2str(pool, relevr), '-');
- selection->elements[i] |= rel ? SOLVER_SETEVR : SOLVER_SETEV;
- }
- }
- }
- selection_prune(pool, selection);
-}
-
-static void
-selection_filter_installed(Pool *pool, Queue *selection)
-{
- Queue q;
- int i, j;
-
- if (!pool->installed)
- queue_empty(selection);
- queue_init(&q);
- for (i = j = 0; i < selection->count; i += 2)
- {
- Id select = selection->elements[i] & SOLVER_SELECTMASK;
- Id id = selection->elements[i + 1];
- if (select == SOLVER_SOLVABLE_ALL)
- {
- select = SOLVER_SOLVABLE_REPO;
- id = pool->installed->repoid;
- }
- else if (select == SOLVER_SOLVABLE_REPO)
- {
- if (id != pool->installed->repoid)
- select = 0;
- }
- else
- {
- int bad = 0;
- Id p, pp;
- queue_empty(&q);
- FOR_JOB_SELECT(p, pp, select, id)
- {
- if (pool->solvables[p].repo != pool->installed)
- bad = 1;
- else
- queue_push(&q, p);
- }
- if (bad || !q.count)
- {
- if (!q.count)
- select = 0;
- else if (q.count == 1)
- {
- select = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
- id = q.elements[0];
- }
- else
- {
- select = SOLVER_SOLVABLE_ONE_OF;
- id = pool_queuetowhatprovides(pool, &q);
- }
- }
- }
- if (select)
- {
- selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SETREPO;
- selection->elements[j++] = id;
- }
- }
- queue_truncate(selection, j);
- queue_free(&q);
-}
-
-static void
-selection_addsrc(Pool *pool, Queue *selection, int flags)
-{
- Queue q;
- Id p, name;
- int i, havesrc;
-
- if ((flags & SELECTION_INSTALLED_ONLY) != 0)
- return; /* sources can't be installed */
- queue_init(&q);
- for (i = 0; i < selection->count; i += 2)
- {
- if (selection->elements[i] != SOLVER_SOLVABLE_NAME)
- continue;
- name = selection->elements[i + 1];
- havesrc = 0;
- queue_empty(&q);
- FOR_POOL_SOLVABLES(p)
- {
- Solvable *s = pool->solvables + p;
- if (s->name != name)
- continue;
- if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
- {
- if (pool_disabled_solvable(pool, s))
- continue;
- havesrc = 1;
- }
- else if (s->repo != pool->installed && !pool_installable(pool, s))
- continue;
- queue_push(&q, p);
- }
- if (!havesrc || !q.count)
- continue;
- if (q.count == 1)
- {
- selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
- selection->elements[i + 1] = q.elements[0];
- }
- else
- {
- selection->elements[i] = SOLVER_SOLVABLE_ONE_OF;
- selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q);
- }
- }
- queue_free(&q);
-}
-
-static inline const char *
-skipkind(const char *n)
-{
- const char *s;
- for (s = n; *s >= 'a' && *s <= 'z'; s++)
- ;
- if (*s == ':' && s != n)
- return s + 1;
- return n;
-}
-
-static inline void
-queue_pushunique2(Queue *q, Id id1, Id id2)
-{
- int i;
- for (i = 0; i < q->count; i += 2)
- if (q->elements[i] == id1 && q->elements[i + 1] == id2)
- return;
- queue_push2(q, id1, id2);
-}
-
-static int
-selection_depglob_id(Pool *pool, Queue *selection, Id id, int flags)
-{
- Id p, pp;
- int match = 0;
-
- FOR_PROVIDES(p, pp, id)
- {
- Solvable *s = pool->solvables + p;
- if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
- continue;
- match = 1;
- if (s->name == id && (flags & SELECTION_NAME) != 0)
- {
- if ((flags & SELECTION_SOURCE_ONLY) != 0)
- id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
- queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
- if ((flags & SELECTION_WITH_SOURCE) != 0)
- selection_addsrc(pool, selection, flags);
- return SELECTION_NAME;
- }
- }
- if ((flags & (SELECTION_SOURCE_ONLY | SELECTION_WITH_SOURCE)) != 0 && (flags & SELECTION_NAME) != 0)
- {
- /* src rpms don't have provides, so we must check every solvable */
- FOR_POOL_SOLVABLES(p) /* slow path */
- {
- Solvable *s = pool->solvables + p;
- if (s->name == id && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC))
- {
- if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
- continue; /* just in case... src rpms can't be installed */
- if (pool_disabled_solvable(pool, s))
- continue;
- if ((flags & SELECTION_SOURCE_ONLY) != 0)
- id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
- queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
- if ((flags & SELECTION_WITH_SOURCE) != 0)
- selection_addsrc(pool, selection, flags);
- return SELECTION_NAME;
- }
- }
- }
- if (match && (flags & SELECTION_PROVIDES) != 0)
- {
- queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
- return SELECTION_PROVIDES;
- }
- return 0;
-}
-
-static int
-selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
-{
- Id id, p, pp;
- int match = 0;
- int doglob = 0;
- int nocase = 0;
- int globflags = 0;
-
- if ((flags & SELECTION_SOURCE_ONLY) != 0)
- {
- flags &= ~SELECTION_PROVIDES; /* sources don't provide anything */
- flags &= ~SELECTION_WITH_SOURCE;
- }
-
- if (!(flags & (SELECTION_NAME|SELECTION_PROVIDES)))
- return 0;
-
- if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed)
- return 0;
-
- nocase = flags & SELECTION_NOCASE;
- if (!nocase && !(flags & SELECTION_SKIP_KIND))
- {
- id = pool_str2id(pool, name, 0);
- if (id)
- {
- /* the id is know, do the fast id matching using the whatprovides lookup */
- int ret = selection_depglob_id(pool, selection, id, flags);
- if (ret)
- return ret;
- }
- }
-
- if ((flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0)
- doglob = 1;
-
- if (!nocase && !(flags & SELECTION_SKIP_KIND) && !doglob)
- return 0; /* all done above in depglob_id */
-
- if (doglob && nocase)
- globflags = FNM_CASEFOLD;
-
- if ((flags & SELECTION_NAME) != 0)
- {
- /* looks like a name glob. hard work. */
- FOR_POOL_SOLVABLES(p)
- {
- Solvable *s = pool->solvables + p;
- const char *n;
- if (s->repo != pool->installed && !pool_installable(pool, s))
- {
- if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
- continue;
- if (pool_disabled_solvable(pool, s))
- continue;
- }
- if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
- continue;
- id = s->name;
- n = pool_id2str(pool, id);
- if (flags & SELECTION_SKIP_KIND)
- n = skipkind(n);
- if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
- {
- if ((flags & SELECTION_SOURCE_ONLY) != 0)
- id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
- queue_pushunique2(selection, SOLVER_SOLVABLE_NAME, id);
- match = 1;
- }
- }
- if (match)
- {
- if ((flags & SELECTION_WITH_SOURCE) != 0)
- selection_addsrc(pool, selection, flags);
- return SELECTION_NAME;
- }
- }
-
- if ((flags & SELECTION_PROVIDES))
- {
- /* looks like a dep glob. really hard work. */
- for (id = 1; id < pool->ss.nstrings; id++)
- {
- const char *n;
- if (!pool->whatprovides[id] || pool->whatprovides[id] == 1)
- continue;
- n = pool_id2str(pool, id);
- if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
- {
- if ((flags & SELECTION_INSTALLED_ONLY) != 0)
- {
- FOR_PROVIDES(p, pp, id)
- if (pool->solvables[p].repo == pool->installed)
- break;
- if (!p)
- continue;
- }
- queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
- match = 1;
- }
- }
- if (match)
- return SELECTION_PROVIDES;
- }
- return 0;
-}
-
-static int
-selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags)
-{
- int ret;
- const char *r;
- Id archid;
-
- if ((ret = selection_depglob(pool, selection, name, flags)) != 0)
- return ret;
- if (!(flags & SELECTION_DOTARCH))
- return 0;
- /* check if there is an .arch suffix */
- if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
- {
- char *rname = solv_strdup(name);
- rname[r - name] = 0;
- if (archid == ARCH_SRC || archid == ARCH_NOSRC)
- flags |= SELECTION_SOURCE_ONLY;
- if ((ret = selection_depglob(pool, selection, rname, flags)) != 0)
- {
- selection_filter_rel(pool, selection, REL_ARCH, archid);
- solv_free(rname);
- return ret | SELECTION_DOTARCH;
- }
- solv_free(rname);
- }
- return 0;
-}
-
-static int
-selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
-{
- Dataiterator di;
- Queue q;
- int type;
-
- /* all files in the file list start with a '/' */
- if (*name != '/')
- {
- if (!(flags & SELECTION_GLOB))
- return 0;
- if (*name != '*' && *name != '[' && *name != '?')
- return 0;
- }
- type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
- if ((flags & SELECTION_NOCASE) != 0)
- type |= SEARCH_NOCASE;
- queue_init(&q);
- dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
- while (dataiterator_step(&di))
- {
- Solvable *s = pool->solvables + di.solvid;
- if (!s->repo)
- continue;
- if (s->repo != pool->installed && !pool_installable(pool, s))
- {
- if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
- continue;
- if (pool_disabled_solvable(pool, s))
- continue;
- }
- if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
- continue;
- queue_push(&q, di.solvid);
- dataiterator_skip_solvable(&di);
- }
- dataiterator_free(&di);
- if (!q.count)
- return 0;
- if (q.count > 1)
- queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
- else
- queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
- queue_free(&q);
- return SELECTION_FILELIST;
-}
-
-static char *
-splitrel(char *rname, char *r, int *rflagsp)
-{
- int nend = r - rname;
- int rflags = 0;
- if (nend && *r == '=' && r[-1] == '!')
- {
- nend--;
- r++;
- rflags = REL_LT|REL_GT;
- }
- for (; *r; r++)
- {
- if (*r == '<')
- rflags |= REL_LT;
- else if (*r == '=')
- rflags |= REL_EQ;
- else if (*r == '>')
- rflags |= REL_GT;
- else
- break;
- }
- while (*r && (*r == ' ' || *r == '\t'))
- r++;
- while (nend && (rname[nend - 1] == ' ' || rname[nend - 1] == '\t'))
- nend--;
- if (!*rname || !*r)
- return 0;
- *rflagsp = rflags;
- rname[nend] = 0;
- return r;
-}
-
-static int
-selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
-{
- int ret, rflags = 0;
- char *r, *rname;
-
- /* relation case, support:
- * depglob rel
- * depglob.arch rel
- */
- rname = solv_strdup(name);
- if ((r = strpbrk(rname, "<=>")) != 0)
- {
- if ((r = splitrel(rname, r, &rflags)) == 0)
- {
- solv_free(rname);
- return 0;
- }
- }
- if ((ret = selection_depglob_arch(pool, selection, rname, flags)) != 0)
- {
- if (rflags)
- selection_filter_rel(pool, selection, rflags, pool_str2id(pool, r, 1));
- solv_free(rname);
- return ret | SELECTION_REL;
- }
- solv_free(rname);
- return 0;
-}
-
-#if defined(MULTI_SEMANTICS)
-# define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE)
-#elif defined(DEBIAN)
-# define EVRCMP_DEPCMP EVRCMP_COMPARE
-#else
-# define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE
-#endif
-
-/* magic epoch promotion code, works only for SELECTION_NAME selections */
-static void
-selection_filter_evr(Pool *pool, Queue *selection, char *evr)
-{
- int i, j;
- Queue q;
- Id qbuf[10];
-
- queue_init(&q);
- queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
- for (i = j = 0; i < selection->count; i += 2)
- {
- Id select = selection->elements[i] & SOLVER_SELECTMASK;
- Id id = selection->elements[i + 1];
- Id p, pp;
- const char *lastepoch = 0;
- int lastepochlen = 0;
-
- queue_empty(&q);
- FOR_JOB_SELECT(p, pp, select, id)
- {
- Solvable *s = pool->solvables + p;
- const char *sevr = pool_id2str(pool, s->evr);
- const char *sp;
- for (sp = sevr; *sp >= '0' && *sp <= '9'; sp++)
- ;
- if (*sp != ':')
- sp = sevr;
- /* compare vr part */
- if (strcmp(evr, sp != sevr ? sp + 1 : sevr) != 0)
- {
- int r = pool_evrcmp_str(pool, sp != sevr ? sp + 1 : sevr, evr, EVRCMP_DEPCMP);
- if (r == -1 || r == 1)
- continue; /* solvable does not match vr */
- }
- queue_push(&q, p);
- if (sp > sevr)
- {
- while (sevr < sp && *sevr == '0') /* normalize epoch */
- sevr++;
- }
- if (!lastepoch)
- {
- lastepoch = sevr;
- lastepochlen = sp - sevr;
- }
- else if (lastepochlen != sp - sevr || strncmp(lastepoch, sevr, lastepochlen) != 0)
- lastepochlen = -1; /* multiple different epochs */
- }
- if (!lastepoch || lastepochlen == 0)
- id = pool_str2id(pool, evr, 1); /* no match at all or zero epoch */
- else if (lastepochlen >= 0)
- {
- /* found exactly one epoch, simply prepend */
- char *evrx = solv_malloc(strlen(evr) + lastepochlen + 2);
- strncpy(evrx, lastepoch, lastepochlen + 1);
- strcpy(evrx + lastepochlen + 1, evr);
- id = pool_str2id(pool, evrx, 1);
- solv_free(evrx);
- }
- else
- {
- /* multiple epochs in multiple solvables, convert to list of solvables */
- selection->elements[j] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF;
- selection->elements[j + 1] = pool_queuetowhatprovides(pool, &q);
- j += 2;
- continue;
- }
- queue_empty(&q);
- queue_push2(&q, selection->elements[i], selection->elements[i + 1]);
- selection_filter_rel(pool, &q, REL_EQ, id);
- if (!q.count)
- continue; /* oops, no match */
- selection->elements[j] = q.elements[0];
- selection->elements[j + 1] = q.elements[1];
- j += 2;
- }
- queue_truncate(selection, j);
- queue_free(&q);
-}
-
-/* match the "canonical" name of the package */
-static int
-selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
-{
- char *rname, *r, *r2;
- Id archid = 0;
- int ret;
-
- /*
- * nameglob-version
- * nameglob-version.arch
- * nameglob-version-release
- * nameglob-version-release.arch
- */
- flags |= SELECTION_NAME;
- flags &= ~SELECTION_PROVIDES;
-
- if (pool->disttype == DISTTYPE_DEB)
- {
- if ((r = strchr(name, '_')) == 0)
- return 0;
- rname = solv_strdup(name); /* so we can modify it */
- r = rname + (r - name);
- *r++ = 0;
- if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
- {
- solv_free(rname);
- return 0;
- }
- /* is there a vaild arch? */
- if ((r2 = strrchr(r, '_')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
- {
- *r2 = 0; /* split off */
- selection_filter_rel(pool, selection, REL_ARCH, archid);
- }
- selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
- solv_free(rname);
- return ret | SELECTION_CANON;
- }
-
- if (pool->disttype == DISTTYPE_HAIKU)
- {
- if ((r = strchr(name, '-')) == 0)
- return 0;
- rname = solv_strdup(name); /* so we can modify it */
- r = rname + (r - name);
- *r++ = 0;
- if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
- {
- solv_free(rname);
- return 0;
- }
- /* is there a vaild arch? */
- if ((r2 = strrchr(r, '-')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
- {
- *r2 = 0; /* split off */
- selection_filter_rel(pool, selection, REL_ARCH, archid);
- }
- selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
- solv_free(rname);
- return ret | SELECTION_CANON;
- }
-
- if ((r = strrchr(name, '-')) == 0)
- return 0;
- rname = solv_strdup(name); /* so we can modify it */
- r = rname + (r - name);
- *r = 0;
-
- /* split off potential arch part from version */
- if ((r2 = strrchr(r + 1, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
- *r2 = 0; /* found valid arch, split it off */
- if (archid == ARCH_SRC || archid == ARCH_NOSRC)
- flags |= SELECTION_SOURCE_ONLY;
-
- /* try with just the version */
- if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
- {
- /* no luck, try with version-release */
- if ((r2 = strrchr(rname, '-')) == 0)
- {
- solv_free(rname);
- return 0;
- }
- *r = '-';
- *r2 = 0;
- r = r2;
- if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
- {
- solv_free(rname);
- return 0;
- }
- }
- if (archid)
- selection_filter_rel(pool, selection, REL_ARCH, archid);
- selection_filter_evr(pool, selection, r + 1); /* magic epoch promotion */
- solv_free(rname);
- return ret | SELECTION_CANON;
-}
-
-int
-selection_make(Pool *pool, Queue *selection, const char *name, int flags)
-{
- int ret = 0;
-
- queue_empty(selection);
- if ((flags & SELECTION_FILELIST) != 0)
- ret = selection_filelist(pool, selection, name, flags);
- if (!ret && (flags & SELECTION_REL) != 0 && strpbrk(name, "<=>") != 0)
- ret = selection_rel(pool, selection, name, flags);
- if (!ret)
- ret = selection_depglob_arch(pool, selection, name, flags);
- if (!ret && (flags & SELECTION_CANON) != 0)
- ret = selection_canon(pool, selection, name, flags);
- if (selection->count && (flags & SELECTION_INSTALLED_ONLY) != 0)
- selection_filter_installed(pool, selection);
- if (ret && !selection->count)
- ret = 0; /* no match -> always return zero */
- if (ret && (flags & SELECTION_FLAT) != 0)
- selection_flatten(pool, selection);
- return ret;
-}
-
-static int
-matchdep(Pool *pool, Id id, char *rname, int rflags, char *revr, int flags)
-{
- if (ISRELDEP(id))
- {
- Reldep *rd = GETRELDEP(pool, id);
- if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH)
- {
- if (matchdep(pool, rd->name, rname, rflags, revr, flags))
- return 1;
- if (matchdep(pool, rd->evr, rname, rflags, revr, flags))
- return 1;
- return 0;
- }
- if (rd->flags == REL_ARCH)
- return matchdep(pool, rd->name, rname, rflags, revr, flags);
- if (!matchdep(pool, rd->name, rname, rflags, revr, flags))
- return 0;
- if (rflags)
- {
- /* XXX: need pool_match_flags_evr here */
- if (!pool_match_dep(pool, pool_rel2id(pool, rd->name, pool_str2id(pool, revr, 1), rflags, 1), id))
- return 0;
- }
- return 1;
- }
- if (flags & SELECTION_GLOB)
- {
- int globflags = (flags & SELECTION_NOCASE) != 0 ? FNM_CASEFOLD : 0;
- return fnmatch(rname, pool_id2str(pool, id), globflags) == 0 ? 1 : 0;
- }
- if (flags & SELECTION_NOCASE)
- return strcasecmp(rname, pool_id2str(pool, id)) == 0 ? 1 : 0;
- return strcmp(rname, pool_id2str(pool, id)) == 0 ? 1 : 0;
-}
-
-/*
- * select against the dependencies in keyname
- * like SELECTION_REL and SELECTION_PROVIDES, but with the
- * deps in keyname instead of provides.
- */
-int
-selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker)
-{
- char *rname, *r;
- int rflags = 0;
- Id p;
- Queue q;
-
- queue_empty(selection);
- rname = solv_strdup(name);
- if ((r = strpbrk(rname, "<=>")) != 0)
- {
- if ((r = splitrel(rname, r, &rflags)) == 0)
- {
- solv_free(rname);
- return 0;
- }
- }
- if ((flags & SELECTION_GLOB) != 0 && !strpbrk(name, "[*?") != 0)
- flags &= ~SELECTION_GLOB;
-
- queue_init(&q);
- FOR_POOL_SOLVABLES(p)
- {
- Solvable *s = pool->solvables + p;
- int i;
-
- if (s->repo != pool->installed && !pool_installable(pool, s))
- {
- if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
- continue;
- if (pool_disabled_solvable(pool, s))
- continue;
- }
- if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
- continue;
- if ((s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) && !(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
- continue;
- queue_empty(&q);
- repo_lookup_deparray(s->repo, p, keyname, &q, marker);
- for (i = 0; i < q.count; i++)
- {
- Id id = q.elements[i];
- if (matchdep(pool, id, rname, rflags, r, flags))
- break;
- }
- if (i < q.count)
- queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, p);
- }
- queue_free(&q);
- solv_free(rname);
- if (!selection->count)
- return 0;
- if ((flags & SELECTION_FLAT) != 0)
- selection_flatten(pool, selection);
- return SELECTION_PROVIDES;
-}
-
-static inline int
-pool_is_kind(Pool *pool, Id name, Id kind)
-{
- const char *n;
- if (!kind)
- return 1;
- n = pool_id2str(pool, name);
- if (kind != 1)
- {
- const char *kn = pool_id2str(pool, kind);
- int knl = strlen(kn);
- return !strncmp(n, kn, knl) && n[knl] == ':' ? 1 : 0;
- }
- else
- {
- if (*n == ':')
- return 1;
- while(*n >= 'a' && *n <= 'z')
- n++;
- return *n == ':' ? 0 : 1;
- }
-}
-
-void
-selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
-{
- int i, j, miss;
- Id p, pp, q1filled = 0;
- Queue q1;
- Map m2;
- Id setflags = 0;
-
- if (!sel1->count || !sel2->count)
- {
- queue_empty(sel1);
- return;
- }
- if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
- {
- /* XXX: not 100% correct, but very useful */
- p = sel1->elements[0] & ~(SOLVER_SELECTMASK | SOLVER_SETMASK); /* job & jobflags */
- queue_free(sel1);
- queue_init_clone(sel1, sel2);
- for (i = 0; i < sel1->count; i += 2)
- sel1->elements[i] = (sel1->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | p ;
- return;
- }
- queue_init(&q1);
- map_init(&m2, pool->nsolvables);
- for (i = 0; i < sel2->count; i += 2)
- {
- Id select = sel2->elements[i] & SOLVER_SELECTMASK;
- if (select == SOLVER_SOLVABLE_ALL)
- {
- queue_free(&q1);
- map_free(&m2);
- return;
- }
- if (select == SOLVER_SOLVABLE_REPO)
- {
- Solvable *s;
- Repo *repo = pool_id2repo(pool, sel2->elements[i + 1]);
- if (repo)
- FOR_REPO_SOLVABLES(repo, p, s)
- map_set(&m2, p);
- }
- else
- {
- if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(sel2->elements[i + 1]))
- {
- Reldep *rd = GETRELDEP(pool, sel2->elements[i + 1]);
- if (rd->flags == REL_ARCH && rd->name == 0)
- {
- /* special arch filter */
- if (!q1filled++)
- selection_solvables(pool, sel1, &q1);
- for (j = 0; j < q1.count; j++)
- {
- Id p = q1.elements[j];
- Solvable *s = pool->solvables + p;
- if (s->arch == rd->evr || (rd->evr == ARCH_SRC && s->arch == ARCH_NOSRC))
- map_set(&m2, p);
- }
- continue;
- }
- else if (rd->flags == REL_KIND && rd->name == 0)
- {
- /* special kind filter */
- if (!q1filled++)
- selection_solvables(pool, sel1, &q1);
- for (j = 0; j < q1.count; j++)
- {
- Id p = q1.elements[j];
- Solvable *s = pool->solvables + p;
- if (pool_is_kind(pool, s->name, rd->evr))
- map_set(&m2, p);
- }
- continue;
- }
- }
- FOR_JOB_SELECT(p, pp, select, sel2->elements[i + 1])
- map_set(&m2, p);
- }
- }
- if (sel2->count == 2) /* XXX: AND all setmasks instead? */
- setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET;
- for (i = j = 0; i < sel1->count; i += 2)
- {
- Id select = sel1->elements[i] & SOLVER_SELECTMASK;
- queue_empty(&q1);
- miss = 0;
- if (select == SOLVER_SOLVABLE_ALL)
- {
- FOR_POOL_SOLVABLES(p)
- {
- if (map_tst(&m2, p))
- queue_push(&q1, p);
- else
- miss = 1;
- }
- }
- else if (select == SOLVER_SOLVABLE_REPO)
- {
- Solvable *s;
- Repo *repo = pool_id2repo(pool, sel1->elements[i + 1]);
- if (repo)
- FOR_REPO_SOLVABLES(repo, p, s)
- {
- if (map_tst(&m2, p))
- queue_push(&q1, p);
- else
- miss = 1;
- }
- }
- else
- {
- FOR_JOB_SELECT(p, pp, select, sel1->elements[i + 1])
- {
- if (map_tst(&m2, p))
- queue_pushunique(&q1, p);
- else
- miss = 1;
- }
- }
- if (!q1.count)
- continue;
- if (!miss)
- {
- sel1->elements[j] = sel1->elements[i] | setflags;
- sel1->elements[j + 1] = sel1->elements[i + 1];
- }
- else if (q1.count > 1)
- {
- sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags;
- sel1->elements[j + 1] = pool_queuetowhatprovides(pool, &q1);
- }
- else
- {
- sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags;
- sel1->elements[j + 1] = q1.elements[0];
- }
- j += 2;
- }
- queue_truncate(sel1, j);
- queue_free(&q1);
- map_free(&m2);
-}
-
-void
-selection_add(Pool *pool, Queue *sel1, Queue *sel2)
-{
- int i;
- for (i = 0; i < sel2->count; i++)
- queue_push(sel1, sel2->elements[i]);
-}
-
-const char *
-pool_selection2str(Pool *pool, Queue *selection, Id flagmask)
-{
- char *s;
- const char *s2;
- int i;
- s = pool_tmpjoin(pool, 0, 0, 0);
- for (i = 0; i < selection->count; i += 2)
- {
- Id how = selection->elements[i];
- if (*s)
- s = pool_tmpappend(pool, s, " + ", 0);
- s2 = solver_select2str(pool, how & SOLVER_SELECTMASK, selection->elements[i + 1]);
- s = pool_tmpappend(pool, s, s2, 0);
- pool_freetmpspace(pool, s2);
- how &= flagmask & SOLVER_SETMASK;
- if (how)
- {
- int o = strlen(s);
- s = pool_tmpappend(pool, s, " ", 0);
- if (how & SOLVER_SETEV)
- s = pool_tmpappend(pool, s, ",setev", 0);
- if (how & SOLVER_SETEVR)
- s = pool_tmpappend(pool, s, ",setevr", 0);
- if (how & SOLVER_SETARCH)
- s = pool_tmpappend(pool, s, ",setarch", 0);
- if (how & SOLVER_SETVENDOR)
- s = pool_tmpappend(pool, s, ",setvendor", 0);
- if (how & SOLVER_SETREPO)
- s = pool_tmpappend(pool, s, ",setrepo", 0);
- if (how & SOLVER_NOAUTOSET)
- s = pool_tmpappend(pool, s, ",noautoset", 0);
- if (s[o + 1] != ',')
- s = pool_tmpappend(pool, s, ",?", 0);
- s[o + 1] = '[';
- s = pool_tmpappend(pool, s, "]", 0);
- }
- }
- return s;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * selection.h
- *
- */
-
-#ifndef LIBSOLV_SELECTION_H
-#define LIBSOLV_SELECTION_H
-
-#include "pool.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define SELECTION_NAME (1 << 0)
-#define SELECTION_PROVIDES (1 << 1)
-#define SELECTION_FILELIST (1 << 2)
-#define SELECTION_CANON (1 << 3)
-#define SELECTION_DOTARCH (1 << 4)
-#define SELECTION_REL (1 << 5)
-
-#define SELECTION_INSTALLED_ONLY (1 << 8)
-#define SELECTION_GLOB (1 << 9)
-#define SELECTION_FLAT (1 << 10)
-#define SELECTION_NOCASE (1 << 11)
-#define SELECTION_SOURCE_ONLY (1 << 12)
-#define SELECTION_WITH_SOURCE (1 << 13)
-#define SELECTION_SKIP_KIND (1 << 14)
-
-extern int selection_make(Pool *pool, Queue *selection, const char *name, int flags);
-extern int selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker);
-
-extern void selection_filter(Pool *pool, Queue *sel1, Queue *sel2);
-extern void selection_add(Pool *pool, Queue *sel1, Queue *sel2);
-extern void selection_solvables(Pool *pool, Queue *selection, Queue *pkgs);
-
-extern const char *pool_selection2str(Pool *pool, Queue *selection, Id flagmask);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
-SHA-1 in C
-By Steve Reid <sreid@sea-to-sky.net>
-100% Public Domain
-
------------------
-Modified 7/98
-By James H. Brown <jbrown@burgoyne.com>
-Still 100% Public Domain
-
-Corrected a problem which generated improper hash values on 16 bit machines
-Routine SHA1Update changed from
- void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
-len)
-to
- void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
-long len)
-
-The 'len' parameter was declared an int which works fine on 32 bit machines.
-However, on 16 bit machines an int is too small for the shifts being done
-against
-it. This caused the hash function to generate incorrect values if len was
-greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
-
-Since the file IO in main() reads 16K at a time, any file 8K or larger would
-be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
-"a"s).
-
-I also changed the declaration of variables i & j in SHA1Update to
-unsigned long from unsigned int for the same reason.
-
-These changes should make no difference to any 32 bit implementations since
-an
-int and a long are the same size in those environments.
-
---
-I also corrected a few compiler warnings generated by Borland C.
-1. Added #include <process.h> for exit() prototype
-2. Removed unused variable 'j' in SHA1Final
-3. Changed exit(0) to return(0) at end of main.
-
-ALL changes I made can be located by searching for comments containing 'JHB'
------------------
-Modified 8/98
-By Steve Reid <sreid@sea-to-sky.net>
-Still 100% public domain
-
-1- Removed #include <process.h> and used return() instead of exit()
-2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
-3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
-
------------------
-Modified 4/01
-By Saul Kravitz <Saul.Kravitz@celera.com>
-Still 100% PD
-Modified to run on Compaq Alpha hardware.
-
------------------
-Modified 07/2002
-By Ralph Giles <giles@ghostscript.com>
-Still 100% public domain
-modified for use with stdint types, autoconf
-code cleanup, removed attribution comments
-switched SHA1Final() argument order for consistency
-use SHA1_ prefix for public api
-move public api to sha1.h
-*/
-
-/*
-Test Vectors (from FIPS PUB 180-1)
-"abc"
- A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
-"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
- 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
-A million repetitions of "a"
- 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
-*/
-
-#include <stdio.h>
-#include <string.h>
-#include "sha1.h"
-
-
-static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
-
-#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
-
-/* blk0() and blk() perform the initial expand. */
-/* I got the idea of expanding during the round function from SSLeay */
-/* FIXME: can we do this in an endian-proof way? */
-#ifdef WORDS_BIGENDIAN
-#define blk0(i) block.l[i]
-#else
-#define blk0(i) (block.l[i] = (rol(block.l[i],24)&0xFF00FF00) \
- |(rol(block.l[i],8)&0x00FF00FF))
-#endif
-#define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15] \
- ^block.l[(i+2)&15]^block.l[i&15],1))
-
-/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
-#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
-#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
-#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
-
-
-/* Hash a single 512-bit block. This is the core of the algorithm. */
-static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
-{
- uint32_t a, b, c, d, e;
- typedef union {
- uint8_t c[64];
- uint32_t l[16];
- } CHAR64LONG16;
- CHAR64LONG16 block;
-
- memcpy(&block, buffer, 64);
-
- /* Copy context->state[] to working vars */
- a = state[0];
- b = state[1];
- c = state[2];
- d = state[3];
- e = state[4];
-
- /* 4 rounds of 20 operations each. Loop unrolled. */
- R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
- R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
- R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
- R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
- R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
- R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
- R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
- R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
- R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
- R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
- R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
- R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
- R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
- R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
- R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
- R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
- R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
- R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
- R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
- R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
-
- /* Add the working vars back into context.state[] */
- state[0] += a;
- state[1] += b;
- state[2] += c;
- state[3] += d;
- state[4] += e;
-
- /* Wipe variables */
- a = b = c = d = e = 0;
-}
-
-
-/* SHA1Init - Initialize new context */
-void solv_SHA1_Init(SHA1_CTX* context)
-{
- /* SHA1 initialization constants */
- context->state[0] = 0x67452301;
- context->state[1] = 0xEFCDAB89;
- context->state[2] = 0x98BADCFE;
- context->state[3] = 0x10325476;
- context->state[4] = 0xC3D2E1F0;
- context->count[0] = context->count[1] = 0;
-}
-
-
-/* Run your data through this. */
-void solv_SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
-{
- size_t i, j;
-
-#ifdef VERBOSE
- SHAPrintContext(context, "before");
-#endif
-
- j = (context->count[0] >> 3) & 63;
- if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
- context->count[1] += (len >> 29);
- if ((j + len) > 63) {
- memcpy(&context->buffer[j], data, (i = 64-j));
- SHA1_Transform(context->state, context->buffer);
- for ( ; i + 63 < len; i += 64) {
- SHA1_Transform(context->state, data + i);
- }
- j = 0;
- }
- else i = 0;
- memcpy(&context->buffer[j], &data[i], len - i);
-
-#ifdef VERBOSE
- SHAPrintContext(context, "after ");
-#endif
-}
-
-
-/* Add padding and return the message digest. */
-void solv_SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
-{
- uint32_t i;
- uint8_t finalcount[8];
-
- for (i = 0; i < 8; i++) {
- finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
- >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
- }
- solv_SHA1_Update(context, (uint8_t *)"\200", 1);
- while ((context->count[0] & 504) != 448) {
- solv_SHA1_Update(context, (uint8_t *)"\0", 1);
- }
- solv_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
- for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
- digest[i] = (uint8_t)
- ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
- }
-
- /* Wipe variables */
- i = 0;
- memset(context->buffer, 0, 64);
- memset(context->state, 0, 20);
- memset(context->count, 0, 8);
- memset(finalcount, 0, 8); /* SWR */
-}
+++ /dev/null
-/* public api for steve reid's public domain SHA-1 implementation */
-/* this file is in the public domain */
-
-#include <stdint.h>
-
-typedef struct {
- uint32_t state[5];
- uint32_t count[2];
- uint8_t buffer[64];
-} SHA1_CTX;
-
-#define SHA1_DIGEST_SIZE 20
-
-void solv_SHA1_Init(SHA1_CTX* context);
-void solv_SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
-void solv_SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
+++ /dev/null
-/*
- * FILE: sha2.c
- * AUTHOR: Aaron D. Gifford <me@aarongifford.com>
- *
- * Copyright (c) 2000-2001, Aaron D. Gifford
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holder nor the names of contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $Id: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
- */
-
-#include <sys/types.h>
-#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
-/* #include <assert.h> */ /* assert() */
-#include <stdio.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <inttypes.h>
-
-#include "sha2.h"
-
-
-/*
- * ASSERT NOTE:
- * Some sanity checking code is included using assert(). On my FreeBSD
- * system, this additional code can be removed by compiling with NDEBUG
- * defined. Check your own systems manpage on assert() to see how to
- * compile WITHOUT the sanity checking code on your system.
- *
- * UNROLLED TRANSFORM LOOP NOTE:
- * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
- * loop version for the hash transform rounds (defined using macros
- * later in this file). Either define on the command line, for example:
- *
- * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
- *
- * or define below:
- *
- * #define SHA2_UNROLL_TRANSFORM
- *
- */
-
- #define SHA2_UNROLL_TRANSFORM
-
-
-/*** SHA-256/384/512 Machine Architecture Definitions *****************/
-/*
- * BYTE_ORDER NOTE:
- *
- * Please make sure that your system defines BYTE_ORDER. If your
- * architecture is little-endian, make sure it also defines
- * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
- * equivilent.
- *
- * If your system does not define the above, then you can do so by
- * hand like this:
- *
- * #define LITTLE_ENDIAN 1234
- * #define BIG_ENDIAN 4321
- *
- * And for little-endian machines, add:
- *
- * #define BYTE_ORDER LITTLE_ENDIAN
- *
- * Or for big-endian machines:
- *
- * #define BYTE_ORDER BIG_ENDIAN
- *
- * The FreeBSD machine this was written on defines BYTE_ORDER
- * appropriately by including <sys/types.h> (which in turn includes
- * <machine/endian.h> where the appropriate definitions are actually
- * made).
- */
-
-/*
- * Define the following sha2_* types to types of the correct length on
- * the native archtecture. Most BSD systems and Linux define u_intXX_t
- * types. Machines with very recent ANSI C headers, can use the
- * uintXX_t definintions from inttypes.h by defining SHA2_USE_INTTYPES_H
- * during compile or in the sha.h header file.
- *
- * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t
- * will need to define these three typedefs below (and the appropriate
- * ones in sha.h too) by hand according to their system architecture.
- *
- * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t
- * types and pointing out recent ANSI C support for uintXX_t in inttypes.h.
- */
-typedef uint8_t sha2_byte; /* Exactly 1 byte */
-typedef uint32_t sha2_word32; /* Exactly 4 bytes */
-typedef uint64_t sha2_word64; /* Exactly 8 bytes */
-
-
-/*** SHA-256/384/512 Various Length Definitions ***********************/
-/* NOTE: Most of these are in sha2.h */
-#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
-#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16)
-#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
-
-
-/*** ENDIAN REVERSAL MACROS *******************************************/
-#ifndef WORDS_BIGENDIAN
-#define REVERSE32(w,x) { \
- sha2_word32 tmp = (w); \
- tmp = (tmp >> 16) | (tmp << 16); \
- (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
-}
-#define REVERSE64(w,x) { \
- sha2_word64 tmp = (w); \
- tmp = (tmp >> 32) | (tmp << 32); \
- tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
- ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
- (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
- ((tmp & 0x0000ffff0000ffffULL) << 16); \
-}
-#endif /* !WORDS_BIGENDIAN */
-
-/*
- * Macro for incrementally adding the unsigned 64-bit integer n to the
- * unsigned 128-bit integer (represented using a two-element array of
- * 64-bit words):
- */
-#define ADDINC128(w,n) { \
- (w)[0] += (sha2_word64)(n); \
- if ((w)[0] < (n)) { \
- (w)[1]++; \
- } \
-}
-
-/*
- * Macros for copying blocks of memory and for zeroing out ranges
- * of memory. Using these macros makes it easy to switch from
- * using memset()/memcpy() and using bzero()/bcopy().
- *
- * Please define either SHA2_USE_MEMSET_MEMCPY or define
- * SHA2_USE_BZERO_BCOPY depending on which function set you
- * choose to use:
- */
-#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
-/* Default to memset()/memcpy() if no option is specified */
-#define SHA2_USE_MEMSET_MEMCPY 1
-#endif
-#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
-/* Abort with an error if BOTH options are defined */
-#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
-#endif
-
-#ifdef SHA2_USE_MEMSET_MEMCPY
-#define MEMSET_BZERO(p,l) memset((p), 0, (l))
-#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
-#endif
-#ifdef SHA2_USE_BZERO_BCOPY
-#define MEMSET_BZERO(p,l) bzero((p), (l))
-#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
-#endif
-
-
-/*** THE SIX LOGICAL FUNCTIONS ****************************************/
-/*
- * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
- *
- * NOTE: The naming of R and S appears backwards here (R is a SHIFT and
- * S is a ROTATION) because the SHA-256/384/512 description document
- * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
- * same "backwards" definition.
- */
-/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
-#define R(b,x) ((x) >> (b))
-/* 32-bit Rotate-right (used in SHA-256): */
-#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
-/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
-#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
-
-/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
-#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
-#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
-
-/* Four of six logical functions used in SHA-256: */
-#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
-#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
-#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
-#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
-
-/* Four of six logical functions used in SHA-384 and SHA-512: */
-#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
-#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
-#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
-#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
-
-/*** INTERNAL FUNCTION PROTOTYPES *************************************/
-/* NOTE: These should not be accessed directly from outside this
- * library -- they are intended for private internal visibility/use
- * only.
- */
-static void SHA256_Last(SHA256_CTX*);
-static void SHA512_Last(SHA512_CTX*);
-static void SHA256_Transform(SHA256_CTX*, const sha2_word32*);
-static void SHA512_Transform(SHA512_CTX*, const sha2_word64*);
-
-
-/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
-/* Hash constant words K for SHA-256: */
-const static sha2_word32 K256[64] = {
- 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
- 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
- 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
- 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
- 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
- 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
- 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
- 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
- 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
- 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
- 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
- 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
- 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
- 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
- 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
- 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
-};
-
-/* Initial hash value H for SHA-256: */
-const static sha2_word32 sha256_initial_hash_value[8] = {
- 0x6a09e667UL,
- 0xbb67ae85UL,
- 0x3c6ef372UL,
- 0xa54ff53aUL,
- 0x510e527fUL,
- 0x9b05688cUL,
- 0x1f83d9abUL,
- 0x5be0cd19UL
-};
-
-/* Hash constant words K for SHA-384 and SHA-512: */
-const static sha2_word64 K512[80] = {
- 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
- 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
- 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
- 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
- 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
- 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
- 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
- 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
- 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
- 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
- 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
- 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
- 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
- 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
- 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
- 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
- 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
- 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
- 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
- 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
- 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
- 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
- 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
- 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
- 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
- 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
- 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
- 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
- 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
- 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
- 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
- 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
- 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
- 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
- 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
- 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
- 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
- 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
- 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
- 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
-};
-
-/* Initial hash value H for SHA-384 */
-const static sha2_word64 sha384_initial_hash_value[8] = {
- 0xcbbb9d5dc1059ed8ULL,
- 0x629a292a367cd507ULL,
- 0x9159015a3070dd17ULL,
- 0x152fecd8f70e5939ULL,
- 0x67332667ffc00b31ULL,
- 0x8eb44a8768581511ULL,
- 0xdb0c2e0d64f98fa7ULL,
- 0x47b5481dbefa4fa4ULL
-};
-
-/* Initial hash value H for SHA-512 */
-const static sha2_word64 sha512_initial_hash_value[8] = {
- 0x6a09e667f3bcc908ULL,
- 0xbb67ae8584caa73bULL,
- 0x3c6ef372fe94f82bULL,
- 0xa54ff53a5f1d36f1ULL,
- 0x510e527fade682d1ULL,
- 0x9b05688c2b3e6c1fULL,
- 0x1f83d9abfb41bd6bULL,
- 0x5be0cd19137e2179ULL
-};
-
-/* Initial hash value H for SHA-224: */
-const static sha2_word32 sha224_initial_hash_value[8] = {
- 0xc1059ed8UL,
- 0x367cd507UL,
- 0x3070dd17UL,
- 0xf70e5939UL,
- 0xffc00b31UL,
- 0x68581511UL,
- 0x64f98fa7UL,
- 0xbefa4fa4UL
-};
-
-
-/*** SHA-256: *********************************************************/
-void solv_SHA256_Init(SHA256_CTX* context) {
- if (context == (SHA256_CTX*)0) {
- return;
- }
- MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);
- MEMSET_BZERO((char *)context->buffer, SHA256_BLOCK_LENGTH);
- context->bitcount = 0;
-}
-
-#ifdef SHA2_UNROLL_TRANSFORM
-
-/* Unrolled SHA-256 round macros: */
-
-#ifndef WORDS_BIGENDIAN
-
-#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
- REVERSE32(*data++, W256[j]); \
- T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
- K256[j] + W256[j]; \
- (d) += T1; \
- (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
- j++
-
-
-#else /* !WORDS_BIGENDIAN */
-
-#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
- T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
- K256[j] + (W256[j] = *data++); \
- (d) += T1; \
- (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
- j++
-
-#endif /* !WORDS_BIGENDIAN */
-
-#define ROUND256(a,b,c,d,e,f,g,h) \
- s0 = W256[(j+1)&0x0f]; \
- s0 = sigma0_256(s0); \
- s1 = W256[(j+14)&0x0f]; \
- s1 = sigma1_256(s1); \
- T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
- (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
- (d) += T1; \
- (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
- j++
-
-static void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
- sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
- sha2_word32 T1, *W256;
- int j;
-
- W256 = context->buffer;
-
- /* Initialize registers with the prev. intermediate value */
- a = context->state[0];
- b = context->state[1];
- c = context->state[2];
- d = context->state[3];
- e = context->state[4];
- f = context->state[5];
- g = context->state[6];
- h = context->state[7];
-
- j = 0;
- do {
- /* Rounds 0 to 15 (unrolled): */
- ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
- ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
- ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
- ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
- ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
- ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
- ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
- ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
- } while (j < 16);
-
- /* Now for the remaining rounds to 64: */
- do {
- ROUND256(a,b,c,d,e,f,g,h);
- ROUND256(h,a,b,c,d,e,f,g);
- ROUND256(g,h,a,b,c,d,e,f);
- ROUND256(f,g,h,a,b,c,d,e);
- ROUND256(e,f,g,h,a,b,c,d);
- ROUND256(d,e,f,g,h,a,b,c);
- ROUND256(c,d,e,f,g,h,a,b);
- ROUND256(b,c,d,e,f,g,h,a);
- } while (j < 64);
-
- /* Compute the current intermediate hash value */
- context->state[0] += a;
- context->state[1] += b;
- context->state[2] += c;
- context->state[3] += d;
- context->state[4] += e;
- context->state[5] += f;
- context->state[6] += g;
- context->state[7] += h;
-
- /* Clean up */
- a = b = c = d = e = f = g = h = T1 = 0;
-}
-
-#else /* SHA2_UNROLL_TRANSFORM */
-
-static void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
- sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
- sha2_word32 T1, T2, *W256;
- int j;
-
- W256 = context->buffer;
-
- /* Initialize registers with the prev. intermediate value */
- a = context->state[0];
- b = context->state[1];
- c = context->state[2];
- d = context->state[3];
- e = context->state[4];
- f = context->state[5];
- g = context->state[6];
- h = context->state[7];
-
- j = 0;
- do {
-#ifndef WORDS_BIGENDIAN
- /* Copy data while converting to host byte order */
- REVERSE32(*data++,W256[j]);
- /* Apply the SHA-256 compression function to update a..h */
- T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
-#else /* !WORDS_BIGENDIAN */
- /* Apply the SHA-256 compression function to update a..h with copy */
- T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
-#endif /* !WORDS_BIGENDIAN */
- T2 = Sigma0_256(a) + Maj(a, b, c);
- h = g;
- g = f;
- f = e;
- e = d + T1;
- d = c;
- c = b;
- b = a;
- a = T1 + T2;
-
- j++;
- } while (j < 16);
-
- do {
- /* Part of the message block expansion: */
- s0 = W256[(j+1)&0x0f];
- s0 = sigma0_256(s0);
- s1 = W256[(j+14)&0x0f];
- s1 = sigma1_256(s1);
-
- /* Apply the SHA-256 compression function to update a..h */
- T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
- (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
- T2 = Sigma0_256(a) + Maj(a, b, c);
- h = g;
- g = f;
- f = e;
- e = d + T1;
- d = c;
- c = b;
- b = a;
- a = T1 + T2;
-
- j++;
- } while (j < 64);
-
- /* Compute the current intermediate hash value */
- context->state[0] += a;
- context->state[1] += b;
- context->state[2] += c;
- context->state[3] += d;
- context->state[4] += e;
- context->state[5] += f;
- context->state[6] += g;
- context->state[7] += h;
-
- /* Clean up */
- a = b = c = d = e = f = g = h = T1 = T2 = 0;
-}
-
-#endif /* SHA2_UNROLL_TRANSFORM */
-
-void solv_SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) {
- unsigned int freespace, usedspace;
-
- if (len == 0) {
- /* Calling with no data is valid - we do nothing */
- return;
- }
-
- /* Sanity check: */
- /* assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0); */
-
- usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
- if (usedspace > 0) {
- /* Calculate how much free space is available in the buffer */
- freespace = SHA256_BLOCK_LENGTH - usedspace;
-
- if (len >= freespace) {
- /* Fill the buffer completely and process it */
- MEMCPY_BCOPY(&((char *)context->buffer)[usedspace], data, freespace);
- context->bitcount += freespace << 3;
- len -= freespace;
- data += freespace;
- SHA256_Transform(context, context->buffer);
- } else {
- /* The buffer is not yet full */
- MEMCPY_BCOPY(&((char *)context->buffer)[usedspace], data, len);
- context->bitcount += len << 3;
- /* Clean up: */
- usedspace = freespace = 0;
- return;
- }
- }
- while (len >= SHA256_BLOCK_LENGTH) {
- /* Process as many complete blocks as we can */
- SHA256_Transform(context, (sha2_word32*)data);
- context->bitcount += SHA256_BLOCK_LENGTH << 3;
- len -= SHA256_BLOCK_LENGTH;
- data += SHA256_BLOCK_LENGTH;
- }
- if (len > 0) {
- /* There's left-overs, so save 'em */
- MEMCPY_BCOPY((char *)context->buffer, data, len);
- context->bitcount += len << 3;
- }
- /* Clean up: */
- usedspace = freespace = 0;
-}
-
-static void SHA256_Last(SHA256_CTX* context) {
- unsigned int usedspace;
-
- usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
-#ifndef WORDS_BIGENDIAN
- /* Convert FROM host byte order */
- REVERSE64(context->bitcount,context->bitcount);
-#endif
- if (usedspace > 0) {
- /* Begin padding with a 1 bit: */
- ((char *)context->buffer)[usedspace++] = 0x80;
-
- if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
- /* Set-up for the last transform: */
- MEMSET_BZERO(&((char *)context->buffer)[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
- } else {
- if (usedspace < SHA256_BLOCK_LENGTH) {
- MEMSET_BZERO(&((char *)context->buffer)[usedspace], SHA256_BLOCK_LENGTH - usedspace);
- }
- /* Do second-to-last transform: */
- SHA256_Transform(context, context->buffer);
-
- /* And set-up for the last transform: */
- MEMSET_BZERO((char *)context->buffer, SHA256_SHORT_BLOCK_LENGTH);
- }
- } else {
- /* Set-up for the last transform: */
- MEMSET_BZERO((char *)context->buffer, SHA256_SHORT_BLOCK_LENGTH);
-
- /* Begin padding with a 1 bit: */
- *((char *)context->buffer) = 0x80;
- }
- /* Set the bit count: */
- MEMCPY_BCOPY(&((char *)context->buffer)[SHA256_SHORT_BLOCK_LENGTH], (char *)(&context->bitcount), 8);
-
- /* Final transform: */
- SHA256_Transform(context, context->buffer);
-}
-
-void solv_SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
- sha2_word32 *d = (sha2_word32*)digest;
-
- /* Sanity check: */
- /* assert(context != (SHA256_CTX*)0); */
-
- /* If no digest buffer is passed, we don't bother doing this: */
- if (digest != (sha2_byte*)0) {
- SHA256_Last(context);
-
-#ifndef WORDS_BIGENDIAN
- {
- /* Convert TO host byte order */
- int j;
- for (j = 0; j < 8; j++) {
- REVERSE32(context->state[j],context->state[j]);
- *d++ = context->state[j];
- }
- }
-#else
- MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);
-#endif
- }
-
- /* Clean up state data: */
- MEMSET_BZERO(context, sizeof(*context));
-}
-
-
-/*** SHA-512: *********************************************************/
-void solv_SHA512_Init(SHA512_CTX* context) {
- if (context == (SHA512_CTX*)0) {
- return;
- }
- MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
- MEMSET_BZERO((char *)context->buffer, SHA512_BLOCK_LENGTH);
- context->bitcount[0] = context->bitcount[1] = 0;
-}
-
-#ifdef SHA2_UNROLL_TRANSFORM
-
-/* Unrolled SHA-512 round macros: */
-#ifndef WORDS_BIGENDIAN
-
-#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
- REVERSE64(*data++, W512[j]); \
- T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
- K512[j] + W512[j]; \
- (d) += T1, \
- (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \
- j++
-
-
-#else /* !WORDS_BIGENDIAN */
-
-#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
- T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
- K512[j] + (W512[j] = *data++); \
- (d) += T1; \
- (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
- j++
-
-#endif /* !WORDS_BIGENDIAN */
-
-#define ROUND512(a,b,c,d,e,f,g,h) \
- s0 = W512[(j+1)&0x0f]; \
- s0 = sigma0_512(s0); \
- s1 = W512[(j+14)&0x0f]; \
- s1 = sigma1_512(s1); \
- T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \
- (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
- (d) += T1; \
- (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
- j++
-
-static void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
- sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
- sha2_word64 T1, *W512 = context->buffer;
- int j;
-
- /* Initialize registers with the prev. intermediate value */
- a = context->state[0];
- b = context->state[1];
- c = context->state[2];
- d = context->state[3];
- e = context->state[4];
- f = context->state[5];
- g = context->state[6];
- h = context->state[7];
-
- j = 0;
- do {
- ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
- ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
- ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
- ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
- ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
- ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
- ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
- ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
- } while (j < 16);
-
- /* Now for the remaining rounds up to 79: */
- do {
- ROUND512(a,b,c,d,e,f,g,h);
- ROUND512(h,a,b,c,d,e,f,g);
- ROUND512(g,h,a,b,c,d,e,f);
- ROUND512(f,g,h,a,b,c,d,e);
- ROUND512(e,f,g,h,a,b,c,d);
- ROUND512(d,e,f,g,h,a,b,c);
- ROUND512(c,d,e,f,g,h,a,b);
- ROUND512(b,c,d,e,f,g,h,a);
- } while (j < 80);
-
- /* Compute the current intermediate hash value */
- context->state[0] += a;
- context->state[1] += b;
- context->state[2] += c;
- context->state[3] += d;
- context->state[4] += e;
- context->state[5] += f;
- context->state[6] += g;
- context->state[7] += h;
-
- /* Clean up */
- a = b = c = d = e = f = g = h = T1 = 0;
-}
-
-#else /* SHA2_UNROLL_TRANSFORM */
-
-static void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
- sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
- sha2_word64 T1, T2, *W512 = context->buffer;
- int j;
-
- /* Initialize registers with the prev. intermediate value */
- a = context->state[0];
- b = context->state[1];
- c = context->state[2];
- d = context->state[3];
- e = context->state[4];
- f = context->state[5];
- g = context->state[6];
- h = context->state[7];
-
- j = 0;
- do {
-#ifndef WORDS_BIGENDIAN
- /* Convert TO host byte order */
- REVERSE64(*data++, W512[j]);
- /* Apply the SHA-512 compression function to update a..h */
- T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
-#else /* !WORDS_BIGENDIAN */
- /* Apply the SHA-512 compression function to update a..h with copy */
- T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
-#endif /* !WORDS_BIGENDIAN */
- T2 = Sigma0_512(a) + Maj(a, b, c);
- h = g;
- g = f;
- f = e;
- e = d + T1;
- d = c;
- c = b;
- b = a;
- a = T1 + T2;
-
- j++;
- } while (j < 16);
-
- do {
- /* Part of the message block expansion: */
- s0 = W512[(j+1)&0x0f];
- s0 = sigma0_512(s0);
- s1 = W512[(j+14)&0x0f];
- s1 = sigma1_512(s1);
-
- /* Apply the SHA-512 compression function to update a..h */
- T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
- (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
- T2 = Sigma0_512(a) + Maj(a, b, c);
- h = g;
- g = f;
- f = e;
- e = d + T1;
- d = c;
- c = b;
- b = a;
- a = T1 + T2;
-
- j++;
- } while (j < 80);
-
- /* Compute the current intermediate hash value */
- context->state[0] += a;
- context->state[1] += b;
- context->state[2] += c;
- context->state[3] += d;
- context->state[4] += e;
- context->state[5] += f;
- context->state[6] += g;
- context->state[7] += h;
-
- /* Clean up */
- a = b = c = d = e = f = g = h = T1 = T2 = 0;
-}
-
-#endif /* SHA2_UNROLL_TRANSFORM */
-
-void solv_SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) {
- unsigned int freespace, usedspace;
-
- if (len == 0) {
- /* Calling with no data is valid - we do nothing */
- return;
- }
-
- /* Sanity check: */
- /* assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0); */
-
- usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
- if (usedspace > 0) {
- /* Calculate how much free space is available in the buffer */
- freespace = SHA512_BLOCK_LENGTH - usedspace;
-
- if (len >= freespace) {
- /* Fill the buffer completely and process it */
- MEMCPY_BCOPY(&((char *)context->buffer)[usedspace], data, freespace);
- ADDINC128(context->bitcount, freespace << 3);
- len -= freespace;
- data += freespace;
- SHA512_Transform(context, context->buffer);
- } else {
- /* The buffer is not yet full */
- MEMCPY_BCOPY(&((char *)context->buffer)[usedspace], data, len);
- ADDINC128(context->bitcount, len << 3);
- /* Clean up: */
- usedspace = freespace = 0;
- return;
- }
- }
- while (len >= SHA512_BLOCK_LENGTH) {
- /* Process as many complete blocks as we can */
- SHA512_Transform(context, (sha2_word64*)data);
- ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
- len -= SHA512_BLOCK_LENGTH;
- data += SHA512_BLOCK_LENGTH;
- }
- if (len > 0) {
- /* There's left-overs, so save 'em */
- MEMCPY_BCOPY((char *)context->buffer, data, len);
- ADDINC128(context->bitcount, len << 3);
- }
- /* Clean up: */
- usedspace = freespace = 0;
-}
-
-static void SHA512_Last(SHA512_CTX* context) {
- unsigned int usedspace;
-
- usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
-#ifndef WORDS_BIGENDIAN
- /* Convert FROM host byte order */
- REVERSE64(context->bitcount[0],context->bitcount[0]);
- REVERSE64(context->bitcount[1],context->bitcount[1]);
-#endif
- if (usedspace > 0) {
- /* Begin padding with a 1 bit: */
- ((char *)context->buffer)[usedspace++] = 0x80;
-
- if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
- /* Set-up for the last transform: */
- MEMSET_BZERO(&((char *)context->buffer)[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
- } else {
- if (usedspace < SHA512_BLOCK_LENGTH) {
- MEMSET_BZERO(&((char *)context->buffer)[usedspace], SHA512_BLOCK_LENGTH - usedspace);
- }
- /* Do second-to-last transform: */
- SHA512_Transform(context, context->buffer);
-
- /* And set-up for the last transform: */
- MEMSET_BZERO((char *)context->buffer, SHA512_BLOCK_LENGTH - 2);
- }
- } else {
- /* Prepare for final transform: */
- MEMSET_BZERO((char *)context->buffer, SHA512_SHORT_BLOCK_LENGTH);
-
- /* Begin padding with a 1 bit: */
- *((char *)context->buffer) = 0x80;
- }
- /* Store the length of input data (in bits): */
- MEMCPY_BCOPY(&((char *)context->buffer)[SHA512_SHORT_BLOCK_LENGTH], (char *)(&context->bitcount[1]), 8);
- MEMCPY_BCOPY(&((char *)context->buffer)[SHA512_SHORT_BLOCK_LENGTH + 8], (char *)(&context->bitcount[0]), 8);
-
- /* Final transform: */
- SHA512_Transform(context, context->buffer);
-}
-
-void solv_SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
- sha2_word64 *d = (sha2_word64*)digest;
-
- /* Sanity check: */
- /* assert(context != (SHA512_CTX*)0); */
-
- /* If no digest buffer is passed, we don't bother doing this: */
- if (digest != (sha2_byte*)0) {
- SHA512_Last(context);
-
- /* Save the hash data for output: */
-#ifndef WORDS_BIGENDIAN
- {
- /* Convert TO host byte order */
- int j;
- for (j = 0; j < 8; j++) {
- REVERSE64(context->state[j],context->state[j]);
- *d++ = context->state[j];
- }
- }
-#else
- MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);
-#endif
- }
-
- /* Zero out state data */
- MEMSET_BZERO(context, sizeof(*context));
-}
-
-
-/*** SHA-384: *********************************************************/
-void solv_SHA384_Init(SHA384_CTX* context) {
- if (context == (SHA384_CTX*)0) {
- return;
- }
- MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);
- MEMSET_BZERO((char *)context->buffer, SHA384_BLOCK_LENGTH);
- context->bitcount[0] = context->bitcount[1] = 0;
-}
-
-void solv_SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) {
- solv_SHA512_Update((SHA512_CTX*)context, data, len);
-}
-
-void solv_SHA384_Final(sha2_byte digest[], SHA384_CTX* context) {
- sha2_word64 *d = (sha2_word64*)digest;
-
- /* Sanity check: */
- /* assert(context != (SHA384_CTX*)0); */
-
- /* If no digest buffer is passed, we don't bother doing this: */
- if (digest != (sha2_byte*)0) {
- SHA512_Last((SHA512_CTX*)context);
-
- /* Save the hash data for output: */
-#ifndef WORDS_BIGENDIAN
- {
- /* Convert TO host byte order */
- int j;
- for (j = 0; j < 6; j++) {
- REVERSE64(context->state[j],context->state[j]);
- *d++ = context->state[j];
- }
- }
-#else
- MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);
-#endif
- }
-
- /* Zero out state data */
- MEMSET_BZERO(context, sizeof(*context));
-}
-
-
-/*** SHA-224: *********************************************************/
-
-void solv_SHA224_Init(SHA224_CTX* context) {
- if (context == (SHA224_CTX*)0) {
- return;
- }
- MEMCPY_BCOPY(context->state, sha224_initial_hash_value, SHA256_DIGEST_LENGTH);
- MEMSET_BZERO((char *)context->buffer, SHA224_BLOCK_LENGTH);
- context->bitcount = 0;
-}
-
-void solv_SHA224_Update(SHA224_CTX* context, const sha2_byte* data, size_t len) {
- solv_SHA256_Update((SHA256_CTX*)context, data, len);
-}
-
-void solv_SHA224_Final(sha2_byte digest[], SHA224_CTX* context) {
- sha2_word32 *d = (sha2_word32*)digest;
-
- /* Sanity check: */
- /* assert(context != (SHA224_CTX*)0); */
-
- /* If no digest buffer is passed, we don't bother doing this: */
- if (digest != (sha2_byte*)0) {
- SHA256_Last(context);
-
-#ifndef WORDS_BIGENDIAN
- {
- /* Convert TO host byte order */
- int j;
- for (j = 0; j < 7; j++) {
- REVERSE32(context->state[j],context->state[j]);
- *d++ = context->state[j];
- }
- }
-#else
- MEMCPY_BCOPY(d, context->state, SHA224_DIGEST_LENGTH);
-#endif
- }
-
- /* Clean up state data: */
- MEMSET_BZERO(context, sizeof(*context));
-}
+++ /dev/null
-/*
- * FILE: sha2.h
- * AUTHOR: Aaron D. Gifford <me@aarongifford.com>
- *
- * Copyright (c) 2000-2001, Aaron D. Gifford
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holder nor the names of contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
- */
-
-#include <inttypes.h>
-
-
-/*** SHA-256/384/512 Various Length Definitions ***********************/
-#define SHA224_BLOCK_LENGTH 64
-#define SHA224_DIGEST_LENGTH 28
-#define SHA256_BLOCK_LENGTH 64
-#define SHA256_DIGEST_LENGTH 32
-#define SHA384_BLOCK_LENGTH 128
-#define SHA384_DIGEST_LENGTH 48
-#define SHA512_BLOCK_LENGTH 128
-#define SHA512_DIGEST_LENGTH 64
-
-
-/*** SHA-256/384/512 Context Structures *******************************/
-/* NOTE: If your architecture does not define either u_intXX_t types or
- * uintXX_t (from inttypes.h), you may need to define things by hand
- * for your system:
- */
-typedef struct _SHA256_CTX {
- uint32_t state[8];
- uint64_t bitcount;
- uint32_t buffer[SHA256_BLOCK_LENGTH/4];
-} SHA256_CTX;
-typedef struct _SHA512_CTX {
- uint64_t state[8];
- uint64_t bitcount[2];
- uint64_t buffer[SHA512_BLOCK_LENGTH/8];
-} SHA512_CTX;
-
-typedef SHA256_CTX SHA224_CTX;
-typedef SHA512_CTX SHA384_CTX;
-
-
-/*** SHA-224/256/384/512 Function Prototypes ******************************/
-void solv_SHA224_Init(SHA224_CTX *);
-void solv_SHA224_Update(SHA224_CTX*, const uint8_t*, size_t);
-void solv_SHA224_Final(uint8_t[SHA224_DIGEST_LENGTH], SHA224_CTX*);
-
-void solv_SHA256_Init(SHA256_CTX *);
-void solv_SHA256_Update(SHA256_CTX*, const uint8_t*, size_t);
-void solv_SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
-
-void solv_SHA384_Init(SHA384_CTX*);
-void solv_SHA384_Update(SHA384_CTX*, const uint8_t*, size_t);
-void solv_SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
-
-void solv_SHA512_Init(SHA512_CTX*);
-void solv_SHA512_Update(SHA512_CTX*, const uint8_t*, size_t);
-void solv_SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+++ /dev/null
-/*
- * Copyright (c) 2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * solvable.c
- *
- * set/retrieve data from solvables
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "util.h"
-#include "policy.h"
-#include "poolvendor.h"
-#include "chksum.h"
-#include "linkedpkg.h"
-
-const char *
-pool_solvable2str(Pool *pool, Solvable *s)
-{
- const char *n, *e, *a;
- int nl, el, al;
- char *p;
- n = pool_id2str(pool, s->name);
- e = pool_id2str(pool, s->evr);
- /* XXX: may want to skip the epoch here */
- a = pool_id2str(pool, s->arch);
- nl = strlen(n);
- el = strlen(e);
- al = strlen(a);
- if (pool->havedistepoch)
- {
- /* strip the distepoch from the evr */
- const char *de = strrchr(e, '-');
- if (de && (de = strchr(de, ':')) != 0)
- el = de - e;
- }
- p = pool_alloctmpspace(pool, nl + el + al + 3);
- strcpy(p, n);
- if (el)
- {
- p[nl++] = '-';
- strncpy(p + nl, e, el);
- p[nl + el] = 0;
- }
- if (al)
- {
- p[nl + el] = pool->disttype == DISTTYPE_HAIKU ? '-' : '.';
- strcpy(p + nl + el + 1, a);
- }
- return p;
-}
-
-Id
-solvable_lookup_type(Solvable *s, Id keyname)
-{
- if (!s->repo)
- return 0;
- return repo_lookup_type(s->repo, s - s->repo->pool->solvables, keyname);
-}
-
-Id
-solvable_lookup_id(Solvable *s, Id keyname)
-{
- if (!s->repo)
- return 0;
- return repo_lookup_id(s->repo, s - s->repo->pool->solvables, keyname);
-}
-
-int
-solvable_lookup_idarray(Solvable *s, Id keyname, Queue *q)
-{
- if (!s->repo)
- {
- queue_empty(q);
- return 0;
- }
- return repo_lookup_idarray(s->repo, s - s->repo->pool->solvables, keyname, q);
-}
-
-int
-solvable_lookup_deparray(Solvable *s, Id keyname, Queue *q, Id marker)
-{
- if (!s->repo)
- {
- queue_empty(q);
- return 0;
- }
- return repo_lookup_deparray(s->repo, s - s->repo->pool->solvables, keyname, q, marker);
-}
-
-static const char *
-solvable_lookup_str_joinarray(Solvable *s, Id keyname, const char *joinstr)
-{
- Queue q;
- Id qbuf[10];
- char *str = 0;
-
- queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
- if (solvable_lookup_idarray(s, keyname, &q) && q.count)
- {
- Pool *pool = s->repo->pool;
- int i;
- str = pool_tmpjoin(pool, pool_id2str(pool, q.elements[0]), 0, 0);
- for (i = 1; i < q.count; i++)
- str = pool_tmpappend(pool, str, joinstr, pool_id2str(pool, q.elements[i]));
- }
- queue_free(&q);
- return str;
-}
-
-const char *
-solvable_lookup_str(Solvable *s, Id keyname)
-{
- const char *str;
- if (!s->repo)
- return 0;
- str = repo_lookup_str(s->repo, s - s->repo->pool->solvables, keyname);
- if (!str && (keyname == SOLVABLE_LICENSE || keyname == SOLVABLE_GROUP))
- str = solvable_lookup_str_joinarray(s, keyname, ", ");
- return str;
-}
-
-static const char *
-solvable_lookup_str_base(Solvable *s, Id keyname, Id basekeyname, int usebase)
-{
- Pool *pool;
- const char *str, *basestr;
- Id p, pp, name;
- Solvable *s2;
- int pass;
-
- if (!s->repo)
- return 0;
- pool = s->repo->pool;
- str = solvable_lookup_str(s, keyname);
- if (str || keyname == basekeyname)
- return str;
- basestr = solvable_lookup_str(s, basekeyname);
- if (!basestr)
- return 0;
- /* search for a solvable with same name and same base that has the
- * translation */
- if (!pool->whatprovides)
- return usebase ? basestr : 0;
- name = s->name;
- /* we do this in two passes, first same vendor, then all other vendors */
- for (pass = 0; pass < 2; pass++)
- {
- FOR_PROVIDES(p, pp, name)
- {
- s2 = pool->solvables + p;
- if (s2->name != name)
- continue;
- if ((s->vendor == s2->vendor) != (pass == 0))
- continue;
- str = solvable_lookup_str(s2, basekeyname);
- if (!str || strcmp(str, basestr))
- continue;
- str = solvable_lookup_str(s2, keyname);
- if (str)
- return str;
- }
-#ifdef ENABLE_LINKED_PKGS
- /* autopattern/product translation magic */
- if (pass)
- {
- const char *n = pool_id2str(pool, name);
- if (*n == 'p')
- {
- if (!strncmp("pattern:", n, 8) && (name = find_autopattern_name(pool, s)) != 0)
- pass = -1;
- if (!strncmp("product:", n, 8) && (name = find_autoproduct_name(pool, s)) != 0)
- pass = -1;
- }
- }
-#endif
- }
- return usebase ? basestr : 0;
-}
-
-const char *
-solvable_lookup_str_poollang(Solvable *s, Id keyname)
-{
- Pool *pool;
- int i, cols;
- const char *str;
- Id *row;
-
- if (!s->repo)
- return 0;
- pool = s->repo->pool;
- if (!pool->nlanguages)
- return solvable_lookup_str(s, keyname);
- cols = pool->nlanguages + 1;
- if (!pool->languagecache)
- {
- pool->languagecache = solv_calloc(cols * ID_NUM_INTERNAL, sizeof(Id));
- pool->languagecacheother = 0;
- }
- if (keyname >= ID_NUM_INTERNAL)
- {
- row = pool->languagecache + ID_NUM_INTERNAL * cols;
- for (i = 0; i < pool->languagecacheother; i++, row += cols)
- if (*row == keyname)
- break;
- if (i >= pool->languagecacheother)
- {
- pool->languagecache = solv_realloc2(pool->languagecache, pool->languagecacheother + 1, cols * sizeof(Id));
- row = pool->languagecache + cols * (ID_NUM_INTERNAL + pool->languagecacheother++);
- *row = keyname;
- }
- }
- else
- row = pool->languagecache + keyname * cols;
- row++; /* skip keyname */
- for (i = 0; i < pool->nlanguages; i++, row++)
- {
- if (!*row)
- *row = pool_id2langid(pool, keyname, pool->languages[i], 1);
- str = solvable_lookup_str_base(s, *row, keyname, 0);
- if (str)
- return str;
- }
- return solvable_lookup_str(s, keyname);
-}
-
-const char *
-solvable_lookup_str_lang(Solvable *s, Id keyname, const char *lang, int usebase)
-{
- if (s->repo)
- {
- Id id = pool_id2langid(s->repo->pool, keyname, lang, 0);
- if (id)
- return solvable_lookup_str_base(s, id, keyname, usebase);
- if (!usebase)
- return 0;
- }
- return solvable_lookup_str(s, keyname);
-}
-
-unsigned long long
-solvable_lookup_num(Solvable *s, Id keyname, unsigned long long notfound)
-{
- if (!s->repo)
- return notfound;
- return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, notfound);
-}
-
-unsigned int
-solvable_lookup_sizek(Solvable *s, Id keyname, unsigned int notfound)
-{
- unsigned long long size;
- if (!s->repo)
- return notfound;
- size = solvable_lookup_num(s, keyname, (unsigned long long)notfound << 10);
- return (unsigned int)((size + 1023) >> 10);
-}
-
-int
-solvable_lookup_void(Solvable *s, Id keyname)
-{
- if (!s->repo)
- return 0;
- return repo_lookup_void(s->repo, s - s->repo->pool->solvables, keyname);
-}
-
-int
-solvable_lookup_bool(Solvable *s, Id keyname)
-{
- if (!s->repo)
- return 0;
- /* historic nonsense: there are two ways of storing a bool, as num == 1 or void. test both. */
- if (repo_lookup_type(s->repo, s - s->repo->pool->solvables, keyname) == REPOKEY_TYPE_VOID)
- return 1;
- return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, 0) == 1;
-}
-
-const unsigned char *
-solvable_lookup_bin_checksum(Solvable *s, Id keyname, Id *typep)
-{
- Repo *repo = s->repo;
-
- if (!repo)
- {
- *typep = 0;
- return 0;
- }
- return repo_lookup_bin_checksum(repo, s - repo->pool->solvables, keyname, typep);
-}
-
-const char *
-solvable_lookup_checksum(Solvable *s, Id keyname, Id *typep)
-{
- const unsigned char *chk = solvable_lookup_bin_checksum(s, keyname, typep);
- return chk ? pool_bin2hex(s->repo->pool, chk, solv_chksum_len(*typep)) : 0;
-}
-
-static inline const char *
-evrid2vrstr(Pool *pool, Id evrid)
-{
- const char *p, *evr = pool_id2str(pool, evrid);
- if (!evr)
- return evr;
- for (p = evr; *p >= '0' && *p <= '9'; p++)
- ;
- return p != evr && *p == ':' && p[1] ? p + 1 : evr;
-}
-
-const char *
-solvable_lookup_location(Solvable *s, unsigned int *medianrp)
-{
- Pool *pool;
- int l = 0;
- char *loc;
- const char *mediadir, *mediafile;
-
- if (medianrp)
- *medianrp = 0;
- if (!s->repo)
- return 0;
- pool = s->repo->pool;
- if (medianrp)
- *medianrp = solvable_lookup_num(s, SOLVABLE_MEDIANR, 0);
- if (solvable_lookup_void(s, SOLVABLE_MEDIADIR))
- mediadir = pool_id2str(pool, s->arch);
- else
- mediadir = solvable_lookup_str(s, SOLVABLE_MEDIADIR);
- if (mediadir)
- l = strlen(mediadir) + 1;
- if (solvable_lookup_void(s, SOLVABLE_MEDIAFILE))
- {
- const char *name, *evr, *arch;
- name = pool_id2str(pool, s->name);
- evr = evrid2vrstr(pool, s->evr);
- arch = pool_id2str(pool, s->arch);
- /* name-vr.arch.rpm */
- loc = pool_alloctmpspace(pool, l + strlen(name) + strlen(evr) + strlen(arch) + 7);
- if (mediadir)
- sprintf(loc, "%s/%s-%s.%s.rpm", mediadir, name, evr, arch);
- else
- sprintf(loc, "%s-%s.%s.rpm", name, evr, arch);
- }
- else
- {
- mediafile = solvable_lookup_str(s, SOLVABLE_MEDIAFILE);
- if (!mediafile)
- return 0;
- loc = pool_alloctmpspace(pool, l + strlen(mediafile) + 1);
- if (mediadir)
- sprintf(loc, "%s/%s", mediadir, mediafile);
- else
- strcpy(loc, mediafile);
- }
- return loc;
-}
-
-const char *
-solvable_get_location(Solvable *s, unsigned int *medianrp)
-{
- const char *loc = solvable_lookup_location(s, medianrp);
- if (medianrp && *medianrp == 0)
- *medianrp = 1; /* compat, to be removed */
- return loc;
-}
-
-const char *
-solvable_lookup_sourcepkg(Solvable *s)
-{
- Pool *pool;
- const char *evr, *name;
- Id archid;
-
- if (!s->repo)
- return 0;
- pool = s->repo->pool;
- if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
- name = pool_id2str(pool, s->name);
- else
- name = solvable_lookup_str(s, SOLVABLE_SOURCENAME);
- if (!name)
- return 0;
- archid = solvable_lookup_id(s, SOLVABLE_SOURCEARCH);
- if (solvable_lookup_void(s, SOLVABLE_SOURCEEVR))
- evr = evrid2vrstr(pool, s->evr);
- else
- evr = solvable_lookup_str(s, SOLVABLE_SOURCEEVR);
- if (archid == ARCH_SRC || archid == ARCH_NOSRC)
- {
- char *str;
- str = pool_tmpjoin(pool, name, evr ? "-" : 0, evr);
- str = pool_tmpappend(pool, str, ".", pool_id2str(pool, archid));
- return pool_tmpappend(pool, str, ".rpm", 0);
- }
- else
- return name; /* FIXME */
-}
-
-
-/*****************************************************************************/
-
-static inline Id dep2name(Pool *pool, Id dep)
-{
- while (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- dep = rd->name;
- }
- return dep;
-}
-
-static int providedbyinstalled_multiversion(Pool *pool, Map *installed, Id n, Id con)
-{
- Id p, pp;
- Solvable *sn = pool->solvables + n;
-
- FOR_PROVIDES(p, pp, sn->name)
- {
- Solvable *s = pool->solvables + p;
- if (s->name != sn->name || s->arch != sn->arch)
- continue;
- if (!MAPTST(installed, p))
- continue;
- if (pool_match_nevr(pool, pool->solvables + p, con))
- continue;
- return 1; /* found installed package that doesn't conflict */
- }
- return 0;
-}
-
-static inline int providedbyinstalled(Pool *pool, Map *installed, Id dep, int ispatch, Map *multiversionmap)
-{
- Id p, pp;
- FOR_PROVIDES(p, pp, dep)
- {
- if (p == SYSTEMSOLVABLE)
- return -1;
- if (ispatch && !pool_match_nevr(pool, pool->solvables + p, dep))
- continue;
- if (ispatch && multiversionmap && multiversionmap->size && MAPTST(multiversionmap, p) && ISRELDEP(dep))
- if (providedbyinstalled_multiversion(pool, installed, p, dep))
- continue;
- if (MAPTST(installed, p))
- return 1;
- }
- return 0;
-}
-
-/*
- * solvable_trivial_installable_map - anwers is a solvable is installable
- * without any other installs/deinstalls.
- * The packages considered to be installed are provided via the
- * installedmap bitmap. A additional "conflictsmap" bitmap providing
- * information about the conflicts of the installed packages can be
- * used for extra speed up. Provide a NULL pointer if you do not
- * have this information.
- * Both maps can be created with pool_create_state_maps() or
- * solver_create_state_maps().
- *
- * returns:
- * 1: solvable is installable without any other package changes
- * 0: solvable is not installable
- * -1: solvable is installable, but doesn't constrain any installed packages
- */
-int
-solvable_trivial_installable_map(Solvable *s, Map *installedmap, Map *conflictsmap, Map *multiversionmap)
-{
- Pool *pool = s->repo->pool;
- Solvable *s2;
- Id p, *dp;
- Id *reqp, req;
- Id *conp, con;
- int r, interesting = 0;
-
- if (conflictsmap && MAPTST(conflictsmap, s - pool->solvables))
- return 0;
- if (s->requires)
- {
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- continue;
- r = providedbyinstalled(pool, installedmap, req, 0, 0);
- if (!r)
- return 0;
- if (r > 0)
- interesting = 1;
- }
- }
- if (s->conflicts)
- {
- int ispatch = 0;
-
- if (!strncmp("patch:", pool_id2str(pool, s->name), 6))
- ispatch = 1;
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- if (providedbyinstalled(pool, installedmap, con, ispatch, multiversionmap))
- {
- if (ispatch && solvable_is_irrelevant_patch(s, installedmap))
- return -1;
- return 0;
- }
- if (!interesting && ISRELDEP(con))
- {
- con = dep2name(pool, con);
- if (providedbyinstalled(pool, installedmap, con, ispatch, multiversionmap))
- interesting = 1;
- }
- }
- if (ispatch && interesting && solvable_is_irrelevant_patch(s, installedmap))
- interesting = 0;
- }
-#if 0
- if (s->repo)
- {
- Id *obsp, obs;
- Repo *installed = 0;
- if (s->obsoletes && s->repo != installed)
- {
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- if (providedbyinstalled(pool, installedmap, obs, 0, 0))
- return 0;
- }
- }
- if (s->repo != installed)
- {
- Id pp;
- FOR_PROVIDES(p, pp, s->name)
- {
- s2 = pool->solvables + p;
- if (s2->repo == installed && s2->name == s->name)
- return 0;
- }
- }
- }
-#endif
- if (!conflictsmap)
- {
- int i;
-
- p = s - pool->solvables;
- for (i = 1; i < pool->nsolvables; i++)
- {
- if (!MAPTST(installedmap, i))
- continue;
- s2 = pool->solvables + i;
- if (!s2->conflicts)
- continue;
- conp = s2->repo->idarraydata + s2->conflicts;
- while ((con = *conp++) != 0)
- {
- dp = pool_whatprovides_ptr(pool, con);
- for (; *dp; dp++)
- if (*dp == p)
- return 0;
- }
- }
- }
- return interesting ? 1 : -1;
-}
-
-/*
- * different interface for solvable_trivial_installable_map, where
- * the information about the installed packages is provided
- * by a queue.
- */
-int
-solvable_trivial_installable_queue(Solvable *s, Queue *installed, Map *multiversionmap)
-{
- Pool *pool = s->repo->pool;
- int i;
- Id p;
- Map installedmap;
- int r;
-
- map_init(&installedmap, pool->nsolvables);
- for (i = 0; i < installed->count; i++)
- {
- p = installed->elements[i];
- if (p > 0) /* makes it work with decisionq */
- MAPSET(&installedmap, p);
- }
- r = solvable_trivial_installable_map(s, &installedmap, 0, multiversionmap);
- map_free(&installedmap);
- return r;
-}
-
-/*
- * different interface for solvable_trivial_installable_map, where
- * the information about the installed packages is provided
- * by a repo containing the installed solvables.
- */
-int
-solvable_trivial_installable_repo(Solvable *s, Repo *installed, Map *multiversionmap)
-{
- Pool *pool = s->repo->pool;
- Id p;
- Solvable *s2;
- Map installedmap;
- int r;
-
- map_init(&installedmap, pool->nsolvables);
- FOR_REPO_SOLVABLES(installed, p, s2)
- MAPSET(&installedmap, p);
- r = solvable_trivial_installable_map(s, &installedmap, 0, multiversionmap);
- map_free(&installedmap);
- return r;
-}
-
-/* FIXME: this mirrors policy_illegal_vendorchange */
-static int
-pool_illegal_vendorchange(Pool *pool, Solvable *s1, Solvable *s2)
-{
- Id v1, v2;
- Id vendormask1, vendormask2;
-
- if (pool->custom_vendorcheck)
- return pool->custom_vendorcheck(pool, s1, s2);
- /* treat a missing vendor as empty string */
- v1 = s1->vendor ? s1->vendor : ID_EMPTY;
- v2 = s2->vendor ? s2->vendor : ID_EMPTY;
- if (v1 == v2)
- return 0;
- vendormask1 = pool_vendor2mask(pool, v1);
- if (!vendormask1)
- return 1; /* can't match */
- vendormask2 = pool_vendor2mask(pool, v2);
- if ((vendormask1 & vendormask2) != 0)
- return 0;
- return 1; /* no class matches */
-}
-
-/* check if this patch is relevant according to the vendor. To bad that patches
- * don't have a vendor, so we need to do some careful repo testing. */
-int
-solvable_is_irrelevant_patch(Solvable *s, Map *installedmap)
-{
- Pool *pool = s->repo->pool;
- Id con, *conp;
- int hadpatchpackage = 0;
-
- if (!s->conflicts)
- return 0;
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- Reldep *rd;
- Id p, pp, p2, pp2;
- if (!ISRELDEP(con))
- continue;
- rd = GETRELDEP(pool, con);
- if (rd->flags != REL_LT)
- continue;
- FOR_PROVIDES(p, pp, con)
- {
- Solvable *si;
- if (!MAPTST(installedmap, p))
- continue;
- si = pool->solvables + p;
- if (!pool_match_nevr(pool, si, con))
- continue;
- FOR_PROVIDES(p2, pp2, rd->name)
- {
- Solvable *s2 = pool->solvables + p2;
- if (!pool_match_nevr(pool, s2, rd->name))
- continue;
- if (pool_match_nevr(pool, s2, con))
- continue; /* does not fulfill patch */
- if (s2->repo == s->repo)
- {
- hadpatchpackage = 1;
- /* ok, we have a package from the patch repo that solves the conflict. check vendor */
- if (si->vendor == s2->vendor)
- return 0;
- if (!pool_illegal_vendorchange(pool, si, s2))
- return 0;
- /* vendor change was illegal, ignore conflict */
- }
- }
- }
- }
- /* if we didn't find a patchpackage don't claim that the patch is irrelevant */
- if (!hadpatchpackage)
- return 0;
- return 1;
-}
-
-/*****************************************************************************/
-
-/*
- * Create maps containing the state of each solvable. Input is a "installed" queue,
- * it contains all solvable ids that are considered to be installed.
- *
- * The created maps can be used for solvable_trivial_installable_map(),
- * pool_calc_duchanges(), pool_calc_installsizechange().
- *
- */
-void
-pool_create_state_maps(Pool *pool, Queue *installed, Map *installedmap, Map *conflictsmap)
-{
- int i;
- Solvable *s;
- Id p, *dp;
- Id *conp, con;
-
- map_init(installedmap, pool->nsolvables);
- if (conflictsmap)
- map_init(conflictsmap, pool->nsolvables);
- for (i = 0; i < installed->count; i++)
- {
- p = installed->elements[i];
- if (p <= 0) /* makes it work with decisionq */
- continue;
- MAPSET(installedmap, p);
- if (!conflictsmap)
- continue;
- s = pool->solvables + p;
- if (!s->conflicts)
- continue;
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- dp = pool_whatprovides_ptr(pool, con);
- for (; *dp; dp++)
- MAPSET(conflictsmap, *dp);
- }
- }
-}
-
-/* Tests if two solvables have identical content. Currently
- * both solvables need to come from the same pool
- */
-
-int
-solvable_identical(Solvable *s1, Solvable *s2)
-{
- unsigned int bt1, bt2;
- Id rq1, rq2;
- Id *reqp;
- if (s1->name != s2->name)
- return 0;
- if (s1->arch != s2->arch)
- return 0;
- if (s1->evr != s2->evr)
- return 0;
-
- /* check vendor, map missing vendor to empty string */
- if ((s1->vendor ? s1->vendor : 1) != (s2->vendor ? s2->vendor : 1))
- {
- /* workaround for bug 881493 */
- if (s1->repo && !strncmp(pool_id2str(s1->repo->pool, s1->name), "product:", 8))
- return 1;
- return 0;
- }
-
- /* looking good, try some fancier stuff */
- /* might also look up the package checksum here */
- bt1 = solvable_lookup_num(s1, SOLVABLE_BUILDTIME, 0);
- bt2 = solvable_lookup_num(s2, SOLVABLE_BUILDTIME, 0);
- if (bt1 && bt2)
- {
- if (bt1 != bt2)
- return 0;
- }
- else
- {
- if (s1->repo)
- {
- /* workaround for bugs 881493 and 885830*/
- const char *n = pool_id2str(s1->repo->pool, s1->name);
- if (!strncmp(n, "product:", 8) || !strncmp(n, "application:", 12))
- return 1;
- }
- /* look at requires in a last attempt to find recompiled packages */
- rq1 = rq2 = 0;
- if (s1->requires)
- for (reqp = s1->repo->idarraydata + s1->requires; *reqp; reqp++)
- rq1 ^= *reqp;
- if (s2->requires)
- for (reqp = s2->repo->idarraydata + s2->requires; *reqp; reqp++)
- rq2 ^= *reqp;
- if (rq1 != rq2)
- return 0;
- }
- return 1;
-}
-
-/* return the self provide dependency of a solvable */
-Id
-solvable_selfprovidedep(Solvable *s)
-{
- Pool *pool;
- Reldep *rd;
- Id prov, *provp;
-
- if (!s->repo)
- return s->name;
- pool = s->repo->pool;
- if (s->provides)
- {
- provp = s->repo->idarraydata + s->provides;
- while ((prov = *provp++) != 0)
- {
- if (!ISRELDEP(prov))
- continue;
- rd = GETRELDEP(pool, prov);
- if (rd->name == s->name && rd->evr == s->evr && rd->flags == REL_EQ)
- return prov;
- }
- }
- return pool_rel2id(pool, s->name, s->evr, REL_EQ, 1);
-}
-
-/* setter functions, simply call the repo variants */
-void
-solvable_set_id(Solvable *s, Id keyname, Id id)
-{
- repo_set_id(s->repo, s - s->repo->pool->solvables, keyname, id);
-}
-
-void
-solvable_set_num(Solvable *s, Id keyname, unsigned long long num)
-{
- repo_set_num(s->repo, s - s->repo->pool->solvables, keyname, num);
-}
-
-void
-solvable_set_str(Solvable *s, Id keyname, const char *str)
-{
- repo_set_str(s->repo, s - s->repo->pool->solvables, keyname, str);
-}
-
-void
-solvable_set_poolstr(Solvable *s, Id keyname, const char *str)
-{
- repo_set_poolstr(s->repo, s - s->repo->pool->solvables, keyname, str);
-}
-
-void
-solvable_add_poolstr_array(Solvable *s, Id keyname, const char *str)
-{
- repo_add_poolstr_array(s->repo, s - s->repo->pool->solvables, keyname, str);
-}
-
-void
-solvable_add_idarray(Solvable *s, Id keyname, Id id)
-{
- repo_add_idarray(s->repo, s - s->repo->pool->solvables, keyname, id);
-}
-
-void
-solvable_add_deparray(Solvable *s, Id keyname, Id dep, Id marker)
-{
- repo_add_deparray(s->repo, s - s->repo->pool->solvables, keyname, dep, marker);
-}
-
-void
-solvable_set_idarray(Solvable *s, Id keyname, Queue *q)
-{
- repo_set_idarray(s->repo, s - s->repo->pool->solvables, keyname, q);
-}
-
-void
-solvable_set_deparray(Solvable *s, Id keyname, Queue *q, Id marker)
-{
- repo_set_deparray(s->repo, s - s->repo->pool->solvables, keyname, q, marker);
-}
-
-void
-solvable_unset(Solvable *s, Id keyname)
-{
- repo_unset(s->repo, s - s->repo->pool->solvables, keyname);
-}
-
-/* return true if a dependency intersects dep in the keyname array */
-int
-solvable_matchesdep(Solvable *s, Id keyname, Id dep, int marker)
-{
- int i;
- Pool *pool = s->repo->pool;
- Queue q;
- queue_init(&q);
- solvable_lookup_deparray(s, keyname, &q, marker);
- for (i = 0; i < q.count; i++)
- if (pool_match_dep(pool, q.elements[i], dep))
- break;
- i = i == q.count ? 0 : 1;
- queue_free(&q);
- return i;
-}
+++ /dev/null
-/*
- * Copyright (c) 2007-2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * solvable.h
- *
- * A solvable represents an object with name-epoch:version-release.arch
- * and dependencies
- */
-
-#ifndef LIBSOLV_SOLVABLE_H
-#define LIBSOLV_SOLVABLE_H
-
-#include "pooltypes.h"
-#include "queue.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct _Repo;
-
-typedef struct _Solvable {
- Id name;
- Id arch;
- Id evr; /* epoch:version-release */
- Id vendor;
-
- struct _Repo *repo; /* repo we belong to */
-
- /* dependencies are offsets into repo->idarraydata */
- Offset provides; /* terminated with Id 0 */
- Offset obsoletes;
- Offset conflicts;
-
- Offset requires;
- Offset recommends;
- Offset suggests;
-
- Offset supplements;
- Offset enhances;
-
-} Solvable;
-
-/* lookup functions */
-Id solvable_lookup_type(Solvable *s, Id keyname);
-Id solvable_lookup_id(Solvable *s, Id keyname);
-unsigned long long solvable_lookup_num(Solvable *s, Id keyname, unsigned long long notfound);
-unsigned int solvable_lookup_sizek(Solvable *s, Id keyname, unsigned int notfound);
-const char *solvable_lookup_str(Solvable *s, Id keyname);
-const char *solvable_lookup_str_poollang(Solvable *s, Id keyname);
-const char *solvable_lookup_str_lang(Solvable *s, Id keyname, const char *lang, int usebase);
-int solvable_lookup_bool(Solvable *s, Id keyname);
-int solvable_lookup_void(Solvable *s, Id keyname);
-const char *solvable_get_location(Solvable *s, unsigned int *medianrp); /* old name */
-const char *solvable_lookup_location(Solvable *s, unsigned int *medianrp);
-const char *solvable_lookup_sourcepkg(Solvable *s);
-const unsigned char *solvable_lookup_bin_checksum(Solvable *s, Id keyname, Id *typep);
-const char *solvable_lookup_checksum(Solvable *s, Id keyname, Id *typep);
-int solvable_lookup_idarray(Solvable *s, Id keyname, Queue *q);
-int solvable_lookup_deparray(Solvable *s, Id keyname, Queue *q, Id marker);
-
-/* setter functions */
-void solvable_set_id(Solvable *s, Id keyname, Id id);
-void solvable_set_num(Solvable *s, Id keyname, unsigned long long num);
-void solvable_set_str(Solvable *s, Id keyname, const char *str);
-void solvable_set_poolstr(Solvable *s, Id keyname, const char *str);
-void solvable_add_poolstr_array(Solvable *s, Id keyname, const char *str);
-void solvable_add_idarray(Solvable *s, Id keyname, Id id);
-void solvable_add_deparray(Solvable *s, Id keyname, Id dep, Id marker);
-void solvable_set_idarray(Solvable *s, Id keyname, Queue *q);
-void solvable_set_deparray(Solvable *s, Id keyname, Queue *q, Id marker);
-void solvable_unset(Solvable *s, Id keyname);
-
-int solvable_identical(Solvable *s1, Solvable *s2);
-Id solvable_selfprovidedep(Solvable *s);
-int solvable_matchesdep(Solvable *s, Id keyname, Id dep, int marker);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_SOLVABLE_H */
+++ /dev/null
-/*
- * Copyright (c) 2007-2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * solver.c
- *
- * SAT based dependency solver
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-
-#include "solver.h"
-#include "solver_private.h"
-#include "bitmap.h"
-#include "pool.h"
-#include "util.h"
-#include "policy.h"
-#include "poolarch.h"
-#include "solverdebug.h"
-#include "cplxdeps.h"
-#include "linkedpkg.h"
-
-#define RULES_BLOCK 63
-
-
-/********************************************************************
- *
- * dependency check helpers
- *
- */
-
-/*-------------------------------------------------------------------
- * handle split provides
- *
- * a splitprovides dep looks like
- * namespace:splitprovides(pkg REL_WITH path)
- * and is only true if pkg is installed and contains the specified path.
- * we also make sure that pkg is selected for an update, otherwise the
- * update would always be forced onto the user.
- * Map m is the map used when called from dep_possible.
- */
-
-static int
-solver_is_updating(Solver *solv, Id p)
-{
- /* check if the update rule is true */
- Pool *pool = solv->pool;
- Rule *r;
- Id l, pp;
- if (solv->decisionmap[p] >= 0)
- return 0; /* old package stayed */
- r = solv->rules + solv->updaterules + (p - solv->installed->start);
- FOR_RULELITERALS(l, pp, r)
- if (l > 0 && l != p && solv->decisionmap[l] > 0)
- return 1;
- return 0;
-}
-
-int
-solver_splitprovides(Solver *solv, Id dep, Map *m)
-{
- Pool *pool = solv->pool;
- Id p, pp;
- Reldep *rd;
- Solvable *s;
-
- if (!solv->dosplitprovides || !solv->installed)
- return 0;
- if (!ISRELDEP(dep))
- return 0;
- rd = GETRELDEP(pool, dep);
- if (rd->flags != REL_WITH)
- return 0;
- /*
- * things are a bit tricky here if pool->addedprovides == 1, because most split-provides are in
- * a non-standard location. If we simply call pool_whatprovides, we'll drag in the complete
- * file list. Instead we rely on pool_addfileprovides ignoring the addfileprovidesfiltered flag
- * for installed packages and check the lazywhatprovidesq (ignoring the REL_WITH part, but
- * we filter the package name further down anyway).
- */
- if (pool->addedfileprovides == 1 && !ISRELDEP(rd->evr) && !pool->whatprovides[rd->evr])
- pp = pool_searchlazywhatprovidesq(pool, rd->evr);
- else
- pp = pool_whatprovides(pool, dep);
- while ((p = pool->whatprovidesdata[pp++]) != 0)
- {
- /* here we have packages that provide the correct name and contain the path,
- * now do extra filtering */
- s = pool->solvables + p;
- if (s->repo != solv->installed || s->name != rd->name)
- continue;
- /* check if the package is updated. if m is set, we're called from dep_possible */
- if (m || solver_is_updating(solv, p))
- return 1;
- }
- return 0;
-}
-
-
-/* mirrors solver_dep_fulfilled, but returns 2 if a new package
- * was involved */
-static int
-solver_dep_fulfilled_alreadyinstalled(Solver *solv, Id dep)
-{
- Pool *pool = solv->pool;
- Id p, pp;
- int r;
-
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags == REL_COND)
- {
- int r1, r2;
- if (ISRELDEP(rd->evr))
- {
- Reldep *rd2 = GETRELDEP(pool, rd->evr);
- if (rd2->flags == REL_ELSE)
- {
- r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name);
- if (r1)
- {
- r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
- return r2 && r1 == 2 ? 2 : r2;
- }
- return solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr);
- }
- }
- r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
- r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
- if (!r1 && !r2)
- return 0;
- return r1 == 2 ? 2 : 1;
- }
- if (rd->flags == REL_AND)
- {
- int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
- if (!r1)
- return 0;
- r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
- if (!r2)
- return 0;
- return r1 == 2 || r2 == 2 ? 2 : 1;
- }
- if (rd->flags == REL_OR)
- {
- int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
- r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
- if (!r1 && !r2)
- return 0;
- return r1 == 2 || r2 == 2 ? 2 : 1;
- }
- if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
- return solver_splitprovides(solv, rd->evr, 0);
- if (rd->flags == REL_NAMESPACE && solv->installsuppdepq)
- {
- Queue *q = solv->installsuppdepq;
- int i;
- for (i = 0; i < q->count; i++)
- if (q->elements[i] == dep || q->elements[i] == rd->name)
- return 2;
- }
- }
- r = 0;
- FOR_PROVIDES(p, pp, dep)
- if (solv->decisionmap[p] > 0)
- {
- Solvable *s = pool->solvables + p;
- if (s->repo && s->repo != solv->installed)
- return 2;
- r = 1;
- }
- return r;
-}
-
-static int
-solver_is_supplementing_alreadyinstalled(Solver *solv, Solvable *s)
-{
- Id sup, *supp;
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (solver_dep_fulfilled_alreadyinstalled(solv, sup) == 2)
- return 1;
- return 0;
-}
-
-static Id
-autouninstall(Solver *solv, Id *problem)
-{
- Pool *pool = solv->pool;
- int i;
- int lastfeature = 0, lastupdate = 0;
- Id v;
- Id extraflags = -1;
- Map *m = 0;
-
- if (!solv->allowuninstall && !solv->allowuninstall_all)
- {
- if (!solv->allowuninstallmap.size)
- return 0; /* why did we get called? */
- m = &solv->allowuninstallmap;
- }
- for (i = 0; (v = problem[i]) != 0; i++)
- {
- if (v < 0)
- extraflags &= solv->job.elements[-v - 1];
- if (v >= solv->updaterules && v < solv->updaterules_end)
- {
- Rule *r;
- if (m && !MAPTST(m, v - solv->updaterules))
- continue;
- /* check if identical to feature rule, we don't like that (except for orphans) */
- r = solv->rules + solv->featurerules + (v - solv->updaterules);
- if (!r->p)
- {
- /* update rule == feature rule */
- if (v > lastfeature)
- lastfeature = v;
- /* prefer orphaned packages in dup mode */
- if (solv->keep_orphans)
- {
- r = solv->rules + v;
- if (!r->d && r->p == (solv->installed->start + (v - solv->updaterules)))
- {
- lastfeature = v;
- lastupdate = 0;
- break;
- }
- }
- continue;
- }
- if (v > lastupdate)
- lastupdate = v;
- }
- }
- if (!lastupdate && !lastfeature)
- return 0;
- v = lastupdate ? lastupdate : lastfeature;
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "allowuninstall disabling ");
- solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + v);
- solver_disableproblem(solv, v);
- if (extraflags != -1 && (extraflags & SOLVER_CLEANDEPS) != 0 && solv->cleandepsmap.size)
- {
- /* add the package to the updatepkgs list, this will automatically turn
- * on cleandeps mode */
- Id p = solv->rules[v].p;
- if (!solv->cleandeps_updatepkgs)
- {
- solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
- queue_init(solv->cleandeps_updatepkgs);
- }
- if (p > 0)
- {
- int oldupdatepkgscnt = solv->cleandeps_updatepkgs->count;
- queue_pushunique(solv->cleandeps_updatepkgs, p);
- if (solv->cleandeps_updatepkgs->count != oldupdatepkgscnt)
- solver_disablepolicyrules(solv);
- }
- }
- return v;
-}
-
-/************************************************************************/
-
-/*
- * enable/disable learnt rules
- *
- * we have enabled or disabled some of our rules. We now reenable all
- * of our learnt rules except the ones that were learnt from rules that
- * are now disabled.
- */
-static void
-enabledisablelearntrules(Solver *solv)
-{
- Pool *pool = solv->pool;
- Rule *r;
- Id why, *whyp;
- int i;
-
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "enabledisablelearntrules called\n");
- for (i = solv->learntrules, r = solv->rules + i; i < solv->nrules; i++, r++)
- {
- whyp = solv->learnt_pool.elements + solv->learnt_why.elements[i - solv->learntrules];
- while ((why = *whyp++) != 0)
- {
- assert(why > 0 && why < i);
- if (solv->rules[why].d < 0)
- break;
- }
- /* why != 0: we found a disabled rule, disable the learnt rule */
- if (why && r->d >= 0)
- {
- IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "disabling ");
- solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
- }
- solver_disablerule(solv, r);
- }
- else if (!why && r->d < 0)
- {
- IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "re-enabling ");
- solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
- }
- solver_enablerule(solv, r);
- }
- }
-}
-
-
-/*
- * make assertion rules into decisions
- *
- * Go through rules and add direct assertions to the decisionqueue.
- * If we find a conflict, disable rules and add them to problem queue.
- */
-
-static void
-makeruledecisions(Solver *solv)
-{
- Pool *pool = solv->pool;
- int i, ri, ii;
- Rule *r, *rr;
- Id v, vv;
- int decisionstart;
- int record_proof = 1;
- int oldproblemcount;
- int havedisabled = 0;
-
- /* The system solvable is always installed first */
- assert(solv->decisionq.count == 0);
- queue_push(&solv->decisionq, SYSTEMSOLVABLE);
- queue_push(&solv->decisionq_why, 0);
- solv->decisionmap[SYSTEMSOLVABLE] = 1; /* installed at level '1' */
-
- decisionstart = solv->decisionq.count;
- for (;;)
- {
- /* if we needed to re-run, back up decisions to decisionstart */
- while (solv->decisionq.count > decisionstart)
- {
- v = solv->decisionq.elements[--solv->decisionq.count];
- --solv->decisionq_why.count;
- vv = v > 0 ? v : -v;
- solv->decisionmap[vv] = 0;
- }
-
- /* note that the ruleassertions queue is ordered */
- for (ii = 0; ii < solv->ruleassertions.count; ii++)
- {
- ri = solv->ruleassertions.elements[ii];
- r = solv->rules + ri;
-
- if (havedisabled && ri >= solv->learntrules)
- {
- /* just started with learnt rule assertions. If we have disabled
- * some rules, adapt the learnt rule status */
- enabledisablelearntrules(solv);
- havedisabled = 0;
- }
-
- if (r->d < 0 || !r->p || r->w2) /* disabled, dummy or no assertion */
- continue;
-
- /* do weak rules in phase 2 */
- if (ri < solv->learntrules && solv->weakrulemap.size && MAPTST(&solv->weakrulemap, ri))
- continue;
-
- v = r->p;
- vv = v > 0 ? v : -v;
-
- if (!solv->decisionmap[vv]) /* if not yet decided */
- {
- queue_push(&solv->decisionq, v);
- queue_push(&solv->decisionq_why, r - solv->rules);
- solv->decisionmap[vv] = v > 0 ? 1 : -1;
- IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
- {
- Solvable *s = solv->pool->solvables + vv;
- if (v < 0)
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (assertion)\n", pool_solvable2str(solv->pool, s));
- else
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing %s (assertion)\n", pool_solvable2str(solv->pool, s));
- }
- continue;
- }
-
- /* check against previous decision: is there a conflict? */
- if (v > 0 && solv->decisionmap[vv] > 0) /* ok to install */
- continue;
- if (v < 0 && solv->decisionmap[vv] < 0) /* ok to remove */
- continue;
-
- /*
- * found a conflict!
- *
- * The rule (r) we're currently processing says something
- * different (v = r->p) than a previous decision (decisionmap[abs(v)])
- * on this literal
- */
-
- if (ri >= solv->learntrules)
- {
- /* conflict with a learnt rule */
- /* can happen when packages cannot be installed for multiple reasons. */
- /* we disable the learnt rule in this case */
- /* (XXX: we should really call analyze_unsolvable_rule here!) */
- solver_disablerule(solv, r);
- continue;
- }
-
- /*
- * find the decision which is the "opposite" of the rule
- */
- for (i = 0; i < solv->decisionq.count; i++)
- if (solv->decisionq.elements[i] == -v)
- break;
- assert(i < solv->decisionq.count); /* assert that we found it */
- oldproblemcount = solv->problems.count;
-
- /*
- * conflict with system solvable ?
- */
- if (v == -SYSTEMSOLVABLE)
- {
- if (record_proof)
- {
- queue_push(&solv->problems, solv->learnt_pool.count);
- queue_push(&solv->learnt_pool, ri);
- queue_push(&solv->learnt_pool, 0);
- }
- else
- queue_push(&solv->problems, 0);
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with system solvable, disabling rule #%d\n", ri);
- if (ri >= solv->jobrules && ri < solv->jobrules_end)
- v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
- else
- v = ri;
- queue_push(&solv->problems, v);
- queue_push(&solv->problems, 0);
- if (v >= solv->featurerules && v < solv->updaterules_end)
- {
- if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
- if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
- {
- solv->problems.count = oldproblemcount;
- havedisabled = 1;
- break; /* start over */
- }
- }
- solver_disableproblem(solv, v);
- havedisabled = 1;
- break; /* start over */
- }
-
- assert(solv->decisionq_why.elements[i] > 0);
-
- /*
- * conflict with a pkg rule ?
- */
- if (solv->decisionq_why.elements[i] < solv->pkgrules_end)
- {
- if (record_proof)
- {
- queue_push(&solv->problems, solv->learnt_pool.count);
- queue_push(&solv->learnt_pool, ri);
- queue_push(&solv->learnt_pool, solv->decisionq_why.elements[i]);
- queue_push(&solv->learnt_pool, 0);
- }
- else
- queue_push(&solv->problems, 0);
- assert(v > 0 || v == -SYSTEMSOLVABLE);
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with pkg rule, disabling rule #%d\n", ri);
- if (ri >= solv->jobrules && ri < solv->jobrules_end)
- v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
- else
- v = ri;
- queue_push(&solv->problems, v);
- queue_push(&solv->problems, 0);
- if (v >= solv->featurerules && v < solv->updaterules_end)
- {
- if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
- if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
- {
- solv->problems.count = oldproblemcount;
- havedisabled = 1;
- break; /* start over */
- }
- }
- solver_disableproblem(solv, v);
- havedisabled = 1;
- break; /* start over */
- }
-
- /*
- * conflict with another job or update/feature rule
- */
-
- /* record proof */
- if (record_proof)
- {
- queue_push(&solv->problems, solv->learnt_pool.count);
- queue_push(&solv->learnt_pool, ri);
- queue_push(&solv->learnt_pool, solv->decisionq_why.elements[i]);
- queue_push(&solv->learnt_pool, 0);
- }
- else
- queue_push(&solv->problems, 0);
-
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflicting update/job assertions over literal %d\n", vv);
-
- /*
- * push all of our rules (can only be feature or job rules)
- * asserting this literal on the problem stack
- */
- for (i = solv->featurerules, rr = solv->rules + i; i < solv->learntrules; i++, rr++)
- {
- if (rr->d < 0 /* disabled */
- || rr->w2) /* or no assertion */
- continue;
- if (rr->p != vv /* not affecting the literal */
- && rr->p != -vv)
- continue;
- if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, i)) /* weak: silently ignore */
- continue;
-
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, " - disabling rule #%d\n", i);
- solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + i);
-
- v = i;
- if (i >= solv->jobrules && i < solv->jobrules_end)
- v = -(solv->ruletojob.elements[i - solv->jobrules] + 1);
- queue_push(&solv->problems, v);
- }
- queue_push(&solv->problems, 0);
-
- if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
- if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
- {
- solv->problems.count = oldproblemcount;
- havedisabled = 1;
- break; /* start over */
- }
-
- for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
- solver_disableproblem(solv, solv->problems.elements[i]);
- havedisabled = 1;
- break; /* start over */
- }
- if (ii < solv->ruleassertions.count)
- continue;
-
- /*
- * phase 2: now do the weak assertions
- */
- if (!solv->weakrulemap.size)
- break; /* no weak rules, no phase 2 */
- for (ii = 0; ii < solv->ruleassertions.count; ii++)
- {
- ri = solv->ruleassertions.elements[ii];
- r = solv->rules + ri;
- if (r->d < 0 || r->w2) /* disabled or no assertion */
- continue;
- if (ri >= solv->learntrules || !MAPTST(&solv->weakrulemap, ri)) /* skip non-weak */
- continue;
- v = r->p;
- vv = v > 0 ? v : -v;
-
- if (!solv->decisionmap[vv]) /* if not yet decided */
- {
- queue_push(&solv->decisionq, v);
- queue_push(&solv->decisionq_why, r - solv->rules);
- solv->decisionmap[vv] = v > 0 ? 1 : -1;
- IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
- {
- Solvable *s = solv->pool->solvables + vv;
- if (v < 0)
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (weak assertion)\n", pool_solvable2str(solv->pool, s));
- else
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing %s (weak assertion)\n", pool_solvable2str(solv->pool, s));
- }
- continue;
- }
- /* check against previous decision: is there a conflict? */
- if (v > 0 && solv->decisionmap[vv] > 0)
- continue;
- if (v < 0 && solv->decisionmap[vv] < 0)
- continue;
-
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "assertion conflict, but I am weak, disabling ");
- solver_printrule(solv, SOLV_DEBUG_UNSOLVABLE, r);
-
- if (ri >= solv->jobrules && ri < solv->jobrules_end)
- v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
- else
- v = ri;
- solver_disableproblem(solv, v);
- if (v < 0)
- solver_reenablepolicyrules(solv, -v);
- havedisabled = 1;
- break; /* start over */
- }
- if (ii == solv->ruleassertions.count)
- break; /* finished! */
- }
-}
-
-
-/********************************************************************/
-/* watches */
-
-
-/*-------------------------------------------------------------------
- * makewatches
- *
- * initial setup for all watches
- */
-
-static void
-makewatches(Solver *solv)
-{
- Rule *r;
- int i;
- int nsolvables = solv->pool->nsolvables;
-
- solv_free(solv->watches);
- /* lower half for removals, upper half for installs */
- solv->watches = solv_calloc(2 * nsolvables, sizeof(Id));
- for (i = 1, r = solv->rules + solv->nrules - 1; i < solv->nrules; i++, r--)
- {
- if (!r->w2) /* assertions do not need watches */
- continue;
-
- /* see addwatches_rule(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 a new learned rule)
- * sets up watches for a single rule
- *
- * see also makewatches() above.
- */
-
-static inline void
-addwatches_rule(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
- */
-
-
-/* shortcuts to check if a literal (positive or negative) assignment
- * evaluates to 'true' or 'false'
- */
-#define DECISIONMAP_TRUE(p) ((p) > 0 ? (decisionmap[p] > 0) : (decisionmap[-p] < 0))
-#define DECISIONMAP_FALSE(p) ((p) > 0 ? (decisionmap[p] < 0) : (decisionmap[-p] > 0))
-#define DECISIONMAP_UNDEF(p) (decisionmap[(p) > 0 ? (p) : -(p)] == 0)
-
-/*-------------------------------------------------------------------
- *
- * propagate
- *
- * make decision and propagate to all rules
- *
- * Evaluate each term affected by the decision (linked through watches).
- * If we find unit rules we make new decisions based on them.
- *
- * return : 0 = everything is OK
- * rule = conflict found in this rule
- */
-
-static Rule *
-propagate(Solver *solv, int level)
-{
- Pool *pool = solv->pool;
- Id *rp, *next_rp; /* rule pointer, next rule pointer in linked list */
- Rule *r; /* rule */
- Id p, pkg, other_watch;
- Id *dp;
- Id *decisionmap = solv->decisionmap;
- Id *watches = solv->watches + pool->nsolvables; /* place ptr in middle */
-
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "----- propagate -----\n");
-
- /* foreach non-propagated decision */
- while (solv->propagate_index < solv->decisionq.count)
- {
- /*
- * 'pkg' was just decided
- * negate because our watches trigger if literal goes FALSE
- */
- pkg = -solv->decisionq.elements[solv->propagate_index++];
-
- IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
- {
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "propagate for decision %d level %d\n", -pkg, level);
- solver_printruleelement(solv, SOLV_DEBUG_PROPAGATE, 0, -pkg);
- }
-
- /* foreach rule where 'pkg' is now FALSE */
- for (rp = watches + pkg; *rp; rp = next_rp)
- {
- r = solv->rules + *rp;
- if (r->d < 0)
- {
- /* rule is disabled, goto next */
- if (pkg == r->w1)
- next_rp = &r->n1;
- else
- next_rp = &r->n2;
- continue;
- }
-
- IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
- {
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE," watch triggered ");
- solver_printrule(solv, SOLV_DEBUG_PROPAGATE, r);
- }
-
- /*
- * 'pkg' was just decided (was set to FALSE), so this rule
- * may now be unit.
- */
- /* find the other watch */
- if (pkg == r->w1)
- {
- other_watch = r->w2;
- next_rp = &r->n1;
- }
- else
- {
- other_watch = r->w1;
- next_rp = &r->n2;
- }
-
- /*
- * if the other watch is true we have nothing to do
- */
- if (DECISIONMAP_TRUE(other_watch))
- continue;
-
- /*
- * The other literal is FALSE or UNDEF
- *
- */
-
- if (r->d)
- {
- /* Not a binary clause, try to move our watch.
- *
- * Go over all literals and find one that is
- * not other_watch
- * and not FALSE
- *
- * (TRUE is also ok, in that case the rule is fulfilled)
- * As speed matters here we do not use the FOR_RULELITERALS
- * macro.
- */
- if (r->p /* we have a 'p' */
- && r->p != other_watch /* which is not watched */
- && !DECISIONMAP_FALSE(r->p)) /* and not FALSE */
- {
- p = r->p;
- }
- else /* go find a 'd' to make 'true' */
- {
- /* foreach p in 'd'
- we just iterate sequentially, doing it in another order just changes the order of decisions, not the decisions itself
- */
- for (dp = pool->whatprovidesdata + r->d; (p = *dp++) != 0;)
- {
- if (p != other_watch /* which is not watched */
- && !DECISIONMAP_FALSE(p)) /* and not FALSE */
- break;
- }
- }
-
- if (p)
- {
- /*
- * if we found some p that is UNDEF or TRUE, move
- * watch to it
- */
- IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
- {
- if (p > 0)
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> move w%d to %s\n", (pkg == r->w1 ? 1 : 2), pool_solvid2str(pool, p));
- else
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> move w%d to !%s\n", (pkg == r->w1 ? 1 : 2), pool_solvid2str(pool, -p));
- }
-
- *rp = *next_rp;
- next_rp = 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;
- }
- /* search failed, thus all unwatched literals are FALSE */
-
- } /* not binary */
-
- /*
- * unit clause found, set literal other_watch to TRUE
- */
-
- if (DECISIONMAP_FALSE(other_watch)) /* check if literal is FALSE */
- return r; /* eek, a conflict! */
-
- IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
- {
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " unit ");
- solver_printrule(solv, SOLV_DEBUG_PROPAGATE, r);
- }
-
- if (other_watch > 0)
- decisionmap[other_watch] = level; /* install! */
- else
- decisionmap[-other_watch] = -level; /* remove! */
-
- queue_push(&solv->decisionq, other_watch);
- queue_push(&solv->decisionq_why, r - solv->rules);
-
- IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
- {
- if (other_watch > 0)
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> decided to install %s\n", pool_solvid2str(pool, other_watch));
- else
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> decided to conflict %s\n", pool_solvid2str(pool, -other_watch));
- }
-
- } /* foreach rule involving 'pkg' */
-
- } /* while we have non-decided decisions */
-
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "----- propagate end-----\n");
-
- return 0; /* all is well */
-}
-
-
-/********************************************************************/
-/* Analysis */
-
-/*-------------------------------------------------------------------
- *
- * revert
- * revert decisionq to a level
- */
-
-static void
-revert(Solver *solv, int level)
-{
- Pool *pool = solv->pool;
- 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;
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "reverting decision %d at %d\n", v, solv->decisionmap[vv]);
- solv->decisionmap[vv] = 0;
- solv->decisionq.count--;
- solv->decisionq_why.count--;
- solv->propagate_index = solv->decisionq.count;
- }
- while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] >= level)
- solv->branches.count -= solv->branches.elements[solv->branches.count - 2];
- if (solv->recommends_index > solv->decisionq.count)
- solv->recommends_index = -1; /* rebuild recommends/suggests maps */
- if (solv->decisionq.count < solv->decisioncnt_jobs)
- solv->decisioncnt_jobs = 0;
- if (solv->decisionq.count < solv->decisioncnt_update)
- solv->decisioncnt_update = 0;
- if (solv->decisionq.count < solv->decisioncnt_keep)
- solv->decisioncnt_keep = 0;
- if (solv->decisionq.count < solv->decisioncnt_resolve)
- solv->decisioncnt_resolve = 0;
- if (solv->decisionq.count < solv->decisioncnt_weak)
- solv->decisioncnt_weak= 0;
- if (solv->decisionq.count < solv->decisioncnt_orphan)
- solv->decisioncnt_orphan = 0;
-}
-
-/*-------------------------------------------------------------------
- *
- * watch2onhighest - put watch2 on literal with highest level
- */
-
-static inline void
-watch2onhighest(Solver *solv, Rule *r)
-{
- int l, wl = 0;
- Id d, v, *dp;
-
- d = r->d < 0 ? -r->d - 1 : r->d;
- if (!d)
- return; /* binary rule, both watches are set */
- dp = solv->pool->whatprovidesdata + 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;
- }
- }
-}
-
-
-/*-------------------------------------------------------------------
- *
- * analyze
- * and learn
- */
-
-static int
-analyze(Solver *solv, int level, Rule *c, Rule **lrp)
-{
- Pool *pool = solv->pool;
- Queue q;
- Rule *r;
- Id q_buf[8];
- int rlevel = 1;
- Map seen; /* global? */
- Id p = 0, pp, v, vv, why;
- int l, i, idx;
- int num = 0, l1num = 0;
- int learnt_why = solv->learnt_pool.count;
- Id *decisionmap = solv->decisionmap;
-
- queue_init_buffer(&q, q_buf, sizeof(q_buf)/sizeof(*q_buf));
-
- POOL_DEBUG(SOLV_DEBUG_ANALYZE, "ANALYZE at %d ----------------------\n", level);
- map_init(&seen, pool->nsolvables);
- idx = solv->decisionq.count;
- for (;;)
- {
- IF_POOLDEBUG (SOLV_DEBUG_ANALYZE)
- solver_printruleclass(solv, SOLV_DEBUG_ANALYZE, c);
- queue_push(&solv->learnt_pool, c - solv->rules);
- FOR_RULELITERALS(v, pp, c)
- {
- if (DECISIONMAP_TRUE(v)) /* the one true literal */
- continue;
- vv = v > 0 ? v : -v;
- if (MAPTST(&seen, vv))
- continue;
- MAPSET(&seen, vv); /* mark that we also need to look at this literal */
- l = solv->decisionmap[vv];
- if (l < 0)
- l = -l;
- if (l == 1)
- l1num++; /* need to do this one in level1 pass */
- else if (l == level)
- num++; /* need to do this one as well */
- else
- {
- queue_push(&q, v); /* not level1 or conflict level, add to new rule */
- if (l > rlevel)
- rlevel = l;
- }
- }
-l1retry:
- if (!num && !--l1num)
- break; /* all literals done */
-
- /* find the next literal to investigate */
- /* (as num + l1num > 0, we know that we'll always find one) */
- for (;;)
- {
- assert(idx > 0);
- v = solv->decisionq.elements[--idx];
- vv = v > 0 ? v : -v;
- if (MAPTST(&seen, vv))
- break;
- }
- MAPCLR(&seen, vv);
-
- if (num && --num == 0)
- {
- /* done with normal literals, now start level 1 literal processing */
- p = -v; /* so that v doesn't get lost */
- if (!l1num)
- break;
- POOL_DEBUG(SOLV_DEBUG_ANALYZE, "got %d involved level 1 decisions\n", l1num);
- /* clear non-l1 bits from seen map */
- for (i = 0; i < q.count; i++)
- {
- v = q.elements[i];
- MAPCLR(&seen, v > 0 ? v : -v);
- }
- /* only level 1 marks left in seen map */
- l1num++; /* as l1retry decrements it */
- goto l1retry;
- }
-
- why = solv->decisionq_why.elements[idx];
- if (why <= 0) /* just in case, maybe for SYSTEMSOLVABLE */
- goto l1retry;
- c = solv->rules + why;
- }
- map_free(&seen);
- assert(p != 0);
- assert(rlevel > 0 && rlevel < level);
- IF_POOLDEBUG (SOLV_DEBUG_ANALYZE)
- {
- POOL_DEBUG(SOLV_DEBUG_ANALYZE, "learned rule for level %d (am %d)\n", rlevel, level);
- solver_printruleelement(solv, SOLV_DEBUG_ANALYZE, 0, p);
- for (i = 0; i < q.count; i++)
- solver_printruleelement(solv, SOLV_DEBUG_ANALYZE, 0, q.elements[i]);
- }
- /* push end marker on learnt reasons stack */
- queue_push(&solv->learnt_pool, 0);
- solv->stats_learned++;
-
- POOL_DEBUG(SOLV_DEBUG_ANALYZE, "reverting decisions (level %d -> %d)\n", level, rlevel);
- level = rlevel;
- revert(solv, level);
- if (q.count < 2)
- {
- Id d = q.count ? q.elements[0] : 0;
- queue_free(&q);
- r = solver_addrule(solv, p, d, 0);
- }
- else
- {
- Id d = pool_queuetowhatprovides(pool, &q);
- queue_free(&q);
- r = solver_addrule(solv, p, 0, d);
- }
- assert(solv->learnt_why.count == (r - solv->rules) - solv->learntrules);
- queue_push(&solv->learnt_why, learnt_why);
- if (r->w2)
- {
- /* needs watches */
- watch2onhighest(solv, r);
- addwatches_rule(solv, r);
- }
- else
- {
- /* rule is an assertion */
- queue_push(&solv->ruleassertions, r - solv->rules);
- }
- *lrp = r;
- return level;
-}
-
-
-/*-------------------------------------------------------------------
- *
- * solver_reset
- *
- * reset all solver decisions
- * called after rules have been enabled/disabled
- */
-
-void
-solver_reset(Solver *solv)
-{
- int i;
- Id v;
-
- /* rewind all decisions */
- for (i = solv->decisionq.count - 1; i >= 0; i--)
- {
- v = solv->decisionq.elements[i];
- solv->decisionmap[v > 0 ? v : -v] = 0;
- }
- queue_empty(&solv->decisionq_why);
- queue_empty(&solv->decisionq);
- solv->recommends_index = -1;
- solv->propagate_index = 0;
- solv->decisioncnt_update = solv->decisioncnt_keep = solv->decisioncnt_resolve = solv->decisioncnt_weak = solv->decisioncnt_orphan = 0;
- queue_empty(&solv->branches);
-
- /* adapt learnt rule status to new set of enabled/disabled rules */
- enabledisablelearntrules(solv);
-}
-
-
-/*-------------------------------------------------------------------
- *
- * analyze_unsolvable_rule
- *
- * recursion helper used by analyze_unsolvable
- */
-
-static void
-analyze_unsolvable_rule(Solver *solv, Rule *r, Id *lastweakp, Map *rseen)
-{
- Pool *pool = solv->pool;
- int i;
- Id why = r - solv->rules;
-
- IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE)
- solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, r);
- if (solv->learntrules && why >= solv->learntrules)
- {
- if (MAPTST(rseen, why - solv->learntrules))
- return;
- MAPSET(rseen, why - solv->learntrules);
- for (i = solv->learnt_why.elements[why - solv->learntrules]; solv->learnt_pool.elements[i]; i++)
- if (solv->learnt_pool.elements[i] > 0)
- analyze_unsolvable_rule(solv, solv->rules + solv->learnt_pool.elements[i], lastweakp, rseen);
- return;
- }
- if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, why))
- if (!*lastweakp || why > *lastweakp)
- *lastweakp = why;
- /* do not add pkg rules to problem */
- if (why < solv->pkgrules_end)
- return;
- /* turn rule into problem */
- if (why >= solv->jobrules && why < solv->jobrules_end)
- why = -(solv->ruletojob.elements[why - solv->jobrules] + 1);
- /* normalize dup/infarch rules */
- if (why > solv->infarchrules && why < solv->infarchrules_end)
- {
- Id name = pool->solvables[-solv->rules[why].p].name;
- while (why > solv->infarchrules && pool->solvables[-solv->rules[why - 1].p].name == name)
- why--;
- }
- if (why > solv->duprules && why < solv->duprules_end)
- {
- Id name = pool->solvables[-solv->rules[why].p].name;
- while (why > solv->duprules && pool->solvables[-solv->rules[why - 1].p].name == name)
- why--;
- }
-
- /* return if problem already countains our rule */
- if (solv->problems.count)
- {
- for (i = solv->problems.count - 1; i >= 0; i--)
- if (solv->problems.elements[i] == 0) /* end of last problem reached? */
- break;
- else if (solv->problems.elements[i] == why)
- return;
- }
- queue_push(&solv->problems, why);
-}
-
-
-/*-------------------------------------------------------------------
- *
- * analyze_unsolvable (called from setpropagatelearn)
- *
- * We know that the problem is not solvable. Record all involved
- * rules (i.e. the "proof") into solv->learnt_pool.
- * Record the learnt pool index and all non-pkg rules into
- * solv->problems. (Our solutions to fix the problems are to
- * disable those rules.)
- *
- * If the proof contains at least one weak rule, we disable the
- * last of them.
- *
- * Otherwise we return -1 if disablerules is not set or disable
- * _all_ of the problem rules and return 0.
- *
- * return: 0 - disabled some rules, try again
- * -1 - hopeless
- */
-
-static int
-analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
-{
- Pool *pool = solv->pool;
- Rule *r;
- Map involved; /* global to speed things up? */
- Map rseen;
- Id pp, v, vv, why;
- int i, idx;
- Id *decisionmap = solv->decisionmap;
- int oldproblemcount;
- int oldlearntpoolcount;
- Id lastweak;
- int record_proof = 1;
-
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "ANALYZE UNSOLVABLE ----------------------\n");
- solv->stats_unsolvable++;
- oldproblemcount = solv->problems.count;
- oldlearntpoolcount = solv->learnt_pool.count;
-
- /* make room for proof index */
- /* must update it later, as analyze_unsolvable_rule would confuse
- * it with a rule index if we put the real value in already */
- queue_push(&solv->problems, 0);
-
- r = cr;
- map_init(&involved, pool->nsolvables);
- map_init(&rseen, solv->learntrules ? solv->nrules - solv->learntrules : 0);
- if (record_proof)
- queue_push(&solv->learnt_pool, r - solv->rules);
- lastweak = 0;
- analyze_unsolvable_rule(solv, r, &lastweak, &rseen);
- FOR_RULELITERALS(v, pp, r)
- {
- if (DECISIONMAP_TRUE(v)) /* the one true literal */
- continue;
- vv = v > 0 ? v : -v;
- MAPSET(&involved, vv);
- }
- idx = solv->decisionq.count;
- while (idx > 0)
- {
- v = solv->decisionq.elements[--idx];
- vv = v > 0 ? v : -v;
- if (!MAPTST(&involved, vv) || vv == SYSTEMSOLVABLE)
- continue;
- why = solv->decisionq_why.elements[idx];
- assert(why > 0);
- if (record_proof)
- queue_push(&solv->learnt_pool, why);
- r = solv->rules + why;
- analyze_unsolvable_rule(solv, r, &lastweak, &rseen);
- FOR_RULELITERALS(v, pp, r)
- {
- if (DECISIONMAP_TRUE(v)) /* the one true literal */
- continue;
- vv = v > 0 ? v : -v;
- MAPSET(&involved, vv);
- }
- }
- map_free(&involved);
- map_free(&rseen);
- queue_push(&solv->problems, 0); /* mark end of this problem */
-
- if (lastweak)
- {
- /* disable last weak rule */
- solv->problems.count = oldproblemcount;
- solv->learnt_pool.count = oldlearntpoolcount;
- if (lastweak >= solv->jobrules && lastweak < solv->jobrules_end)
- v = -(solv->ruletojob.elements[lastweak - solv->jobrules] + 1);
- else
- v = lastweak;
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "disabling ");
- solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + lastweak);
- if (lastweak >= solv->choicerules && lastweak < solv->choicerules_end)
- solver_disablechoicerules(solv, solv->rules + lastweak);
- solver_disableproblem(solv, v);
- if (v < 0)
- solver_reenablepolicyrules(solv, -v);
- solver_reset(solv);
- return 0;
- }
-
- if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
- if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
- {
- solv->problems.count = oldproblemcount;
- solv->learnt_pool.count = oldlearntpoolcount;
- solver_reset(solv);
- return 0;
- }
-
- /* finish proof */
- if (record_proof)
- {
- queue_push(&solv->learnt_pool, 0);
- solv->problems.elements[oldproblemcount] = oldlearntpoolcount;
- }
-
- /* + 2: index + trailing zero */
- if (disablerules && oldproblemcount + 2 < solv->problems.count)
- {
- for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
- solver_disableproblem(solv, solv->problems.elements[i]);
- /* XXX: might want to enable all weak rules again */
- solver_reset(solv);
- return 0;
- }
- POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "UNSOLVABLE\n");
- return -1;
-}
-
-
-/*-------------------------------------------------------------------
- *
- * setpropagatelearn
- *
- * add free decision (solvable to install) to decisionq
- * increase level and propagate decision
- * return if no conflict.
- *
- * in conflict case, analyze conflict rule, add resulting
- * rule to learnt rule set, make decision from learnt
- * rule (always unit) and re-propagate.
- *
- * returns the new solver level or -1 if unsolvable
- *
- */
-
-static int
-setpropagatelearn(Solver *solv, int level, Id decision, int disablerules, Id ruleid)
-{
- Pool *pool = solv->pool;
- Rule *r, *lr;
-
- if (decision)
- {
- level++;
- if (decision > 0)
- solv->decisionmap[decision] = level;
- else
- solv->decisionmap[-decision] = -level;
- queue_push(&solv->decisionq, decision);
- queue_push(&solv->decisionq_why, -ruleid); /* <= 0 -> free decision */
- }
- assert(ruleid >= 0 && level > 0);
- for (;;)
- {
- r = propagate(solv, level);
- if (!r)
- break;
- if (level == 1)
- return analyze_unsolvable(solv, r, disablerules);
- POOL_DEBUG(SOLV_DEBUG_ANALYZE, "conflict with rule #%d\n", (int)(r - solv->rules));
- level = analyze(solv, level, r, &lr);
- /* the new rule is unit by design */
- decision = lr->p;
- solv->decisionmap[decision > 0 ? decision : -decision] = decision > 0 ? level : -level;
- queue_push(&solv->decisionq, decision);
- queue_push(&solv->decisionq_why, lr - solv->rules);
- IF_POOLDEBUG (SOLV_DEBUG_ANALYZE)
- {
- POOL_DEBUG(SOLV_DEBUG_ANALYZE, "decision: ");
- solver_printruleelement(solv, SOLV_DEBUG_ANALYZE, 0, decision);
- POOL_DEBUG(SOLV_DEBUG_ANALYZE, "new rule: ");
- solver_printrule(solv, SOLV_DEBUG_ANALYZE, lr);
- }
- }
- return level;
-}
-
-static void
-reorder_dq_for_jobrules(Solver *solv, int level, Queue *dq)
-{
- Pool *pool = solv->pool;
- int i, j, haveone = 0, dqcount = dq->count;
- int decisionqcount = solv->decisionq.count;
- Id p;
- Solvable *s;
-
- /* at the time we process jobrules the installed packages are not kept yet */
- /* reorder so that "future-supplemented" packages come first */
- FOR_REPO_SOLVABLES(solv->installed, p, s)
- {
- if (MAPTST(&solv->noupdate, p - solv->installed->start))
- continue;
- if (solv->decisionmap[p] == 0)
- {
- if (s->recommends || s->suggests)
- queue_push(&solv->decisionq, p);
- solv->decisionmap[p] = level + 1;
- haveone = 1;
- }
- }
- if (!haveone)
- return;
- policy_update_recommendsmap(solv);
- for (i = 0; i < dqcount; i++)
- {
- p = dq->elements[i];
- if (!(pool->solvables[p].repo == solv->installed || MAPTST(&solv->suggestsmap, p) || solver_is_enhancing(solv, pool->solvables + p)))
- {
- queue_push(dq, p);
- dq->elements[i] = 0;
- }
- }
- dqcount = dq->count;
- for (i = 0; i < dqcount; i++)
- {
- p = dq->elements[i];
- if (p && !(pool->solvables[p].repo == solv->installed || MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
- {
- queue_push(dq, p);
- dq->elements[i] = 0;
- }
- }
- for (i = j = 0; i < dq->count; i++)
- if (dq->elements[i])
- dq->elements[j++] = dq->elements[i];
- queue_truncate(dq, j);
- FOR_REPO_SOLVABLES(solv->installed, p, s)
- if (solv->decisionmap[p] == level + 1)
- solv->decisionmap[p] = 0;
- if (solv->decisionq.count != decisionqcount)
- {
- solv->recommends_index = -1;
- queue_truncate(&solv->decisionq, decisionqcount);
- }
-}
-
-/*-------------------------------------------------------------------
- *
- * branch handling
- */
-
-static void
-createbranch(Solver *solv, int level, Queue *dq, Id p, Id data)
-{
- Pool *pool = solv->pool;
- int i;
- IF_POOLDEBUG (SOLV_DEBUG_POLICY)
- {
- POOL_DEBUG (SOLV_DEBUG_POLICY, "creating a branch:\n");
- for (i = 0; i < dq->count; i++)
- POOL_DEBUG (SOLV_DEBUG_POLICY, " - %s\n", pool_solvid2str(pool, dq->elements[i]));
- }
- queue_push(&solv->branches, -dq->elements[0]);
- for (i = 1; i < dq->count; i++)
- queue_push(&solv->branches, dq->elements[i]);
- queue_push2(&solv->branches, p, data);
- queue_push2(&solv->branches, dq->count + 4, level);
-}
-
-static int
-takebranch(Solver *solv, int pos, int end, const char *msg, int disablerules)
-{
- Pool *pool = solv->pool;
- int level;
- Id p, why;
-#if 0
- {
- int i;
- printf("branch group level %d [%d-%d] %d %d:\n", solv->branches.elements[end - 1], start, end, solv->branches.elements[end - 4], solv->branches.elements[end - 3]);
- for (i = end - solv->branches.elements[end - 2]; i < end - 4; i++)
- printf("%c %c%s\n", i == pos ? 'x' : ' ', solv->branches.elements[i] >= 0 ? ' ' : '-', pool_solvid2str(pool, solv->branches.elements[i] >= 0 ? solv->branches.elements[i] : -solv->branches.elements[i]));
- }
-#endif
- level = solv->branches.elements[end - 1];
- p = solv->branches.elements[pos];
- solv->branches.elements[pos] = -p;
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "%s %d -> %d with %s\n", msg, solv->decisionmap[p], level, pool_solvid2str(pool, p));
- /* hack: set level to zero so that revert does not remove the branch */
- solv->branches.elements[end - 1] = 0;
- revert(solv, level);
- solv->branches.elements[end - 1] = level;
- /* hack: revert simply sets the count, so we can still access the reverted elements */
- why = -solv->decisionq_why.elements[solv->decisionq_why.count];
- assert(why >= 0);
- return setpropagatelearn(solv, level, p, disablerules, why);
-}
-
-/*-------------------------------------------------------------------
- *
- * select and install
- *
- * install best package from the queue. We add an extra package, inst, if
- * provided. See comment in weak install section.
- *
- * returns the new solver level or -1 if unsolvable
- *
- */
-
-static int
-selectandinstall(Solver *solv, int level, Queue *dq, int disablerules, Id ruleid)
-{
- Pool *pool = solv->pool;
- Id p;
-
- if (dq->count > 1)
- policy_filter_unwanted(solv, dq, POLICY_MODE_CHOOSE);
- /* if we're resolving job rules and didn't resolve the installed packages yet,
- * do some special supplements ordering */
- if (dq->count > 1 && ruleid >= solv->jobrules && ruleid < solv->jobrules_end && solv->installed && !solv->focus_installed)
- reorder_dq_for_jobrules(solv, level, dq);
- /* if we have multiple candidates we open a branch */
- if (dq->count > 1)
- createbranch(solv, level, dq, 0, ruleid);
- p = dq->elements[0];
- POOL_DEBUG(SOLV_DEBUG_POLICY, "installing %s\n", pool_solvid2str(pool, p));
- return setpropagatelearn(solv, level, p, disablerules, ruleid);
-}
-
-
-/********************************************************************/
-/* Main solver interface */
-
-
-/*-------------------------------------------------------------------
- *
- * solver_create
- * create solver structure
- *
- * pool: all available solvables
- * installed: installed Solvables
- *
- *
- * Upon solving, rules are created to flag the Solvables
- * of the 'installed' Repo as installed.
- */
-
-Solver *
-solver_create(Pool *pool)
-{
- Solver *solv;
- solv = (Solver *)solv_calloc(1, sizeof(Solver));
- solv->pool = pool;
- solv->installed = pool->installed;
-
- solv->allownamechange = 1;
-
- solv->dup_allowdowngrade = 1;
- solv->dup_allownamechange = 1;
- solv->dup_allowarchchange = 1;
- solv->dup_allowvendorchange = 1;
-
- solv->keepexplicitobsoletes = pool->noobsoletesmultiversion ? 0 : 1;
-
- queue_init(&solv->ruletojob);
- queue_init(&solv->decisionq);
- queue_init(&solv->decisionq_why);
- queue_init(&solv->problems);
- queue_init(&solv->orphaned);
- queue_init(&solv->learnt_why);
- queue_init(&solv->learnt_pool);
- queue_init(&solv->branches);
- queue_init(&solv->weakruleq);
- queue_init(&solv->ruleassertions);
- queue_init(&solv->addedmap_deduceq);
-
- queue_push(&solv->learnt_pool, 0); /* so that 0 does not describe a proof */
-
- map_init(&solv->recommendsmap, pool->nsolvables);
- map_init(&solv->suggestsmap, pool->nsolvables);
- map_init(&solv->noupdate, solv->installed ? solv->installed->end - solv->installed->start : 0);
- solv->recommends_index = 0;
-
- solv->decisionmap = (Id *)solv_calloc(pool->nsolvables, sizeof(Id));
- solv->nrules = 1;
- solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
- memset(solv->rules, 0, sizeof(Rule));
-
- return solv;
-}
-
-
-static int
-resolve_jobrules(Solver *solv, int level, int disablerules, Queue *dq)
-{
- Pool *pool = solv->pool;
- int oldlevel = level;
- int i, olevel;
- Rule *r;
-
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving job rules\n");
- if (!solv->decisioncnt_jobs)
- solv->decisioncnt_jobs = solv->decisionq.count;
- for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++)
- {
- Id l, pp;
- if (r->d < 0) /* ignore disabled rules */
- continue;
- queue_empty(dq);
- FOR_RULELITERALS(l, pp, r)
- {
- if (l < 0)
- {
- if (solv->decisionmap[-l] <= 0)
- break;
- }
- else
- {
- if (solv->decisionmap[l] > 0)
- break;
- if (solv->decisionmap[l] == 0)
- queue_push(dq, l);
- }
- }
- if (l || !dq->count)
- continue;
- /* prune to installed if not updating */
- if (dq->count > 1 && solv->installed && !solv->updatemap_all &&
- !(solv->job.elements[solv->ruletojob.elements[i - solv->jobrules]] & SOLVER_ORUPDATE))
- {
- int j, k;
- for (j = k = 0; j < dq->count; j++)
- {
- Solvable *s = pool->solvables + dq->elements[j];
- if (s->repo == solv->installed)
- {
- dq->elements[k++] = dq->elements[j];
- if (solv->updatemap.size && MAPTST(&solv->updatemap, dq->elements[j] - solv->installed->start))
- {
- k = 0; /* package wants to be updated, do not prune */
- break;
- }
- }
- }
- if (k)
- dq->count = k;
- }
- olevel = level;
- level = selectandinstall(solv, level, dq, disablerules, i);
- if (level <= olevel)
- {
- if (level == olevel)
- {
- i--;
- r--;
- continue; /* try something else */
- }
- if (level < oldlevel)
- return level;
- /* redo from start of jobrules */
- i = solv->jobrules - 1;
- r = solv->rules + i;
- }
- }
- return level;
-}
-
-/*-------------------------------------------------------------------
- *
- * solver_free
- */
-
-static inline void
-queuep_free(Queue **qp)
-{
- if (!*qp)
- return;
- queue_free(*qp);
- *qp = solv_free(*qp);
-}
-
-static inline void
-map_zerosize(Map *m)
-{
- if (m->size)
- {
- map_free(m);
- map_init(m, 0);
- }
-}
-
-void
-solver_free(Solver *solv)
-{
- queue_free(&solv->job);
- queue_free(&solv->ruletojob);
- queue_free(&solv->decisionq);
- queue_free(&solv->decisionq_why);
- queue_free(&solv->learnt_why);
- queue_free(&solv->learnt_pool);
- queue_free(&solv->problems);
- queue_free(&solv->solutions);
- queue_free(&solv->orphaned);
- queue_free(&solv->branches);
- queue_free(&solv->weakruleq);
- queue_free(&solv->ruleassertions);
- queue_free(&solv->addedmap_deduceq);
- queuep_free(&solv->cleandeps_updatepkgs);
- queuep_free(&solv->cleandeps_mistakes);
- queuep_free(&solv->update_targets);
- queuep_free(&solv->installsuppdepq);
- queuep_free(&solv->recommendscplxq);
- queuep_free(&solv->suggestscplxq);
- queuep_free(&solv->brokenorphanrules);
-
- map_free(&solv->recommendsmap);
- map_free(&solv->suggestsmap);
- map_free(&solv->noupdate);
- map_free(&solv->weakrulemap);
- map_free(&solv->multiversion);
-
- map_free(&solv->updatemap);
- map_free(&solv->bestupdatemap);
- map_free(&solv->fixmap);
- map_free(&solv->dupmap);
- map_free(&solv->dupinvolvedmap);
- map_free(&solv->droporphanedmap);
- map_free(&solv->cleandepsmap);
- map_free(&solv->allowuninstallmap);
-
- solv_free(solv->decisionmap);
- solv_free(solv->rules);
- solv_free(solv->watches);
- solv_free(solv->obsoletes);
- solv_free(solv->obsoletes_data);
- solv_free(solv->specialupdaters);
- solv_free(solv->choicerules_ref);
- solv_free(solv->bestrules_pkg);
- solv_free(solv->yumobsrules_info);
- solv_free(solv->instbuddy);
- solv_free(solv);
-}
-
-int
-solver_get_flag(Solver *solv, int flag)
-{
- switch (flag)
- {
- case SOLVER_FLAG_ALLOW_DOWNGRADE:
- return solv->allowdowngrade;
- case SOLVER_FLAG_ALLOW_NAMECHANGE:
- return solv->allownamechange;
- case SOLVER_FLAG_ALLOW_ARCHCHANGE:
- return solv->allowarchchange;
- case SOLVER_FLAG_ALLOW_VENDORCHANGE:
- return solv->allowvendorchange;
- case SOLVER_FLAG_ALLOW_UNINSTALL:
- return solv->allowuninstall;
- case SOLVER_FLAG_NO_UPDATEPROVIDE:
- return solv->noupdateprovide;
- case SOLVER_FLAG_SPLITPROVIDES:
- return solv->dosplitprovides;
- case SOLVER_FLAG_IGNORE_RECOMMENDED:
- return solv->dontinstallrecommended;
- case SOLVER_FLAG_ADD_ALREADY_RECOMMENDED:
- return solv->addalreadyrecommended;
- case SOLVER_FLAG_NO_INFARCHCHECK:
- return solv->noinfarchcheck;
- case SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES:
- return solv->keepexplicitobsoletes;
- case SOLVER_FLAG_BEST_OBEY_POLICY:
- return solv->bestobeypolicy;
- case SOLVER_FLAG_NO_AUTOTARGET:
- return solv->noautotarget;
- case SOLVER_FLAG_DUP_ALLOW_DOWNGRADE:
- return solv->dup_allowdowngrade;
- case SOLVER_FLAG_DUP_ALLOW_NAMECHANGE:
- return solv->dup_allownamechange;
- case SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE:
- return solv->dup_allowarchchange;
- case SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE:
- return solv->dup_allowvendorchange;
- case SOLVER_FLAG_KEEP_ORPHANS:
- return solv->keep_orphans;
- case SOLVER_FLAG_BREAK_ORPHANS:
- return solv->break_orphans;
- case SOLVER_FLAG_FOCUS_INSTALLED:
- return solv->focus_installed;
- case SOLVER_FLAG_YUM_OBSOLETES:
- return solv->do_yum_obsoletes;
- case SOLVER_FLAG_NEED_UPDATEPROVIDE:
- return solv->needupdateprovide;
- default:
- break;
- }
- return -1;
-}
-
-int
-solver_set_flag(Solver *solv, int flag, int value)
-{
- int old = solver_get_flag(solv, flag);
- switch (flag)
- {
- case SOLVER_FLAG_ALLOW_DOWNGRADE:
- solv->allowdowngrade = value;
- break;
- case SOLVER_FLAG_ALLOW_NAMECHANGE:
- solv->allownamechange = value;
- break;
- case SOLVER_FLAG_ALLOW_ARCHCHANGE:
- solv->allowarchchange = value;
- break;
- case SOLVER_FLAG_ALLOW_VENDORCHANGE:
- solv->allowvendorchange = value;
- break;
- case SOLVER_FLAG_ALLOW_UNINSTALL:
- solv->allowuninstall = value;
- break;
- case SOLVER_FLAG_NO_UPDATEPROVIDE:
- solv->noupdateprovide = value;
- break;
- case SOLVER_FLAG_SPLITPROVIDES:
- solv->dosplitprovides = value;
- break;
- case SOLVER_FLAG_IGNORE_RECOMMENDED:
- solv->dontinstallrecommended = value;
- break;
- case SOLVER_FLAG_ADD_ALREADY_RECOMMENDED:
- solv->addalreadyrecommended = value;
- break;
- case SOLVER_FLAG_NO_INFARCHCHECK:
- solv->noinfarchcheck = value;
- break;
- case SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES:
- solv->keepexplicitobsoletes = value;
- break;
- case SOLVER_FLAG_BEST_OBEY_POLICY:
- solv->bestobeypolicy = value;
- break;
- case SOLVER_FLAG_NO_AUTOTARGET:
- solv->noautotarget = value;
- break;
- case SOLVER_FLAG_DUP_ALLOW_DOWNGRADE:
- solv->dup_allowdowngrade = value;
- break;
- case SOLVER_FLAG_DUP_ALLOW_NAMECHANGE:
- solv->dup_allownamechange = value;
- break;
- case SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE:
- solv->dup_allowarchchange = value;
- break;
- case SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE:
- solv->dup_allowvendorchange = value;
- break;
- case SOLVER_FLAG_KEEP_ORPHANS:
- solv->keep_orphans = value;
- break;
- case SOLVER_FLAG_BREAK_ORPHANS:
- solv->break_orphans = value;
- break;
- case SOLVER_FLAG_FOCUS_INSTALLED:
- solv->focus_installed = value;
- break;
- case SOLVER_FLAG_YUM_OBSOLETES:
- solv->do_yum_obsoletes = value;
- break;
- case SOLVER_FLAG_NEED_UPDATEPROVIDE:
- solv->needupdateprovide = value;
- break;
- default:
- break;
- }
- return old;
-}
-
-static int
-cleandeps_check_mistakes(Solver *solv)
-{
- Pool *pool = solv->pool;
- Rule *r;
- Id p, pp;
- int i;
- int mademistake = 0;
-
- if (!solv->cleandepsmap.size)
- return 0;
- /* check for mistakes */
- for (i = solv->installed->start; i < solv->installed->end; i++)
- {
- if (!MAPTST(&solv->cleandepsmap, i - solv->installed->start))
- continue;
- r = solv->rules + solv->featurerules + (i - solv->installed->start);
- /* a mistake is when the featurerule is true but the updaterule is false */
- if (!r->p)
- continue;
- FOR_RULELITERALS(p, pp, r)
- if (p > 0 && solv->decisionmap[p] > 0)
- break;
- if (!p)
- continue; /* feature rule is not true */
- r = solv->rules + solv->updaterules + (i - solv->installed->start);
- if (!r->p)
- continue;
- FOR_RULELITERALS(p, pp, r)
- if (p > 0 && solv->decisionmap[p] > 0)
- break;
- if (p)
- continue; /* update rule is true */
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps mistake: ");
- solver_printruleclass(solv, SOLV_DEBUG_SOLVER, r);
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "feature rule: ");
- solver_printruleclass(solv, SOLV_DEBUG_SOLVER, solv->rules + solv->featurerules + (i - solv->installed->start));
- if (!solv->cleandeps_mistakes)
- {
- solv->cleandeps_mistakes = solv_calloc(1, sizeof(Queue));
- queue_init(solv->cleandeps_mistakes);
- }
- queue_push(solv->cleandeps_mistakes, i);
- MAPCLR(&solv->cleandepsmap, i - solv->installed->start);
- solver_reenablepolicyrules_cleandeps(solv, i);
- mademistake = 1;
- }
- return mademistake;
-}
-
-static void
-prune_to_update_targets(Solver *solv, Id *cp, Queue *q)
-{
- int i, j;
- Id p, *cp2;
- for (i = j = 0; i < q->count; i++)
- {
- p = q->elements[i];
- for (cp2 = cp; *cp2; cp2++)
- if (*cp2 == p)
- {
- q->elements[j++] = p;
- break;
- }
- }
- queue_truncate(q, j);
-}
-
-#ifdef ENABLE_COMPLEX_DEPS
-
-static void
-add_complex_recommends(Solver *solv, Id rec, Queue *dq, Map *dqmap)
-{
- Pool *pool = solv->pool;
- int oldcnt = dq->count;
- int cutcnt, blkcnt;
- Id p;
- int i, j;
-
-#if 0
- printf("ADD_COMPLEX_RECOMMENDS %s\n", pool_dep2str(pool, rec));
-#endif
- i = pool_normalize_complex_dep(pool, rec, dq, CPLXDEPS_EXPAND);
- if (i == 0 || i == 1)
- return;
- cutcnt = dq->count;
- for (i = oldcnt; i < cutcnt; i++)
- {
- blkcnt = dq->count;
- for (; (p = dq->elements[i]) != 0; i++)
- {
- if (p < 0)
- {
- if (solv->decisionmap[-p] <= 0)
- break;
- continue;
- }
- if (solv->decisionmap[p] > 0)
- {
- queue_truncate(dq, blkcnt);
- break;
- }
- if (dqmap)
- {
- if (!MAPTST(dqmap, p))
- continue;
- }
- else
- {
- if (solv->decisionmap[p] < 0)
- continue;
- if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
- continue;
- }
- queue_push(dq, p);
- }
- while (dq->elements[i])
- i++;
- }
- queue_deleten(dq, oldcnt, cutcnt - oldcnt);
- /* unify */
- if (dq->count != oldcnt)
- {
- for (j = oldcnt; j < dq->count; j++)
- {
- p = dq->elements[j];
- for (i = 0; i < j; i++)
- if (dq->elements[i] == p)
- {
- dq->elements[j] = 0;
- break;
- }
- }
- for (i = j = oldcnt; j < dq->count; j++)
- if (dq->elements[j])
- dq->elements[i++] = dq->elements[j];
- queue_truncate(dq, i);
- }
-#if 0
- printf("RETURN:\n");
- for (i = oldcnt; i < dq->count; i++)
- printf(" - %s\n", pool_solvid2str(pool, dq->elements[i]));
-#endif
-}
-
-static void
-do_complex_recommendations(Solver *solv, Id rec, Map *m, int noselected)
-{
- Pool *pool = solv->pool;
- Queue dq;
- Id p;
- int i, blk;
-
-#if 0
- printf("DO_COMPLEX_RECOMMENDATIONS %s\n", pool_dep2str(pool, rec));
-#endif
- queue_init(&dq);
- i = pool_normalize_complex_dep(pool, rec, &dq, CPLXDEPS_EXPAND);
- if (i == 0 || i == 1)
- {
- queue_free(&dq);
- return;
- }
- for (i = 0; i < dq.count; i++)
- {
- blk = i;
- for (; (p = dq.elements[i]) != 0; i++)
- {
- if (p < 0)
- {
- if (solv->decisionmap[-p] <= 0)
- break;
- continue;
- }
- if (solv->decisionmap[p] > 0)
- {
- if (noselected)
- break;
- MAPSET(m, p);
- for (i++; (p = dq.elements[i]) != 0; i++)
- if (p > 0 && solv->decisionmap[p] > 0)
- MAPSET(m, p);
- p = 1;
- break;
- }
- }
- if (!p)
- {
- for (i = blk; (p = dq.elements[i]) != 0; i++)
- if (p > 0)
- MAPSET(m, p);
- }
- while (dq.elements[i])
- i++;
- }
- queue_free(&dq);
-}
-
-#endif
-
-/*-------------------------------------------------------------------
- *
- * solver_run_sat
- *
- * all rules have been set up, now actually run the solver
- *
- */
-
-void
-solver_run_sat(Solver *solv, int disablerules, int doweak)
-{
- Queue dq; /* local decisionqueue */
- Queue dqs; /* local decisionqueue for supplements */
- int systemlevel;
- int level, olevel;
- Rule *r;
- int i, j, n;
- Solvable *s;
- Pool *pool = solv->pool;
- Id p, pp, *dp, postponed;
- int minimizationsteps;
- int installedpos = solv->installed ? solv->installed->start : 0;
-
- IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
- {
- POOL_DEBUG (SOLV_DEBUG_RULE_CREATION, "number of rules: %d\n", solv->nrules);
- for (i = 1; i < solv->nrules; i++)
- solver_printruleclass(solv, SOLV_DEBUG_RULE_CREATION, solv->rules + i);
- }
-
- /* start SAT algorithm */
- level = 0;
- systemlevel = level + 1;
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "solving...\n");
-
- queue_init(&dq);
- queue_init(&dqs);
-
- /*
- * here's the main loop:
- * 1) decide assertion rules and propagate
- * 2) fulfill jobs
- * 3) try to keep installed packages
- * 4) fulfill all unresolved rules
- * 5) install recommended packages
- * 6) minimalize solution if we had choices
- * if we encounter a problem, we rewind to a safe level and restart
- * with step 1
- */
-
- minimizationsteps = 0;
- for (;;)
- {
- /*
- * initial propagation of the assertions
- */
- if (level <= 0)
- {
- if (level < 0)
- break;
- makeruledecisions(solv);
- level = 1;
- if (!disablerules && solv->problems.count)
- {
- level = -1;
- break;
- }
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "initial propagate (propagate_index: %d; size decisionq: %d)...\n", solv->propagate_index, solv->decisionq.count);
- if ((r = propagate(solv, level)) != 0)
- {
- level = analyze_unsolvable(solv, r, disablerules);
- continue;
- }
- systemlevel = level + 1;
- }
-
- /*
- * resolve jobs first (unless focus_installed is set)
- */
- if (level < systemlevel && !solv->focus_installed)
- {
- olevel = level;
- level = resolve_jobrules(solv, level, disablerules, &dq);
- if (level < olevel)
- continue;
- systemlevel = level + 1;
- }
-
-
- /*
- * installed packages
- */
- if (!solv->decisioncnt_update)
- solv->decisioncnt_update = solv->decisionq.count;
- if (level < systemlevel && solv->installed && solv->installed->nsolvables && !solv->installed->disabled)
- {
- Repo *installed = solv->installed;
- int pass;
-
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving installed packages\n");
- /* we use two passes if we need to update packages
- * to create a better user experience */
- for (pass = solv->updatemap.size ? 0 : 1; pass < 2; pass++)
- {
- int passlevel = level;
- Id *specialupdaters = solv->specialupdaters;
- if (pass == 1 && !solv->decisioncnt_keep)
- solv->decisioncnt_keep = solv->decisionq.count;
- /* start with installedpos, the position that gave us problems the last time */
- for (i = installedpos, n = installed->start; n < installed->end; i++, n++)
- {
- Rule *rr;
- Id d;
-
- if (i == installed->end)
- i = installed->start;
- s = pool->solvables + i;
- if (s->repo != installed)
- continue;
-
- if (solv->decisionmap[i] > 0 && (!specialupdaters || !specialupdaters[i - installed->start]))
- continue; /* already decided */
- if (!pass && solv->updatemap.size && !MAPTST(&solv->updatemap, i - installed->start))
- continue; /* updates first */
- r = solv->rules + solv->updaterules + (i - installed->start);
- rr = r;
- if (!rr->p || rr->d < 0) /* disabled -> look at feature rule */
- rr -= solv->installed->end - solv->installed->start;
- if (!rr->p) /* identical to update rule? */
- rr = r;
- if (!rr->p && !(specialupdaters && specialupdaters[i - installed->start]))
- continue; /* orpaned package */
-
- /* check if we should update this package to the latest version
- * noupdate is set for erase jobs, in that case we want to deinstall
- * the installed package and not replace it with a newer version
- * rr->p != i is for dup jobs where the installed package cannot be kept */
- queue_empty(&dq);
- if (!MAPTST(&solv->noupdate, i - installed->start) && (solv->decisionmap[i] < 0 || solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, i - installed->start)) || (rr->p && rr->p != i)))
- {
- if (!rr->p)
- {
- /* specialupdater with no update/feature rule */
- for (d = specialupdaters[i - installed->start]; (p = pool->whatprovidesdata[d++]) != 0; )
- {
- if (solv->decisionmap[p] > 0)
- {
- dq.count = 0;
- break;
- }
- if (!solv->decisionmap[p])
- queue_push(&dq, p);
- }
- }
- else if (specialupdaters && (d = specialupdaters[i - installed->start]) != 0)
- {
- /* special multiversion handling, make sure best version is chosen */
- if (rr->p == i && solv->decisionmap[i] >= 0)
- queue_push(&dq, i);
- while ((p = pool->whatprovidesdata[d++]) != 0)
- if (solv->decisionmap[p] >= 0)
- queue_push(&dq, p);
- if (dq.count && solv->update_targets && solv->update_targets->elements[i - installed->start])
- prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[i - installed->start], &dq);
- if (dq.count)
- {
- policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
- p = dq.elements[0];
- if (p != i && solv->decisionmap[p] == 0)
- {
- rr = solv->rules + solv->featurerules + (i - solv->installed->start);
- if (!rr->p) /* update rule == feature rule? */
- rr = rr - solv->featurerules + solv->updaterules;
- dq.count = 1;
- }
- else
- dq.count = 0;
- }
- }
- else
- {
- /* update to best package of the update rule */
- FOR_RULELITERALS(p, pp, rr)
- {
- if (solv->decisionmap[p] > 0)
- {
- dq.count = 0; /* already fulfilled */
- break;
- }
- if (!solv->decisionmap[p])
- queue_push(&dq, p);
- }
- }
- }
- if (dq.count && solv->update_targets && solv->update_targets->elements[i - installed->start])
- prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[i - installed->start], &dq);
- /* install best version */
- if (dq.count)
- {
- olevel = level;
- level = selectandinstall(solv, level, &dq, disablerules, rr - solv->rules);
- if (level <= olevel)
- {
- if (level < passlevel)
- break; /* trouble */
- if (level < olevel)
- n = installed->start; /* redo all */
- i--;
- n--;
- continue;
- }
- }
- /* if still undecided keep package */
- if (solv->decisionmap[i] == 0)
- {
- olevel = level;
- if (solv->cleandepsmap.size && MAPTST(&solv->cleandepsmap, i - installed->start))
- {
-#if 0
- POOL_DEBUG(SOLV_DEBUG_POLICY, "cleandeps erasing %s\n", pool_solvid2str(pool, i));
- level = setpropagatelearn(solv, level, -i, disablerules, 0);
-#else
- continue;
-#endif
- }
- else
- {
- POOL_DEBUG(SOLV_DEBUG_POLICY, "keeping %s\n", pool_solvid2str(pool, i));
- level = setpropagatelearn(solv, level, i, disablerules, r - solv->rules);
- }
- if (level <= olevel)
- {
- if (level < passlevel)
- break; /* trouble */
- if (level < olevel)
- n = installed->start; /* redo all */
- i--;
- n--;
- continue; /* retry with learnt rule */
- }
- }
- }
- if (n < installed->end)
- {
- installedpos = i; /* retry problem solvable next time */
- break; /* ran into trouble */
- }
- installedpos = installed->start; /* reset installedpos */
- }
- systemlevel = level + 1;
- if (pass < 2)
- continue; /* had trouble, retry */
- }
- if (!solv->decisioncnt_keep)
- solv->decisioncnt_keep = solv->decisionq.count;
-
- if (level < systemlevel && solv->focus_installed)
- {
- olevel = level;
- level = resolve_jobrules(solv, level, disablerules, &dq);
- if (level < olevel)
- continue;
- systemlevel = level + 1;
- }
-
- if (level < systemlevel)
- systemlevel = level;
-
- /*
- * decide
- */
- if (!solv->decisioncnt_resolve)
- solv->decisioncnt_resolve = solv->decisionq.count;
- POOL_DEBUG(SOLV_DEBUG_POLICY, "deciding unresolved rules\n");
- postponed = 0;
- for (i = 1, n = 1; ; i++, n++)
- {
- if (n >= solv->nrules)
- {
- if (postponed <= 0)
- break;
- i = postponed;
- postponed = -1;
- n = 1;
- }
- if (i == solv->nrules)
- i = 1;
- r = solv->rules + i;
- if (r->d < 0) /* ignore disabled rules */
- continue;
- if (r->p < 0) /* most common cases first */
- {
- if (r->d == 0 || solv->decisionmap[-r->p] <= 0)
- continue;
- }
- if (dq.count)
- queue_empty(&dq);
- if (r->d == 0)
- {
- /* binary or unary rule */
- /* need two positive undecided literals, r->p already checked above */
- if (r->w2 <= 0)
- continue;
- if (solv->decisionmap[r->p] || solv->decisionmap[r->w2])
- continue;
- queue_push(&dq, r->p);
- queue_push(&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
- * (decisionmap[-r->p] for the r->p < 0 case is already checked above)
- */
- if (r->p >= 0)
- {
- if (solv->decisionmap[r->p] > 0)
- continue;
- if (solv->decisionmap[r->p] == 0)
- queue_push(&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)
- queue_push(&dq, p);
- }
- }
- if (p)
- continue;
- }
- IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
- {
- POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "unfulfilled ");
- solver_printruleclass(solv, SOLV_DEBUG_PROPAGATE, r);
- }
- /* dq.count < 2 cannot happen as this means that
- * the rule is unit */
- assert(dq.count > 1);
-
- /* prune to cleandeps packages */
- if (solv->cleandepsmap.size && solv->installed)
- {
- Repo *installed = solv->installed;
- for (j = 0; j < dq.count; j++)
- if (pool->solvables[dq.elements[j]].repo == installed && MAPTST(&solv->cleandepsmap, dq.elements[j] - installed->start))
- break;
- if (j < dq.count)
- {
- dq.elements[0] = dq.elements[j];
- queue_truncate(&dq, 1);
- }
- }
-
- if (dq.count > 1 && postponed >= 0)
- {
- policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE_NOREORDER);
- if (dq.count > 1)
- {
- if (!postponed)
- postponed = i;
- continue;
- }
- }
-
- olevel = level;
- level = selectandinstall(solv, level, &dq, disablerules, r - solv->rules);
- if (level < systemlevel)
- break; /* trouble */
- /* something changed, so look at all rules again */
- n = 0;
- }
-
- if (n < solv->nrules) /* ran into trouble? */
- continue; /* start over */
-
- /* decide leftover cleandeps packages */
- if (solv->cleandepsmap.size && solv->installed)
- {
- for (p = solv->installed->start; p < solv->installed->end; p++)
- {
- s = pool->solvables + p;
- if (s->repo != solv->installed)
- continue;
- if (solv->decisionmap[p] == 0 && MAPTST(&solv->cleandepsmap, p - solv->installed->start))
- {
- POOL_DEBUG(SOLV_DEBUG_POLICY, "cleandeps erasing %s\n", pool_solvid2str(pool, p));
- olevel = level;
- level = setpropagatelearn(solv, level, -p, 0, 0);
- if (level < olevel)
- break;
- }
- }
- if (p < solv->installed->end)
- continue;
- }
-
- /* at this point we have a consistent system. now do the extras... */
-
- if (!solv->decisioncnt_weak)
- solv->decisioncnt_weak = solv->decisionq.count;
- if (doweak)
- {
- int qcount;
-
- POOL_DEBUG(SOLV_DEBUG_POLICY, "installing recommended packages\n");
- queue_empty(&dq); /* recommended packages */
- queue_empty(&dqs); /* supplemented packages */
- for (i = 1; i < pool->nsolvables; i++)
- {
- if (solv->decisionmap[i] < 0)
- continue;
- if (solv->decisionmap[i] > 0)
- {
- /* installed, check for recommends */
- Id *recp, rec, pp, p;
- s = pool->solvables + i;
- if (!solv->addalreadyrecommended && s->repo == solv->installed)
- continue;
- /* XXX need to special case AND ? */
- if (s->recommends)
- {
- recp = s->repo->idarraydata + s->recommends;
- while ((rec = *recp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, rec))
- {
- add_complex_recommends(solv, rec, &dq, 0);
- continue;
- }
-#endif
- qcount = dq.count;
- FOR_PROVIDES(p, pp, rec)
- {
- if (solv->decisionmap[p] > 0)
- {
- dq.count = qcount;
- break;
- }
- else if (solv->decisionmap[p] == 0)
- {
- if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
- continue;
- queue_pushunique(&dq, p);
- }
- }
- }
- }
- }
- else
- {
- s = pool->solvables + i;
- if (!s->supplements)
- continue;
- if (!pool_installable(pool, s))
- continue;
- if (!solver_is_supplementing(solv, s))
- continue;
- if (solv->dupmap_all && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start))))
- continue;
- queue_push(&dqs, i);
- }
- }
-
- /* filter out all packages obsoleted by installed packages */
- /* this is no longer needed if we have reverse obsoletes */
- if ((dqs.count || dq.count) && solv->installed)
- {
- Map obsmap;
- Id obs, *obsp, po, ppo;
-
- map_init(&obsmap, pool->nsolvables);
- for (p = solv->installed->start; p < solv->installed->end; p++)
- {
- s = pool->solvables + p;
- if (s->repo != solv->installed || !s->obsoletes)
- continue;
- if (solv->decisionmap[p] <= 0)
- continue;
- if (!solv->keepexplicitobsoletes && solv->multiversion.size && MAPTST(&solv->multiversion, p))
- continue;
- obsp = s->repo->idarraydata + s->obsoletes;
- /* foreach obsoletes */
- while ((obs = *obsp++) != 0)
- FOR_PROVIDES(po, ppo, obs)
- {
- Solvable *pos = pool->solvables + po;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pos, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, pos))
- continue;
- MAPSET(&obsmap, po);
- }
- }
- for (i = j = 0; i < dqs.count; i++)
- if (!MAPTST(&obsmap, dqs.elements[i]))
- dqs.elements[j++] = dqs.elements[i];
- dqs.count = j;
- for (i = j = 0; i < dq.count; i++)
- if (!MAPTST(&obsmap, dq.elements[i]))
- dq.elements[j++] = dq.elements[i];
- dq.count = j;
- map_free(&obsmap);
- }
-
- /* filter out all already supplemented packages if requested */
- if (!solv->addalreadyrecommended && dqs.count)
- {
- /* filter out old supplements */
- for (i = j = 0; i < dqs.count; i++)
- {
- p = dqs.elements[i];
- s = pool->solvables + p;
- if (s->supplements && solver_is_supplementing_alreadyinstalled(solv, s))
- dqs.elements[j++] = p;
- }
- dqs.count = j;
- }
-
- /* multiversion doesn't mix well with supplements.
- * filter supplemented packages where we already decided
- * to install a different version (see bnc#501088) */
- if (dqs.count && solv->multiversion.size)
- {
- for (i = j = 0; i < dqs.count; i++)
- {
- p = dqs.elements[i];
- if (MAPTST(&solv->multiversion, p))
- {
- Id p2, pp2;
- s = pool->solvables + p;
- FOR_PROVIDES(p2, pp2, s->name)
- if (solv->decisionmap[p2] > 0 && pool->solvables[p2].name == s->name)
- break;
- if (p2)
- continue; /* ignore this package */
- }
- dqs.elements[j++] = p;
- }
- dqs.count = j;
- }
-
- /* make dq contain both recommended and supplemented pkgs */
- if (dqs.count)
- {
- for (i = 0; i < dqs.count; i++)
- queue_pushunique(&dq, dqs.elements[i]);
- }
-
- if (dq.count)
- {
- Map dqmap;
- int decisioncount = solv->decisionq.count;
-
- if (dq.count == 1)
- {
- /* simple case, just one package. no need to choose to best version */
- p = dq.elements[0];
- if (dqs.count)
- POOL_DEBUG(SOLV_DEBUG_POLICY, "installing supplemented %s\n", pool_solvid2str(pool, p));
- else
- POOL_DEBUG(SOLV_DEBUG_POLICY, "installing recommended %s\n", pool_solvid2str(pool, p));
- level = setpropagatelearn(solv, level, p, 0, 0);
- continue; /* back to main loop */
- }
-
- /* filter packages, this gives us the best versions */
- policy_filter_unwanted(solv, &dq, POLICY_MODE_RECOMMEND);
-
- /* create map of result */
- map_init(&dqmap, pool->nsolvables);
- for (i = 0; i < dq.count; i++)
- MAPSET(&dqmap, dq.elements[i]);
-
- /* install all supplemented packages */
- for (i = 0; i < dqs.count; i++)
- {
- p = dqs.elements[i];
- if (solv->decisionmap[p] || !MAPTST(&dqmap, p))
- continue;
- POOL_DEBUG(SOLV_DEBUG_POLICY, "installing supplemented %s\n", pool_solvid2str(pool, p));
- olevel = level;
- level = setpropagatelearn(solv, level, p, 0, 0);
- if (level <= olevel)
- break;
- }
- if (i < dqs.count || solv->decisionq.count < decisioncount)
- {
- map_free(&dqmap);
- continue;
- }
-
- /* install all recommended packages */
- /* more work as we want to created branches if multiple
- * choices are valid */
- for (i = 0; i < decisioncount; i++)
- {
- Id rec, *recp, pp;
- p = solv->decisionq.elements[i];
- if (p < 0)
- continue;
- s = pool->solvables + p;
- if (!s->repo || (!solv->addalreadyrecommended && s->repo == solv->installed))
- continue;
- if (!s->recommends)
- continue;
- recp = s->repo->idarraydata + s->recommends;
- while ((rec = *recp++) != 0)
- {
- queue_empty(&dq);
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, rec))
- add_complex_recommends(solv, rec, &dq, &dqmap);
- else
-#endif
- FOR_PROVIDES(p, pp, rec)
- {
- if (solv->decisionmap[p] > 0)
- {
- dq.count = 0;
- break;
- }
- else if (solv->decisionmap[p] == 0 && MAPTST(&dqmap, p))
- queue_push(&dq, p);
- }
- if (!dq.count)
- continue;
- if (dq.count > 1)
- policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
- /* if we have multiple candidates we open a branch */
- if (dq.count > 1)
- createbranch(solv, level, &dq, s - pool->solvables, rec);
- p = dq.elements[0];
- POOL_DEBUG(SOLV_DEBUG_POLICY, "installing recommended %s\n", pool_solvid2str(pool, p));
- olevel = level;
- level = setpropagatelearn(solv, level, p, 0, 0);
- if (level <= olevel || solv->decisionq.count < decisioncount)
- break; /* we had to revert some decisions */
- }
- if (rec)
- break; /* had a problem above, quit loop */
- }
- map_free(&dqmap);
- continue; /* back to main loop so that all deps are checked */
- }
- }
-
- if (!solv->decisioncnt_orphan)
- solv->decisioncnt_orphan = solv->decisionq.count;
- if (solv->installed && (solv->orphaned.count || solv->brokenorphanrules))
- {
- int installedone = 0;
-
- /* let's see if we can install some unsupported package */
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "deciding orphaned packages\n");
- for (i = 0; i < solv->orphaned.count; i++)
- {
- p = solv->orphaned.elements[i];
- if (solv->decisionmap[p])
- continue; /* already decided */
- if (solv->droporphanedmap_all)
- continue;
- if (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))
- continue;
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "keeping orphaned %s\n", pool_solvid2str(pool, p));
- olevel = level;
- level = setpropagatelearn(solv, level, p, 0, 0);
- installedone = 1;
- if (level < olevel)
- break;
- }
- if (installedone || i < solv->orphaned.count)
- continue; /* back to main loop */
- for (i = 0; i < solv->orphaned.count; i++)
- {
- p = solv->orphaned.elements[i];
- if (solv->decisionmap[p])
- continue; /* already decided */
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "removing orphaned %s\n", pool_solvid2str(pool, p));
- olevel = level;
- level = setpropagatelearn(solv, level, -p, 0, 0);
- if (level < olevel)
- break;
- }
- if (i < solv->orphaned.count)
- continue; /* back to main loop */
- if (solv->brokenorphanrules)
- {
- solver_check_brokenorphanrules(solv, &dq);
- if (dq.count)
- {
- policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
- for (i = 0; i < dq.count; i++)
- {
- p = dq.elements[i];
- POOL_DEBUG(SOLV_DEBUG_POLICY, "installing orphaned dep %s\n", pool_solvid2str(pool, p));
- olevel = level;
- level = setpropagatelearn(solv, level, p, 0, 0);
- if (level < olevel)
- break;
- }
- continue;
- }
- }
- }
-
- /* one final pass to make sure we decided all installed packages */
- if (solv->installed)
- {
- for (p = solv->installed->start; p < solv->installed->end; p++)
- {
- if (solv->decisionmap[p])
- continue; /* already decided */
- s = pool->solvables + p;
- if (s->repo != solv->installed)
- continue;
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "removing unwanted %s\n", pool_solvid2str(pool, p));
- olevel = level;
- level = setpropagatelearn(solv, level, -p, 0, 0);
- if (level < olevel)
- break;
- }
- if (p < solv->installed->end)
- continue; /* back to main loop */
- }
-
- if (solv->installed && solv->cleandepsmap.size && cleandeps_check_mistakes(solv))
- {
- solver_reset(solv);
- level = 0; /* restart from scratch */
- continue;
- }
-
- if (solv->solution_callback)
- {
- solv->solution_callback(solv, solv->solution_callback_data);
- if (solv->branches.count)
- {
- int l, endi = 0;
- p = l = 0;
- for (i = solv->branches.count - 1; i >= 0; i--)
- {
- p = solv->branches.elements[i];
- if (p > 0 && !l)
- {
- endi = i + 1;
- l = p;
- i -= 3; /* skip: p data count */
- }
- else if (p > 0)
- break;
- else if (p < 0)
- l = 0;
- }
- if (i >= 0)
- {
- while (i > 0 && solv->branches.elements[i - 1] > 0)
- i--;
- level = takebranch(solv, i, endi, "branching", disablerules);
- continue;
- }
- }
- /* all branches done, we're finally finished */
- break;
- }
-
- /* auto-minimization step */
- if (solv->branches.count)
- {
- int endi, lasti = -1, lastiend = -1;
- if (solv->recommends_index < solv->decisionq.count)
- policy_update_recommendsmap(solv);
- for (endi = solv->branches.count; endi > 0;)
- {
- int l, lastsi = -1, starti = endi - solv->branches.elements[endi - 2];
- l = solv->branches.elements[endi - 1];
- for (i = starti; i < endi - 4; i++)
- {
- p = solv->branches.elements[i];
- if (p <= 0)
- continue;
- if (solv->decisionmap[p] > l)
- {
- lasti = i;
- lastiend = endi;
- lastsi = -1;
- break;
- }
- if (lastsi < 0 && (MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
- lastsi = i;
- }
- if (lastsi >= 0)
- {
- /* we have a recommended package that could not be installed */
- /* take it if our current selection is not recommended */
- for (i = starti; i < endi - 4; i++)
- {
- p = -solv->branches.elements[i];
- if (p <= 0 || solv->decisionmap[p] != l + 1)
- continue;
- if (!(MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
- {
- lasti = lastsi;
- lastiend = endi;
- break;
- }
- }
- }
- endi = starti;
- }
- if (lasti >= 0)
- {
- minimizationsteps++;
- level = takebranch(solv, lasti, lastiend, "minimizing", disablerules);
- continue; /* back to main loop */
- }
- }
- /* no minimization found, we're finally finished! */
- break;
- }
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "solver statistics: %d learned rules, %d unsolvable, %d minimization steps\n", solv->stats_learned, solv->stats_unsolvable, minimizationsteps);
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "done solving.\n\n");
- queue_free(&dq);
- queue_free(&dqs);
- if (level < 0)
- {
- /* unsolvable */
- solv->decisioncnt_jobs = solv->decisionq.count;
- solv->decisioncnt_update = solv->decisionq.count;
- solv->decisioncnt_keep = solv->decisionq.count;
- solv->decisioncnt_resolve = solv->decisionq.count;
- solv->decisioncnt_weak = solv->decisionq.count;
- solv->decisioncnt_orphan = solv->decisionq.count;
- }
-#if 0
- solver_printdecisionq(solv, SOLV_DEBUG_RESULT);
-#endif
-}
-
-
-/*-------------------------------------------------------------------
- *
- * remove disabled conflicts
- *
- * purpose: update the decisionmap after some rules were disabled.
- * this is used to calculate the suggested/recommended package list.
- * Also returns a "removed" list to undo the discisionmap changes.
- */
-
-static void
-removedisabledconflicts(Solver *solv, Queue *removed)
-{
- Pool *pool = solv->pool;
- int i, n;
- Id p, why, *dp;
- Id new;
- Rule *r;
- Id *decisionmap = solv->decisionmap;
-
- queue_empty(removed);
- for (i = 0; i < solv->decisionq.count; i++)
- {
- p = solv->decisionq.elements[i];
- if (p > 0)
- continue; /* conflicts only, please */
- why = solv->decisionq_why.elements[i];
- if (why == 0)
- {
- /* no rule involved, must be a orphan package drop */
- continue;
- }
- /* we never do conflicts on free decisions, so there
- * must have been an unit rule */
- assert(why > 0);
- r = solv->rules + why;
- if (r->d < 0 && decisionmap[-p])
- {
- /* rule is now disabled, remove from decisionmap */
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "removing conflict for package %s[%d]\n", pool_solvid2str(pool, -p), -p);
- queue_push(removed, -p);
- queue_push(removed, decisionmap[-p]);
- decisionmap[-p] = 0;
- }
- }
- if (!removed->count)
- return;
- /* we removed some confliced packages. some of them might still
- * be in conflict, so search for unit rules and re-conflict */
- new = 0;
- for (i = n = 1, r = solv->rules + i; n < solv->nrules; i++, r++, n++)
- {
- if (i == solv->nrules)
- {
- i = 1;
- r = solv->rules + i;
- }
- if (r->d < 0)
- continue;
- if (!r->w2)
- {
- if (r->p < 0 && !decisionmap[-r->p])
- new = r->p;
- }
- else if (!r->d)
- {
- /* binary rule */
- if (r->p < 0 && decisionmap[-r->p] == 0 && DECISIONMAP_FALSE(r->w2))
- new = r->p;
- else if (r->w2 < 0 && decisionmap[-r->w2] == 0 && DECISIONMAP_FALSE(r->p))
- new = r->w2;
- }
- else
- {
- if (r->p < 0 && decisionmap[-r->p] == 0)
- new = r->p;
- if (new || DECISIONMAP_FALSE(r->p))
- {
- dp = pool->whatprovidesdata + r->d;
- while ((p = *dp++) != 0)
- {
- if (new && p == new)
- continue;
- if (p < 0 && decisionmap[-p] == 0)
- {
- if (new)
- {
- new = 0;
- break;
- }
- new = p;
- }
- else if (!DECISIONMAP_FALSE(p))
- {
- new = 0;
- break;
- }
- }
- }
- }
- if (new)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLVER, "re-conflicting package %s[%d]\n", pool_solvid2str(pool, -new), -new);
- decisionmap[-new] = -1;
- new = 0;
- n = 0; /* redo all rules */
- }
- }
-}
-
-static inline void
-undo_removedisabledconflicts(Solver *solv, Queue *removed)
-{
- int i;
- for (i = 0; i < removed->count; i += 2)
- solv->decisionmap[removed->elements[i]] = removed->elements[i + 1];
-}
-
-
-/*-------------------------------------------------------------------
- *
- * weaken solvable dependencies
- */
-
-static void
-weaken_solvable_deps(Solver *solv, Id p)
-{
- int i;
- Rule *r;
-
- for (i = 1, r = solv->rules + i; i < solv->pkgrules_end; i++, r++)
- {
- if (r->p != -p)
- continue;
- if ((r->d == 0 || r->d == -1) && r->w2 < 0)
- continue; /* conflict */
- queue_push(&solv->weakruleq, i);
- }
-}
-
-
-/********************************************************************/
-/* main() */
-
-
-void
-solver_calculate_multiversionmap(Pool *pool, Queue *job, Map *multiversionmap)
-{
- int i;
- Id how, what, select;
- Id p, pp;
- for (i = 0; i < job->count; i += 2)
- {
- how = job->elements[i];
- if ((how & SOLVER_JOBMASK) != SOLVER_MULTIVERSION)
- continue;
- what = job->elements[i + 1];
- select = how & SOLVER_SELECTMASK;
- if (!multiversionmap->size)
- map_grow(multiversionmap, pool->nsolvables);
- if (select == SOLVER_SOLVABLE_ALL)
- {
- FOR_POOL_SOLVABLES(p)
- MAPSET(multiversionmap, p);
- }
- else if (select == SOLVER_SOLVABLE_REPO)
- {
- Solvable *s;
- Repo *repo = pool_id2repo(pool, what);
- if (repo)
- FOR_REPO_SOLVABLES(repo, p, s)
- MAPSET(multiversionmap, p);
- }
- FOR_JOB_SELECT(p, pp, select, what)
- MAPSET(multiversionmap, p);
- }
-}
-
-void
-solver_calculate_noobsmap(Pool *pool, Queue *job, Map *multiversionmap)
-{
- solver_calculate_multiversionmap(pool, job, multiversionmap);
-}
-
-/*
- * add a rule created by a job, record job number and weak flag
- */
-static inline void
-solver_addjobrule(Solver *solv, Id p, Id p2, Id d, Id job, int weak)
-{
- solver_addrule(solv, p, p2, d);
- queue_push(&solv->ruletojob, job);
- if (weak)
- queue_push(&solv->weakruleq, solv->nrules - 1);
-}
-
-static inline void
-add_cleandeps_package(Solver *solv, Id p)
-{
- if (!solv->cleandeps_updatepkgs)
- {
- solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
- queue_init(solv->cleandeps_updatepkgs);
- }
- queue_pushunique(solv->cleandeps_updatepkgs, p);
-}
-
-static void
-add_update_target(Solver *solv, Id p, Id how)
-{
- Pool *pool = solv->pool;
- Solvable *s = pool->solvables + p;
- Repo *installed = solv->installed;
- Id pi, pip;
- if (!solv->update_targets)
- {
- solv->update_targets = solv_calloc(1, sizeof(Queue));
- queue_init(solv->update_targets);
- }
- if (s->repo == installed)
- {
- queue_push2(solv->update_targets, p, p);
- return;
- }
- FOR_PROVIDES(pi, pip, s->name)
- {
- Solvable *si = pool->solvables + pi;
- if (si->repo != installed || si->name != s->name)
- continue;
- if (how & SOLVER_FORCEBEST)
- {
- if (!solv->bestupdatemap.size)
- map_grow(&solv->bestupdatemap, installed->end - installed->start);
- MAPSET(&solv->bestupdatemap, pi - installed->start);
- }
- if (how & SOLVER_CLEANDEPS)
- add_cleandeps_package(solv, pi);
- queue_push2(solv->update_targets, pi, p);
- /* check if it's ok to keep the installed package */
- if (s->evr == si->evr && solvable_identical(s, si))
- queue_push2(solv->update_targets, pi, pi);
- }
- if (s->obsoletes)
- {
- Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(pi, pip, obs)
- {
- Solvable *si = pool->solvables + pi;
- if (si->repo != installed)
- continue;
- if (si->name == s->name)
- continue; /* already handled above */
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, si, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, si))
- continue;
- if (how & SOLVER_FORCEBEST)
- {
- if (!solv->bestupdatemap.size)
- map_grow(&solv->bestupdatemap, installed->end - installed->start);
- MAPSET(&solv->bestupdatemap, pi - installed->start);
- }
- if (how & SOLVER_CLEANDEPS)
- add_cleandeps_package(solv, pi);
- queue_push2(solv->update_targets, pi, p);
- }
- }
- }
-}
-
-static int
-transform_update_targets_sortfn(const void *ap, const void *bp, void *dp)
-{
- const Id *a = ap;
- const Id *b = bp;
- if (a[0] - b[0])
- return a[0] - b[0];
- return a[1] - b[1];
-}
-
-static void
-transform_update_targets(Solver *solv)
-{
- Repo *installed = solv->installed;
- Queue *update_targets = solv->update_targets;
- int i, j;
- Id p, q, lastp, lastq;
-
- if (!update_targets->count)
- {
- queue_free(update_targets);
- solv->update_targets = solv_free(update_targets);
- return;
- }
- if (update_targets->count > 2)
- solv_sort(update_targets->elements, update_targets->count >> 1, 2 * sizeof(Id), transform_update_targets_sortfn, solv);
- queue_insertn(update_targets, 0, installed->end - installed->start, 0);
- lastp = lastq = 0;
- for (i = j = installed->end - installed->start; i < update_targets->count; i += 2)
- {
- if ((p = update_targets->elements[i]) != lastp)
- {
- if (!solv->updatemap.size)
- map_grow(&solv->updatemap, installed->end - installed->start);
- MAPSET(&solv->updatemap, p - installed->start);
- update_targets->elements[j++] = 0; /* finish old set */
- update_targets->elements[p - installed->start] = j; /* start new set */
- lastp = p;
- lastq = 0;
- }
- if ((q = update_targets->elements[i + 1]) != lastq)
- {
- update_targets->elements[j++] = q;
- lastq = q;
- }
- }
- queue_truncate(update_targets, j);
- queue_push(update_targets, 0); /* finish last set */
-}
-
-
-static void
-addedmap2deduceq(Solver *solv, Map *addedmap)
-{
- Pool *pool = solv->pool;
- int i, j;
- Id p;
- Rule *r;
-
- queue_empty(&solv->addedmap_deduceq);
- for (i = 2, j = solv->pkgrules_end - 1; i < pool->nsolvables && j > 0; j--)
- {
- r = solv->rules + j;
- if (r->p >= 0)
- continue;
- if ((r->d == 0 || r->d == -1) && r->w2 < 0)
- continue;
- p = -r->p;
- if (!MAPTST(addedmap, p))
- {
- /* should never happen, but... */
- if (!solv->addedmap_deduceq.count || solv->addedmap_deduceq.elements[solv->addedmap_deduceq.count - 1] != -p)
- queue_push(&solv->addedmap_deduceq, -p);
- continue;
- }
- for (; i < p; i++)
- if (MAPTST(addedmap, i))
- queue_push(&solv->addedmap_deduceq, i);
- if (i == p)
- i++;
- }
- for (; i < pool->nsolvables; i++)
- if (MAPTST(addedmap, i))
- queue_push(&solv->addedmap_deduceq, i);
- j = 0;
- for (i = 2; i < pool->nsolvables; i++)
- if (MAPTST(addedmap, i))
- j++;
-}
-
-static void
-deduceq2addedmap(Solver *solv, Map *addedmap)
-{
- int j;
- Id p;
- Rule *r;
- for (j = solv->pkgrules_end - 1; j > 0; j--)
- {
- r = solv->rules + j;
- if (r->d < 0 && r->p)
- solver_enablerule(solv, r);
- if (r->p >= 0)
- continue;
- if ((r->d == 0 || r->d == -1) && r->w2 < 0)
- continue;
- p = -r->p;
- MAPSET(addedmap, p);
- }
- for (j = 0; j < solv->addedmap_deduceq.count; j++)
- {
- p = solv->addedmap_deduceq.elements[j];
- if (p > 0)
- MAPSET(addedmap, p);
- else
- MAPCLR(addedmap, p);
- }
-}
-
-#ifdef ENABLE_COMPLEX_DEPS
-static int
-add_complex_jobrules(Solver *solv, Id dep, int flags, int jobidx, int weak)
-{
- Pool *pool = solv->pool;
- Queue bq;
- int i, j;
-
- queue_init(&bq);
- i = pool_normalize_complex_dep(pool, dep, &bq, flags | CPLXDEPS_EXPAND);
- if (i == 0 || i == 1)
- {
- queue_free(&bq);
- if (i == 0)
- solver_addjobrule(solv, -SYSTEMSOLVABLE, 0, 0, jobidx, weak);
- return 0;
- }
- for (i = 0; i < bq.count; i++)
- {
- if (!bq.elements[i])
- continue;
- for (j = 0; bq.elements[i + j + 1]; j++)
- ;
- if (j > 1)
- solver_addjobrule(solv, bq.elements[i], 0, pool_ids2whatprovides(pool, bq.elements + i + 1, j), jobidx, weak);
- else
- solver_addjobrule(solv, bq.elements[i], bq.elements[i + 1], 0, jobidx, weak);
- i += j + 1;
- }
- queue_free(&bq);
- return 1;
-}
-#endif
-
-/*
- *
- * solve job queue
- *
- */
-
-int
-solver_solve(Solver *solv, Queue *job)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- int i;
- int oldnrules, initialnrules;
- Map addedmap; /* '1' == have pkg-rules for solvable */
- Map installcandidatemap;
- Id how, what, select, name, weak, p, pp, d;
- Queue q;
- Solvable *s;
- Rule *r;
- int now, solve_start;
- int needduprules = 0;
- int hasbestinstalljob = 0;
-
- solve_start = solv_timems(0);
-
- /* log solver options */
- POOL_DEBUG(SOLV_DEBUG_STATS, "solver started\n");
- POOL_DEBUG(SOLV_DEBUG_STATS, "dosplitprovides=%d, noupdateprovide=%d, noinfarchcheck=%d\n", solv->dosplitprovides, solv->noupdateprovide, solv->noinfarchcheck);
- POOL_DEBUG(SOLV_DEBUG_STATS, "allowuninstall=%d, allowdowngrade=%d, allownamechange=%d, allowarchchange=%d, allowvendorchange=%d\n", solv->allowuninstall, solv->allowdowngrade, solv->allownamechange, solv->allowarchchange, solv->allowvendorchange);
- POOL_DEBUG(SOLV_DEBUG_STATS, "promoteepoch=%d, forbidselfconflicts=%d\n", pool->promoteepoch, pool->forbidselfconflicts);
- POOL_DEBUG(SOLV_DEBUG_STATS, "obsoleteusesprovides=%d, implicitobsoleteusesprovides=%d, obsoleteusescolors=%d, implicitobsoleteusescolors=%d\n", pool->obsoleteusesprovides, pool->implicitobsoleteusesprovides, pool->obsoleteusescolors, pool->implicitobsoleteusescolors);
- POOL_DEBUG(SOLV_DEBUG_STATS, "dontinstallrecommended=%d, addalreadyrecommended=%d\n", solv->dontinstallrecommended, solv->addalreadyrecommended);
-
- /* create whatprovides if not already there */
- if (!pool->whatprovides)
- pool_createwhatprovides(pool);
-
- /* create obsolete index */
- policy_create_obsolete_index(solv);
-
- /* remember job */
- queue_free(&solv->job);
- queue_init_clone(&solv->job, job);
- solv->pooljobcnt = pool->pooljobs.count;
- if (pool->pooljobs.count)
- queue_insertn(&solv->job, 0, pool->pooljobs.count, pool->pooljobs.elements);
- job = &solv->job;
-
- /* free old stuff in jase we re-run a solver */
- queuep_free(&solv->update_targets);
- queuep_free(&solv->cleandeps_updatepkgs);
- queue_empty(&solv->ruleassertions);
- solv->bestrules_pkg = solv_free(solv->bestrules_pkg);
- solv->yumobsrules_info = solv_free(solv->yumobsrules_info);
- solv->choicerules_ref = solv_free(solv->choicerules_ref);
- if (solv->noupdate.size)
- map_empty(&solv->noupdate);
- map_zerosize(&solv->multiversion);
- solv->updatemap_all = 0;
- map_zerosize(&solv->updatemap);
- solv->bestupdatemap_all = 0;
- map_zerosize(&solv->bestupdatemap);
- solv->fixmap_all = 0;
- map_zerosize(&solv->fixmap);
- solv->dupmap_all = 0;
- map_zerosize(&solv->dupmap);
- map_zerosize(&solv->dupinvolvedmap);
- solv->droporphanedmap_all = 0;
- map_zerosize(&solv->droporphanedmap);
- solv->allowuninstall_all = 0;
- map_zerosize(&solv->allowuninstallmap);
- map_zerosize(&solv->cleandepsmap);
- map_zerosize(&solv->weakrulemap);
- queue_empty(&solv->weakruleq);
- solv->watches = solv_free(solv->watches);
- queue_empty(&solv->ruletojob);
- if (solv->decisionq.count)
- memset(solv->decisionmap, 0, pool->nsolvables * sizeof(Id));
- queue_empty(&solv->decisionq);
- queue_empty(&solv->decisionq_why);
- solv->decisioncnt_jobs = solv->decisioncnt_update = solv->decisioncnt_keep = solv->decisioncnt_resolve = solv->decisioncnt_weak = solv->decisioncnt_orphan = 0;
- queue_empty(&solv->learnt_why);
- queue_empty(&solv->learnt_pool);
- queue_empty(&solv->branches);
- solv->propagate_index = 0;
- queue_empty(&solv->problems);
- queue_empty(&solv->solutions);
- queue_empty(&solv->orphaned);
- solv->stats_learned = solv->stats_unsolvable = 0;
- if (solv->recommends_index)
- {
- map_empty(&solv->recommendsmap);
- map_empty(&solv->suggestsmap);
- queuep_free(&solv->recommendscplxq);
- queuep_free(&solv->suggestscplxq);
- solv->recommends_index = 0;
- }
- queuep_free(&solv->brokenorphanrules);
- solv->specialupdaters = solv_free(solv->specialupdaters);
-
-
- /*
- * create basic rule set of all involved packages
- * use addedmap bitmap to make sure we don't create rules twice
- */
-
- /* create multiversion map if needed */
- solver_calculate_multiversionmap(pool, job, &solv->multiversion);
-
- map_init(&addedmap, pool->nsolvables);
- MAPSET(&addedmap, SYSTEMSOLVABLE);
-
- map_init(&installcandidatemap, pool->nsolvables);
- queue_init(&q);
-
- now = solv_timems(0);
- /*
- * create rules for all package that could be involved with the solving
- * so called: pkg rules
- *
- */
- initialnrules = solv->pkgrules_end ? solv->pkgrules_end : 1;
- if (initialnrules > 1)
- deduceq2addedmap(solv, &addedmap);
- if (solv->nrules != initialnrules)
- solver_shrinkrules(solv, initialnrules);
- solv->nrules = initialnrules;
- solv->pkgrules_end = 0;
-
- if (installed)
- {
- /* check for update/verify jobs as they need to be known early */
- /* also setup the droporphaned map, we need it when creating update rules */
- for (i = 0; i < job->count; i += 2)
- {
- how = job->elements[i];
- what = job->elements[i + 1];
- select = how & SOLVER_SELECTMASK;
- switch (how & SOLVER_JOBMASK)
- {
- case SOLVER_VERIFY:
- if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid))
- solv->fixmap_all = 1;
- FOR_JOB_SELECT(p, pp, select, what)
- {
- s = pool->solvables + p;
- if (s->repo != installed)
- continue;
- if (!solv->fixmap.size)
- map_grow(&solv->fixmap, installed->end - installed->start);
- MAPSET(&solv->fixmap, p - installed->start);
- }
- break;
- case SOLVER_UPDATE:
- if (select == SOLVER_SOLVABLE_ALL)
- {
- solv->updatemap_all = 1;
- if (how & SOLVER_FORCEBEST)
- solv->bestupdatemap_all = 1;
- if (how & SOLVER_CLEANDEPS)
- {
- FOR_REPO_SOLVABLES(installed, p, s)
- add_cleandeps_package(solv, p);
- }
- }
- else if (select == SOLVER_SOLVABLE_REPO)
- {
- Repo *repo = pool_id2repo(pool, what);
- if (!repo)
- break;
- if (repo == installed && !(how & SOLVER_TARGETED))
- {
- solv->updatemap_all = 1;
- if (how & SOLVER_FORCEBEST)
- solv->bestupdatemap_all = 1;
- if (how & SOLVER_CLEANDEPS)
- {
- FOR_REPO_SOLVABLES(installed, p, s)
- add_cleandeps_package(solv, p);
- }
- break;
- }
- if (solv->noautotarget && !(how & SOLVER_TARGETED))
- break;
- /* targeted update */
- FOR_REPO_SOLVABLES(repo, p, s)
- add_update_target(solv, p, how);
- }
- else
- {
- if (!(how & SOLVER_TARGETED))
- {
- int targeted = 1;
- FOR_JOB_SELECT(p, pp, select, what)
- {
- s = pool->solvables + p;
- if (s->repo != installed)
- continue;
- if (!solv->updatemap.size)
- map_grow(&solv->updatemap, installed->end - installed->start);
- MAPSET(&solv->updatemap, p - installed->start);
- if (how & SOLVER_FORCEBEST)
- {
- if (!solv->bestupdatemap.size)
- map_grow(&solv->bestupdatemap, installed->end - installed->start);
- MAPSET(&solv->bestupdatemap, p - installed->start);
- }
- if (how & SOLVER_CLEANDEPS)
- add_cleandeps_package(solv, p);
- targeted = 0;
- }
- if (!targeted || solv->noautotarget)
- break;
- }
- FOR_JOB_SELECT(p, pp, select, what)
- add_update_target(solv, p, how);
- }
- break;
- case SOLVER_DROP_ORPHANED:
- if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
- solv->droporphanedmap_all = 1;
- FOR_JOB_SELECT(p, pp, select, what)
- {
- s = pool->solvables + p;
- if (s->repo != installed)
- continue;
- if (!solv->droporphanedmap.size)
- map_grow(&solv->droporphanedmap, installed->end - installed->start);
- MAPSET(&solv->droporphanedmap, p - installed->start);
- }
- break;
- case SOLVER_ALLOWUNINSTALL:
- if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid))
- solv->allowuninstall_all = 1;
- FOR_JOB_SELECT(p, pp, select, what)
- {
- s = pool->solvables + p;
- if (s->repo != installed)
- continue;
- if (!solv->allowuninstallmap.size)
- map_grow(&solv->allowuninstallmap, installed->end - installed->start);
- MAPSET(&solv->allowuninstallmap, p - installed->start);
- }
- break;
- default:
- break;
- }
- }
-
- if (solv->update_targets)
- transform_update_targets(solv);
-
- oldnrules = solv->nrules;
- FOR_REPO_SOLVABLES(installed, p, s)
- solver_addpkgrulesforsolvable(solv, s, &addedmap);
- POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for installed solvables\n", solv->nrules - oldnrules);
- oldnrules = solv->nrules;
- FOR_REPO_SOLVABLES(installed, p, s)
- solver_addpkgrulesforupdaters(solv, s, &addedmap, 1);
- POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for updaters of installed solvables\n", solv->nrules - oldnrules);
- }
-
- /*
- * create rules for all packages involved in the job
- * (to be installed or removed)
- */
-
- oldnrules = solv->nrules;
- for (i = 0; i < job->count; i += 2)
- {
- how = job->elements[i];
- what = job->elements[i + 1];
- select = how & SOLVER_SELECTMASK;
-
- switch (how & SOLVER_JOBMASK)
- {
- case SOLVER_INSTALL:
- FOR_JOB_SELECT(p, pp, select, what)
- {
- MAPSET(&installcandidatemap, p);
- solver_addpkgrulesforsolvable(solv, pool->solvables + p, &addedmap);
- }
- break;
- case SOLVER_DISTUPGRADE:
- if (select == SOLVER_SOLVABLE_ALL)
- {
- solv->dupmap_all = 1;
- solv->updatemap_all = 1;
- if (how & SOLVER_FORCEBEST)
- solv->bestupdatemap_all = 1;
- }
- if ((how & SOLVER_TARGETED) != 0)
- needduprules = 1;
- if (!solv->dupmap_all || solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size || solv->keep_orphans)
- needduprules = 1;
- break;
- default:
- break;
- }
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for packages involved in a job\n", solv->nrules - oldnrules);
-
-
- /*
- * add rules for suggests, enhances
- */
- oldnrules = solv->nrules;
- solver_addpkgrulesforweak(solv, &addedmap);
- POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules because of weak dependencies\n", solv->nrules - oldnrules);
-
-#ifdef ENABLE_LINKED_PKGS
- oldnrules = solv->nrules;
- solver_addpkgrulesforlinked(solv, &addedmap);
- POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules because of linked packages\n", solv->nrules - oldnrules);
-#endif
-
- /*
- * first pass done, we now have all the pkg rules we need.
- * unify existing rules before going over all job rules and
- * policy rules.
- * at this point the system is always solvable,
- * as an empty system (remove all packages) is a valid solution
- */
-
- IF_POOLDEBUG (SOLV_DEBUG_STATS)
- {
- int possible = 0, installable = 0;
- for (i = 1; i < pool->nsolvables; i++)
- {
- if (pool_installable(pool, pool->solvables + i))
- installable++;
- if (MAPTST(&addedmap, i))
- possible++;
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "%d of %d installable solvables considered for solving\n", possible, installable);
- }
-
- if (solv->nrules > initialnrules)
- solver_unifyrules(solv); /* remove duplicate pkg rules */
- solv->pkgrules_end = solv->nrules; /* mark end of pkg rules */
-
- if (solv->nrules > initialnrules)
- addedmap2deduceq(solv, &addedmap); /* so that we can recreate the addedmap */
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "pkg rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
- POOL_DEBUG(SOLV_DEBUG_STATS, "pkg rule creation took %d ms\n", solv_timems(now));
-
- /* create dup maps if needed. We need the maps early to create our
- * update rules */
- if (needduprules)
- solver_createdupmaps(solv);
-
- /*
- * create feature rules
- *
- * foreach installed:
- * create assertion (keep installed, if no update available)
- * or
- * create update rule (A|update1(A)|update2(A)|...)
- *
- * those are used later on to keep a version of the installed packages in
- * best effort mode
- */
-
- solv->featurerules = solv->nrules; /* mark start of feature rules */
- if (installed)
- {
- /* foreach possibly installed solvable */
- for (i = installed->start, s = pool->solvables + i; i < installed->end; i++, s++)
- {
- if (s->repo != installed)
- {
- solver_addrule(solv, 0, 0, 0); /* create dummy rule */
- continue;
- }
- solver_addupdaterule(solv, s, 1); /* allow s to be updated */
- }
- /* make sure we accounted for all rules */
- assert(solv->nrules - solv->featurerules == installed->end - installed->start);
- }
- solv->featurerules_end = solv->nrules;
-
- /*
- * Add update rules for installed solvables
- *
- * almost identical to feature rules
- * except that downgrades/archchanges/vendorchanges are not allowed
- */
-
- solv->updaterules = solv->nrules;
-
- if (installed)
- { /* foreach installed solvables */
- /* we create all update rules, but disable some later on depending on the job */
- for (i = installed->start, s = pool->solvables + i; i < installed->end; i++, s++)
- {
- Rule *sr;
-
- if (s->repo != installed)
- {
- solver_addrule(solv, 0, 0, 0); /* create dummy rule */
- continue;
- }
- solver_addupdaterule(solv, s, 0); /* allowall = 0: downgrades not allowed */
- /*
- * check for and remove duplicate
- */
- r = solv->rules + solv->nrules - 1; /* r: update rule */
- sr = r - (installed->end - installed->start); /* sr: feature rule */
- if (!r->p)
- {
- if (sr->p)
- memset(sr, 0, sizeof(*sr)); /* no feature rules without update rules */
- continue;
- }
- /* it's also orphaned if the feature rule consists just of the installed package */
- if (!solv->dupmap_all && sr->p == i && !sr->d && !sr->w2)
- queue_push(&solv->orphaned, i);
- if (!solver_rulecmp(solv, r, sr))
- memset(sr, 0, sizeof(*sr)); /* delete unneeded feature rule */
- else
- solver_disablerule(solv, sr); /* disable feature rule for now */
- }
- /* consistency check: we added a rule for _every_ installed solvable */
- assert(solv->nrules - solv->updaterules == installed->end - installed->start);
- }
- solv->updaterules_end = solv->nrules;
-
-
- /*
- * now add all job rules
- */
-
- solv->jobrules = solv->nrules;
- for (i = 0; i < job->count; i += 2)
- {
- oldnrules = solv->nrules;
-
- if (i && i == solv->pooljobcnt)
- POOL_DEBUG(SOLV_DEBUG_JOB, "end of pool jobs\n");
- how = job->elements[i];
- what = job->elements[i + 1];
- weak = how & SOLVER_WEAK;
- select = how & SOLVER_SELECTMASK;
- switch (how & SOLVER_JOBMASK)
- {
- case SOLVER_INSTALL:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: %sinstall %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
- if ((how & SOLVER_CLEANDEPS) != 0 && !solv->cleandepsmap.size && installed)
- map_grow(&solv->cleandepsmap, installed->end - installed->start);
- if (select == SOLVER_SOLVABLE)
- {
- p = what;
- d = 0;
- }
-#ifdef ENABLE_COMPLEX_DEPS
- else if ((select == SOLVER_SOLVABLE_PROVIDES || select == SOLVER_SOLVABLE_NAME) && pool_is_complex_dep(pool, what))
- {
- if (add_complex_jobrules(solv, what, select == SOLVER_SOLVABLE_NAME ? CPLXDEPS_NAME : 0, i, weak))
- if (how & SOLVER_FORCEBEST)
- hasbestinstalljob = 1;
- break;
- }
-#endif
- else
- {
- queue_empty(&q);
- FOR_JOB_SELECT(p, pp, select, what)
- queue_push(&q, p);
- if (!q.count)
- {
- if (select == SOLVER_SOLVABLE_ONE_OF)
- break; /* ignore empty installs */
- /* no candidate found or unsupported, make this an impossible rule */
- queue_push(&q, -SYSTEMSOLVABLE);
- }
- p = queue_shift(&q); /* get first candidate */
- d = !q.count ? 0 : pool_queuetowhatprovides(pool, &q); /* internalize */
- }
- /* force install of namespace supplements hack */
- if (select == SOLVER_SOLVABLE_PROVIDES && !d && (p == SYSTEMSOLVABLE || p == -SYSTEMSOLVABLE) && ISRELDEP(what))
- {
- Reldep *rd = GETRELDEP(pool, what);
- if (rd->flags == REL_NAMESPACE)
- {
- p = SYSTEMSOLVABLE;
- if (!solv->installsuppdepq)
- {
- solv->installsuppdepq = solv_calloc(1, sizeof(Queue));
- queue_init(solv->installsuppdepq);
- }
- queue_pushunique(solv->installsuppdepq, rd->evr == 0 ? rd->name : what);
- }
- }
- solver_addjobrule(solv, p, 0, d, i, weak);
- if (how & SOLVER_FORCEBEST)
- hasbestinstalljob = 1;
- break;
- case SOLVER_ERASE:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: %s%serase %s\n", weak ? "weak " : "", how & SOLVER_CLEANDEPS ? "clean deps " : "", solver_select2str(pool, select, what));
- if ((how & SOLVER_CLEANDEPS) != 0 && !solv->cleandepsmap.size && installed)
- map_grow(&solv->cleandepsmap, installed->end - installed->start);
- /* specific solvable: by id or by nevra */
- name = (select == SOLVER_SOLVABLE || (select == SOLVER_SOLVABLE_NAME && ISRELDEP(what))) ? 0 : -1;
- if (select == SOLVER_SOLVABLE_ALL) /* hmmm ;) */
- {
- FOR_POOL_SOLVABLES(p)
- solver_addjobrule(solv, -p, 0, 0, i, weak);
- }
- else if (select == SOLVER_SOLVABLE_REPO)
- {
- Repo *repo = pool_id2repo(pool, what);
- if (repo)
- FOR_REPO_SOLVABLES(repo, p, s)
- solver_addjobrule(solv, -p, 0, 0, i, weak);
- }
-#ifdef ENABLE_COMPLEX_DEPS
- else if ((select == SOLVER_SOLVABLE_PROVIDES || select == SOLVER_SOLVABLE_NAME) && pool_is_complex_dep(pool, what))
- {
- /* no special "erase a specific solvable" handling? */
- add_complex_jobrules(solv, what, select == SOLVER_SOLVABLE_NAME ? (CPLXDEPS_NAME | CPLXDEPS_TODNF | CPLXDEPS_INVERT) : (CPLXDEPS_TODNF | CPLXDEPS_INVERT), i, weak);
- break;
- }
-#endif
- FOR_JOB_SELECT(p, pp, select, what)
- {
- s = pool->solvables + p;
- if (installed && s->repo == installed)
- name = !name ? s->name : -1;
- solver_addjobrule(solv, -p, 0, 0, i, weak);
- }
- /* special case for "erase a specific solvable": we also
- * erase all other solvables with that name, so that they
- * don't get picked up as replacement.
- * name is > 0 if exactly one installed solvable matched.
- */
- /* XXX: look also at packages that obsolete this package? */
- if (name > 0)
- {
- int j, k;
- k = solv->nrules;
- FOR_PROVIDES(p, pp, name)
- {
- s = pool->solvables + p;
- if (s->name != name)
- continue;
- /* keep other versions installed */
- if (s->repo == installed)
- continue;
- /* keep installcandidates of other jobs */
- if (MAPTST(&installcandidatemap, p))
- continue;
- /* don't add the same rule twice */
- for (j = oldnrules; j < k; j++)
- if (solv->rules[j].p == -p)
- break;
- if (j == k)
- solver_addjobrule(solv, -p, 0, 0, i, weak); /* remove by id */
- }
- }
- break;
-
- case SOLVER_UPDATE:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: %supdate %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
- break;
- case SOLVER_VERIFY:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: %sverify %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
- break;
- case SOLVER_WEAKENDEPS:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: %sweaken deps %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
- if (select != SOLVER_SOLVABLE)
- break;
- s = pool->solvables + what;
- weaken_solvable_deps(solv, what);
- break;
- case SOLVER_MULTIVERSION:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: %smultiversion %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
- break;
- case SOLVER_LOCK:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: %slock %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
- if (select == SOLVER_SOLVABLE_ALL)
- {
- FOR_POOL_SOLVABLES(p)
- solver_addjobrule(solv, installed && pool->solvables[p].repo == installed ? p : -p, 0, 0, i, weak);
- }
- else if (select == SOLVER_SOLVABLE_REPO)
- {
- Repo *repo = pool_id2repo(pool, what);
- if (repo)
- FOR_REPO_SOLVABLES(repo, p, s)
- solver_addjobrule(solv, installed && pool->solvables[p].repo == installed ? p : -p, 0, 0, i, weak);
- }
- FOR_JOB_SELECT(p, pp, select, what)
- solver_addjobrule(solv, installed && pool->solvables[p].repo == installed ? p : -p, 0, 0, i, weak);
- break;
- case SOLVER_DISTUPGRADE:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: distupgrade %s\n", solver_select2str(pool, select, what));
- break;
- case SOLVER_DROP_ORPHANED:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: drop orphaned %s\n", solver_select2str(pool, select, what));
- break;
- case SOLVER_USERINSTALLED:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: user installed %s\n", solver_select2str(pool, select, what));
- break;
- case SOLVER_ALLOWUNINSTALL:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: allowuninstall %s\n", solver_select2str(pool, select, what));
- break;
- default:
- POOL_DEBUG(SOLV_DEBUG_JOB, "job: unknown job\n");
- break;
- }
-
- IF_POOLDEBUG (SOLV_DEBUG_JOB)
- {
- int j;
- if (solv->nrules == oldnrules)
- POOL_DEBUG(SOLV_DEBUG_JOB, " - no rule created\n");
- for (j = oldnrules; j < solv->nrules; j++)
- {
- POOL_DEBUG(SOLV_DEBUG_JOB, " - job ");
- solver_printrule(solv, SOLV_DEBUG_JOB, solv->rules + j);
- }
- }
- }
- assert(solv->ruletojob.count == solv->nrules - solv->jobrules);
- solv->jobrules_end = solv->nrules;
-
- /* now create infarch and dup rules */
- if (!solv->noinfarchcheck)
- {
- solver_addinfarchrules(solv, &addedmap);
-#if 0
- if (pool->implicitobsoleteusescolors)
- {
- /* currently doesn't work well with infarch rules, so make
- * them weak */
- for (i = solv->infarchrules; i < solv->infarchrules_end; i++)
- queue_push(&solv->weakruleq, i);
- }
-#endif
- }
- else
- solv->infarchrules = solv->infarchrules_end = solv->nrules;
-
- if (needduprules)
- solver_addduprules(solv, &addedmap);
- else
- solv->duprules = solv->duprules_end = solv->nrules;
-
- if (solv->bestupdatemap_all || solv->bestupdatemap.size || hasbestinstalljob)
- solver_addbestrules(solv, hasbestinstalljob);
- else
- solv->bestrules = solv->bestrules_end = solv->nrules;
-
- if (needduprules)
- solver_freedupmaps(solv); /* no longer needed */
-
- if (solv->do_yum_obsoletes)
- solver_addyumobsrules(solv);
- else
- solv->yumobsrules = solv->yumobsrules_end = solv->nrules;
-
- if (1)
- solver_addchoicerules(solv);
- else
- solv->choicerules = solv->choicerules_end = solv->nrules;
-
- if (0)
- {
- for (i = solv->featurerules; i < solv->nrules; i++)
- solver_printruleclass(solv, SOLV_DEBUG_RESULT, solv->rules + i);
- }
- /* all rules created
- * --------------------------------------------------------------
- * prepare for solving
- */
-
- /* free unneeded memory */
- map_free(&addedmap);
- map_free(&installcandidatemap);
- queue_free(&q);
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "%d pkg rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules\n", solv->pkgrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules);
- POOL_DEBUG(SOLV_DEBUG_STATS, "overall rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
-
- /* create weak map */
- if (solv->weakruleq.count)
- {
- map_grow(&solv->weakrulemap, solv->nrules);
- for (i = 0; i < solv->weakruleq.count; i++)
- {
- p = solv->weakruleq.elements[i];
- MAPSET(&solv->weakrulemap, p);
- }
- }
-
- /* enable cleandepsmap creation if we have updatepkgs */
- if (solv->cleandeps_updatepkgs && !solv->cleandepsmap.size)
- map_grow(&solv->cleandepsmap, installed->end - installed->start);
- /* no mistakes */
- if (solv->cleandeps_mistakes)
- {
- queue_free(solv->cleandeps_mistakes);
- solv->cleandeps_mistakes = solv_free(solv->cleandeps_mistakes);
- }
-
- /* all new rules are learnt after this point */
- solv->learntrules = solv->nrules;
-
- /* create watches chains */
- makewatches(solv);
-
- /* create assertion index. it is only used to speed up
- * makeruledecsions() a bit */
- for (i = 1, r = solv->rules + i; i < solv->nrules; i++, r++)
- if (r->p && !r->w2 && (r->d == 0 || r->d == -1))
- queue_push(&solv->ruleassertions, i);
-
- /* disable update rules that conflict with our job */
- solver_disablepolicyrules(solv);
-
- /* break orphans if requested */
- if (solv->dupmap_all && solv->orphaned.count && solv->break_orphans)
- solver_breakorphans(solv);
-
- /*
- * ********************************************
- * solve!
- * ********************************************
- */
-
- now = solv_timems(0);
- solver_run_sat(solv, 1, solv->dontinstallrecommended ? 0 : 1);
- POOL_DEBUG(SOLV_DEBUG_STATS, "solver took %d ms\n", solv_timems(now));
-
- /*
- * prepare solution queue if there were problems
- */
- solver_prepare_solutions(solv);
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "final solver statistics: %d problems, %d learned rules, %d unsolvable\n", solv->problems.count / 2, solv->stats_learned, solv->stats_unsolvable);
- POOL_DEBUG(SOLV_DEBUG_STATS, "solver_solve took %d ms\n", solv_timems(solve_start));
-
- /* return number of problems */
- return solv->problems.count ? solv->problems.count / 2 : 0;
-}
-
-Transaction *
-solver_create_transaction(Solver *solv)
-{
- return transaction_create_decisionq(solv->pool, &solv->decisionq, &solv->multiversion);
-}
-
-void solver_get_orphaned(Solver *solv, Queue *orphanedq)
-{
- queue_free(orphanedq);
- queue_init_clone(orphanedq, &solv->orphaned);
-}
-
-void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *suggestionsq, int noselected)
-{
- Pool *pool = solv->pool;
- Queue redoq, disabledq;
- int goterase, i;
- Solvable *s;
- Rule *r;
- Map obsmap;
-
- if (!recommendationsq && !suggestionsq)
- return;
-
- map_init(&obsmap, pool->nsolvables);
- if (solv->installed)
- {
- Id obs, *obsp, p, po, ppo;
- for (p = solv->installed->start; p < solv->installed->end; p++)
- {
- s = pool->solvables + p;
- if (s->repo != solv->installed || !s->obsoletes)
- continue;
- if (solv->decisionmap[p] <= 0)
- continue;
- if (solv->multiversion.size && MAPTST(&solv->multiversion, p))
- continue;
- obsp = s->repo->idarraydata + s->obsoletes;
- /* foreach obsoletes */
- while ((obs = *obsp++) != 0)
- FOR_PROVIDES(po, ppo, obs)
- MAPSET(&obsmap, po);
- }
- }
-
- queue_init(&redoq);
- queue_init(&disabledq);
- goterase = 0;
- /* disable all erase jobs (including weak "keep uninstalled" rules) */
- for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++)
- {
- if (r->d < 0) /* disabled ? */
- continue;
- if (r->p >= 0) /* install job? */
- continue;
- queue_push(&disabledq, i);
- solver_disablerule(solv, r);
- goterase++;
- }
-
- if (goterase)
- {
- enabledisablelearntrules(solv);
- removedisabledconflicts(solv, &redoq);
- }
-
- /*
- * find recommended packages
- */
- if (recommendationsq)
- {
- Id rec, *recp, p, pp;
-
- queue_empty(recommendationsq);
- /* create map of all recommened packages */
- solv->recommends_index = -1;
- MAPZERO(&solv->recommendsmap);
-
- /* put all packages the solver already chose in the map */
- if (solv->decisioncnt_weak)
- {
- for (i = solv->decisioncnt_weak; i < solv->decisioncnt_orphan; i++)
- {
- Id why;
- why = solv->decisionq_why.elements[i];
- if (why)
- continue; /* forced by unit rule or dep resolving */
- p = solv->decisionq.elements[i];
- if (p < 0)
- continue;
- MAPSET(&solv->recommendsmap, p);
- }
- }
-
- for (i = 0; i < solv->decisionq.count; i++)
- {
- p = solv->decisionq.elements[i];
- if (p < 0)
- continue;
- s = pool->solvables + p;
- if (s->recommends)
- {
- recp = s->repo->idarraydata + s->recommends;
- while ((rec = *recp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, rec))
- {
- do_complex_recommendations(solv, rec, &solv->recommendsmap, noselected);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, rec)
- if (solv->decisionmap[p] > 0)
- break;
- if (p)
- {
- if (!noselected)
- {
- FOR_PROVIDES(p, pp, rec)
- if (solv->decisionmap[p] > 0)
- MAPSET(&solv->recommendsmap, p);
- }
- continue; /* p != 0: already fulfilled */
- }
- FOR_PROVIDES(p, pp, rec)
- MAPSET(&solv->recommendsmap, p);
- }
- }
- }
- for (i = 1; i < pool->nsolvables; i++)
- {
- if (solv->decisionmap[i] < 0)
- continue;
- if (solv->decisionmap[i] > 0 && noselected)
- continue;
- if (MAPTST(&obsmap, i))
- continue;
- s = pool->solvables + i;
- if (!MAPTST(&solv->recommendsmap, i))
- {
- if (!s->supplements)
- continue;
- if (!pool_installable(pool, s))
- continue;
- if (!solver_is_supplementing(solv, s))
- continue;
- }
- queue_push(recommendationsq, i);
- }
- /* we use MODE_SUGGEST here so that repo prio is ignored */
- policy_filter_unwanted(solv, recommendationsq, POLICY_MODE_SUGGEST);
- }
-
- /*
- * find suggested packages
- */
-
- if (suggestionsq)
- {
- Id sug, *sugp, p, pp;
-
- queue_empty(suggestionsq);
- /* create map of all suggests that are still open */
- solv->recommends_index = -1;
- MAPZERO(&solv->suggestsmap);
- for (i = 0; i < solv->decisionq.count; i++)
- {
- p = solv->decisionq.elements[i];
- if (p < 0)
- continue;
- s = pool->solvables + p;
- if (s->suggests)
- {
- sugp = s->repo->idarraydata + s->suggests;
- while ((sug = *sugp++) != 0)
- {
-#ifdef ENABLE_COMPLEX_DEPS
- if (pool_is_complex_dep(pool, sug))
- {
- do_complex_recommendations(solv, sug, &solv->suggestsmap, noselected);
- continue;
- }
-#endif
- FOR_PROVIDES(p, pp, sug)
- if (solv->decisionmap[p] > 0)
- break;
- if (p)
- {
- if (!noselected)
- {
- FOR_PROVIDES(p, pp, sug)
- if (solv->decisionmap[p] > 0)
- MAPSET(&solv->suggestsmap, p);
- }
- continue; /* already fulfilled */
- }
- FOR_PROVIDES(p, pp, sug)
- MAPSET(&solv->suggestsmap, p);
- }
- }
- }
- for (i = 1; i < pool->nsolvables; i++)
- {
- if (solv->decisionmap[i] < 0)
- continue;
- if (solv->decisionmap[i] > 0 && noselected)
- continue;
- if (MAPTST(&obsmap, i))
- continue;
- s = pool->solvables + i;
- if (!MAPTST(&solv->suggestsmap, i))
- {
- if (!s->enhances)
- continue;
- if (!pool_installable(pool, s))
- continue;
- if (!solver_is_enhancing(solv, s))
- continue;
- }
- queue_push(suggestionsq, i);
- }
- policy_filter_unwanted(solv, suggestionsq, POLICY_MODE_SUGGEST);
- }
-
- /* undo removedisabledconflicts */
- if (redoq.count)
- undo_removedisabledconflicts(solv, &redoq);
- queue_free(&redoq);
-
- /* undo job rule disabling */
- for (i = 0; i < disabledq.count; i++)
- solver_enablerule(solv, solv->rules + disabledq.elements[i]);
- queue_free(&disabledq);
- map_free(&obsmap);
-}
-
-
-/***********************************************************************/
-/* disk usage computations */
-
-/*-------------------------------------------------------------------
- *
- * calculate DU changes
- */
-
-void
-solver_calc_duchanges(Solver *solv, DUChanges *mps, int nmps)
-{
- Map installedmap;
-
- solver_create_state_maps(solv, &installedmap, 0);
- pool_calc_duchanges(solv->pool, &installedmap, mps, nmps);
- map_free(&installedmap);
-}
-
-
-/*-------------------------------------------------------------------
- *
- * calculate changes in install size
- */
-
-int
-solver_calc_installsizechange(Solver *solv)
-{
- Map installedmap;
- int change;
-
- solver_create_state_maps(solv, &installedmap, 0);
- change = pool_calc_installsizechange(solv->pool, &installedmap);
- map_free(&installedmap);
- return change;
-}
-
-void
-solver_create_state_maps(Solver *solv, Map *installedmap, Map *conflictsmap)
-{
- pool_create_state_maps(solv->pool, &solv->decisionq, installedmap, conflictsmap);
-}
-
-void
-solver_trivial_installable(Solver *solv, Queue *pkgs, Queue *res)
-{
- Pool *pool = solv->pool;
- Map installedmap;
- int i;
- pool_create_state_maps(pool, &solv->decisionq, &installedmap, 0);
- pool_trivial_installable_multiversionmap(pool, &installedmap, pkgs, res, solv->multiversion.size ? &solv->multiversion : 0);
- for (i = 0; i < res->count; i++)
- if (res->elements[i] != -1)
- {
- Solvable *s = pool->solvables + pkgs->elements[i];
- if (!strncmp("patch:", pool_id2str(pool, s->name), 6) && solvable_is_irrelevant_patch(s, &installedmap))
- res->elements[i] = -1;
- }
- map_free(&installedmap);
-}
-
-/*-------------------------------------------------------------------
- *
- * decision introspection
- */
-
-int
-solver_get_decisionlevel(Solver *solv, Id p)
-{
- return solv->decisionmap[p];
-}
-
-void
-solver_get_decisionqueue(Solver *solv, Queue *decisionq)
-{
- queue_free(decisionq);
- queue_init_clone(decisionq, &solv->decisionq);
-}
-
-int
-solver_get_lastdecisionblocklevel(Solver *solv)
-{
- Id p;
- if (solv->decisionq.count == 0)
- return 0;
- p = solv->decisionq.elements[solv->decisionq.count - 1];
- if (p < 0)
- p = -p;
- return solv->decisionmap[p] < 0 ? -solv->decisionmap[p] : solv->decisionmap[p];
-}
-
-void
-solver_get_decisionblock(Solver *solv, int level, Queue *decisionq)
-{
- Id p;
- int i;
-
- queue_empty(decisionq);
- for (i = 0; i < solv->decisionq.count; i++)
- {
- p = solv->decisionq.elements[i];
- if (p < 0)
- p = -p;
- if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
- break;
- }
- if (i == solv->decisionq.count)
- return;
- for (i = 0; i < solv->decisionq.count; i++)
- {
- p = solv->decisionq.elements[i];
- if (p < 0)
- p = -p;
- if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
- queue_push(decisionq, p);
- else
- break;
- }
-}
-
-int
-solver_describe_decision(Solver *solv, Id p, Id *infop)
-{
- int i;
- Id pp, why;
-
- if (infop)
- *infop = 0;
- if (!solv->decisionmap[p])
- return SOLVER_REASON_UNRELATED;
- pp = solv->decisionmap[p] < 0 ? -p : p;
- for (i = 0; i < solv->decisionq.count; i++)
- if (solv->decisionq.elements[i] == pp)
- break;
- if (i == solv->decisionq.count) /* just in case... */
- return SOLVER_REASON_UNRELATED;
- why = solv->decisionq_why.elements[i];
- if (infop)
- *infop = why > 0 ? why : -why;
- if (why > 0)
- return SOLVER_REASON_UNIT_RULE;
- why = -why;
- if (i == 0)
- return SOLVER_REASON_KEEP_INSTALLED; /* the systemsolvable */
- if (i < solv->decisioncnt_update)
- return SOLVER_REASON_RESOLVE_JOB;
- if (i < solv->decisioncnt_keep)
- {
- if (why == 0 && pp < 0)
- return SOLVER_REASON_CLEANDEPS_ERASE;
- return SOLVER_REASON_UPDATE_INSTALLED;
- }
- if (i < solv->decisioncnt_resolve)
- {
- if (solv->focus_installed && i >= solv->decisioncnt_jobs)
- return SOLVER_REASON_RESOLVE_JOB;
- if (why == 0 && pp < 0)
- return SOLVER_REASON_CLEANDEPS_ERASE;
- return SOLVER_REASON_KEEP_INSTALLED;
- }
- if (why > 0)
- return SOLVER_REASON_RESOLVE;
- /* weak or orphaned */
- if (i < solv->decisioncnt_orphan)
- return SOLVER_REASON_WEAKDEP;
- return SOLVER_REASON_RESOLVE_ORPHAN;
-}
-
-
-void
-solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq)
-{
- Pool *pool = solv->pool;
- int i;
- int level = solv->decisionmap[p];
- int decisionno;
- Solvable *s;
-
- queue_empty(whyq);
- if (level < 0)
- return; /* huh? */
- for (decisionno = 0; decisionno < solv->decisionq.count; decisionno++)
- if (solv->decisionq.elements[decisionno] == p)
- break;
- if (decisionno == solv->decisionq.count)
- return; /* huh? */
- if (decisionno < solv->decisioncnt_weak || decisionno >= solv->decisioncnt_orphan)
- return; /* huh? */
-
- /* 1) list all packages that recommend us */
- for (i = 1; i < pool->nsolvables; i++)
- {
- Id *recp, rec, pp2, p2;
- if (solv->decisionmap[i] < 0 || solv->decisionmap[i] >= level)
- continue;
- s = pool->solvables + i;
- if (!s->recommends)
- continue;
- if (!solv->addalreadyrecommended && s->repo == solv->installed)
- continue;
- recp = s->repo->idarraydata + s->recommends;
- while ((rec = *recp++) != 0)
- {
- int found = 0;
- FOR_PROVIDES(p2, pp2, rec)
- {
- if (p2 == p)
- found = 1;
- else
- {
- /* if p2 is already installed, this recommends is ignored */
- if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
- break;
- }
- }
- if (!p2 && found)
- {
- queue_push(whyq, SOLVER_REASON_RECOMMENDED);
- queue_push2(whyq, i, rec);
- }
- }
- }
- /* 2) list all supplements */
- s = pool->solvables + p;
- if (s->supplements && level > 0)
- {
- Id *supp, sup, pp2, p2;
- /* this is a hack. to use solver_dep_fulfilled we temporarily clear
- * everything above our level in the decisionmap */
- for (i = decisionno; i < solv->decisionq.count; i++ )
- {
- p2 = solv->decisionq.elements[i];
- if (p2 > 0)
- solv->decisionmap[p2] = -solv->decisionmap[p2];
- }
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (solver_dep_fulfilled(solv, sup))
- {
- int found = 0;
- /* let's see if this is an easy supp */
- FOR_PROVIDES(p2, pp2, sup)
- {
- if (!solv->addalreadyrecommended && solv->installed)
- {
- if (pool->solvables[p2].repo == solv->installed)
- continue;
- }
- if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
- {
- queue_push(whyq, SOLVER_REASON_SUPPLEMENTED);
- queue_push2(whyq, p2, sup);
- found = 1;
- }
- }
- if (!found)
- {
- /* hard case, just note dependency with no package */
- queue_push(whyq, SOLVER_REASON_SUPPLEMENTED);
- queue_push2(whyq, 0, sup);
- }
- }
- for (i = decisionno; i < solv->decisionq.count; i++)
- {
- p2 = solv->decisionq.elements[i];
- if (p2 > 0)
- solv->decisionmap[p2] = -solv->decisionmap[p2];
- }
- }
-}
-
-void
-pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what)
-{
- Id p, pp;
- how &= SOLVER_SELECTMASK;
- queue_empty(pkgs);
- if (how == SOLVER_SOLVABLE_ALL)
- {
- FOR_POOL_SOLVABLES(p)
- queue_push(pkgs, p);
- }
- else if (how == SOLVER_SOLVABLE_REPO)
- {
- Repo *repo = pool_id2repo(pool, what);
- Solvable *s;
- if (repo)
- FOR_REPO_SOLVABLES(repo, p, s)
- queue_push(pkgs, p);
- }
- else
- {
- FOR_JOB_SELECT(p, pp, how, what)
- queue_push(pkgs, p);
- }
-}
-
-int
-pool_isemptyupdatejob(Pool *pool, Id how, Id what)
-{
- Id p, pp, pi, pip;
- Id select = how & SOLVER_SELECTMASK;
- if ((how & SOLVER_JOBMASK) != SOLVER_UPDATE)
- return 0;
- if (select == SOLVER_SOLVABLE_ALL || select == SOLVER_SOLVABLE_REPO)
- return 0;
- if (!pool->installed)
- return 1;
- FOR_JOB_SELECT(p, pp, select, what)
- if (pool->solvables[p].repo == pool->installed)
- return 0;
- /* hard work */
- FOR_JOB_SELECT(p, pp, select, what)
- {
- Solvable *s = pool->solvables + p;
- FOR_PROVIDES(pi, pip, s->name)
- {
- Solvable *si = pool->solvables + pi;
- if (si->repo != pool->installed || si->name != s->name)
- continue;
- return 0;
- }
- if (s->obsoletes)
- {
- Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(pi, pip, obs)
- {
- Solvable *si = pool->solvables + pi;
- if (si->repo != pool->installed)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, si, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, si))
- continue;
- return 0;
- }
- }
- }
- }
- return 1;
-}
-
-static int
-get_userinstalled_cmp(const void *ap, const void *bp, void *dp)
-{
- return *(Id *)ap - *(Id *)bp;
-}
-
-static int
-get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp)
-{
- Pool *pool = dp;
- return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp));
-}
-
-static int
-get_userinstalled_cmp_namearch(const void *ap, const void *bp, void *dp)
-{
- Pool *pool = dp;
- int r;
- r = strcmp(pool_id2str(pool, ((Id *)ap)[0]), pool_id2str(pool, ((Id *)bp)[0]));
- if (r)
- return r;
- return strcmp(pool_id2str(pool, ((Id *)ap)[1]), pool_id2str(pool, ((Id *)bp)[1]));
-}
-
-static void
-get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags)
-{
- Id lastp = -1, lasta = -1;
- int i, j;
- if (q->count < ((flags & GET_USERINSTALLED_NAMEARCH) ? 4 : 2))
- return;
- if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
- solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), get_userinstalled_cmp_namearch, pool);
- else if ((flags & GET_USERINSTALLED_NAMES) != 0)
- solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool);
- else
- solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0);
- if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
- {
- for (i = j = 0; i < q->count; i += 2)
- if (q->elements[i] != lastp || q->elements[i + 1] != lasta)
- {
- q->elements[j++] = lastp = q->elements[i];
- q->elements[j++] = lasta = q->elements[i + 1];
- }
- }
- else
- {
- for (i = j = 0; i < q->count; i++)
- if (q->elements[i] != lastp)
- q->elements[j++] = lastp = q->elements[i];
- }
- queue_truncate(q, j);
-}
-
-static void
-namearch2solvables(Pool *pool, Queue *q, Queue *qout, int job)
-{
- int i;
- if (!pool->installed)
- return;
- for (i = 0; i < q->count; i += 2)
- {
- Id p, pp, name = q->elements[i], arch = q->elements[i + 1];
- FOR_PROVIDES(p, pp, name)
- {
- Solvable *s = pool->solvables + p;
- if (s->repo != pool->installed || s->name != name || (arch && s->arch != arch))
- continue;
- if (job)
- queue_push(qout, job);
- queue_push(qout, p);
- }
- }
-}
-
-void
-solver_get_userinstalled(Solver *solv, Queue *q, int flags)
-{
- Pool *pool = solv->pool;
- Id p, p2, pp;
- Solvable *s;
- Repo *installed = solv->installed;
- int i, j;
- Map userinstalled;
-
- map_init(&userinstalled, 0);
- queue_empty(q);
- /* first process jobs */
- for (i = 0; i < solv->job.count; i += 2)
- {
- Id how = solv->job.elements[i];
- Id what, select;
- if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
- {
- if (!userinstalled.size)
- map_grow(&userinstalled, installed->end - installed->start);
- what = solv->job.elements[i + 1];
- select = how & SOLVER_SELECTMASK;
- if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
- FOR_REPO_SOLVABLES(installed, p, s)
- MAPSET(&userinstalled, p - installed->start);
- FOR_JOB_SELECT(p, pp, select, what)
- if (pool->solvables[p].repo == installed)
- MAPSET(&userinstalled, p - installed->start);
- continue;
- }
- if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL)
- continue;
- if ((how & SOLVER_NOTBYUSER) != 0)
- continue;
- what = solv->job.elements[i + 1];
- select = how & SOLVER_SELECTMASK;
- FOR_JOB_SELECT(p, pp, select, what)
- if (solv->decisionmap[p] > 0)
- {
- queue_push(q, p);
-#ifdef ENABLE_LINKED_PKGS
- if (has_package_link(pool, pool->solvables + p))
- {
- int j;
- Queue lq;
- queue_init(&lq);
- find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0);
- for (j = 0; j < lq.count; j++)
- if (solv->decisionmap[lq.elements[j]] > 0)
- queue_push(q, lq.elements[j]);
- }
-#endif
- }
- }
- /* now process updates of userinstalled packages */
- if (installed && userinstalled.size)
- {
- for (i = 1; i < solv->decisionq.count; i++)
- {
- p = solv->decisionq.elements[i];
- if (p <= 0)
- continue;
- s = pool->solvables + p;
- if (!s->repo)
- continue;
- if (s->repo == installed)
- {
- if (MAPTST(&userinstalled, p - installed->start))
- queue_push(q, p);
- continue;
- }
- /* new package, check if we replace a userinstalled one */
- FOR_PROVIDES(p2, pp, s->name)
- {
- Solvable *ps = pool->solvables + p2;
- if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
- continue;
- if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
- continue;
- if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- queue_push(q, p);
- break;
- }
- if (!p2 && s->repo != installed && s->obsoletes)
- {
- Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(p2, pp, obs)
- {
- Solvable *ps = pool->solvables + p2;
- if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- queue_push(q, p);
- break;
- }
- if (p2)
- break;
- }
- }
- }
- }
- map_free(&userinstalled);
-
- /* convert to desired output format */
- if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
- {
- int qcount = q->count;
- queue_insertn(q, 0, qcount, 0);
- for (i = j = 0; i < qcount; i++)
- {
- s = pool->solvables + q->elements[i + qcount];
- q->elements[j++] = s->name;
- q->elements[j++] = s->arch;
- }
- }
- else if ((flags & GET_USERINSTALLED_NAMES) != 0)
- {
- for (i = 0; i < q->count; i++)
- {
- s = pool->solvables + q->elements[i];
- q->elements[i] = s->name;
- }
- }
- /* sort and unify */
- get_userinstalled_sort_uniq(pool, q, flags);
-
- /* invert if asked for */
- if ((flags & GET_USERINSTALLED_INVERTED) != 0)
- {
- /* first generate queue with all installed packages */
- Queue invq;
- queue_init(&invq);
- for (i = 1; i < solv->decisionq.count; i++)
- {
- p = solv->decisionq.elements[i];
- if (p <= 0)
- continue;
- s = pool->solvables + p;
- if (!s->repo)
- continue;
- if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
- queue_push2(&invq, s->name, s->arch);
- else if ((flags & GET_USERINSTALLED_NAMES) != 0)
- queue_push(&invq, s->name);
- else
- queue_push(&invq, p);
- }
- /* push q on invq, just in case... */
- queue_insertn(&invq, invq.count, q->count, q->elements);
- get_userinstalled_sort_uniq(pool, &invq, flags);
- /* subtract queues (easy as they are sorted and invq is a superset of q) */
- if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
- {
- if (q->count)
- {
- for (i = j = 0; i < invq.count; i += 2)
- if (invq.elements[i] == q->elements[j] && invq.elements[i + 1] == q->elements[j + 1])
- {
- invq.elements[i] = invq.elements[i + 1] = 0;
- j += 2;
- if (j >= q->count)
- break;
- }
- queue_empty(q);
- }
- for (i = 0; i < invq.count; i += 2)
- if (invq.elements[i])
- queue_push2(q, invq.elements[i], invq.elements[i + 1]);
- }
- else
- {
- if (q->count)
- {
- for (i = j = 0; i < invq.count; i++)
- if (invq.elements[i] == q->elements[j])
- {
- invq.elements[i] = 0;
- if (++j >= q->count)
- break;
- }
- queue_empty(q);
- }
- for (i = 0; i < invq.count; i++)
- if (invq.elements[i])
- queue_push(q, invq.elements[i]);
- }
- queue_free(&invq);
- }
-}
-
-void
-pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags)
-{
- int i;
-
- if ((flags & GET_USERINSTALLED_INVERTED) != 0)
- {
- Queue invq;
- Id p, lastid;
- Solvable *s;
- int bad;
- if (!pool->installed)
- return;
- queue_init(&invq);
- if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
- flags &= ~GET_USERINSTALLED_NAMES; /* just in case */
- FOR_REPO_SOLVABLES(pool->installed, p, s)
- queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p);
- if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
- {
- /* for namearch we convert to packages */
- namearch2solvables(pool, q, &invq, 0);
- get_userinstalled_sort_uniq(pool, &invq, flags);
- namearch2solvables(pool, q, &invq, 0);
- flags = 0;
- }
- else
- {
- queue_insertn(&invq, invq.count, q->count, q->elements);
- get_userinstalled_sort_uniq(pool, &invq, flags);
- /* now the fun part, add q again, sort, and remove all dups */
- queue_insertn(&invq, invq.count, q->count, q->elements);
- }
- if (invq.count > 1)
- {
- if ((flags & GET_USERINSTALLED_NAMES) != 0)
- solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool);
- else
- solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0);
- }
- lastid = -1;
- bad = 1;
- for (i = 0; i < invq.count; i++)
- {
- if (invq.elements[i] == lastid)
- {
- bad = 1;
- continue;
- }
- if (!bad)
- queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
- bad = 0;
- lastid = invq.elements[i];
- }
- if (!bad)
- queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
- queue_free(&invq);
- }
- else
- {
- if (flags & GET_USERINSTALLED_NAMEARCH)
- namearch2solvables(pool, q, job, SOLVER_USERINSTALLED | SOLVER_SOLVABLE);
- else
- {
- for (i = 0; i < q->count; i++)
- queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]);
- }
- }
-}
-
-int
-solver_alternatives_count(Solver *solv)
-{
- Id *elements = solv->branches.elements;
- int res, count;
- for (res = 0, count = solv->branches.count; count; res++)
- count -= elements[count - 2];
- return res;
-}
-
-int
-solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp)
-{
- int cnt = solver_alternatives_count(solv);
- int count = solv->branches.count;
- Id *elements = solv->branches.elements;
- if (choices)
- queue_empty(choices);
- if (alternative <= 0 || alternative > cnt)
- return 0;
- elements += count;
- for (; cnt > alternative; cnt--)
- elements -= elements[-2];
- if (levelp)
- *levelp = elements[-1];
- if (fromp)
- *fromp = elements[-4];
- if (idp)
- *idp = elements[-3];
- if (chosenp)
- {
- int i;
- *chosenp = 0;
- for (i = elements[-2]; i > 4; i--)
- {
- Id p = -elements[-i];
- if (p > 0 && solv->decisionmap[p] == elements[-1] + 1)
- {
- *chosenp = p;
- break;
- }
- }
- }
- if (choices)
- queue_insertn(choices, 0, elements[-2] - 4, elements - elements[-2]);
- return elements[-4] ? SOLVER_ALTERNATIVE_TYPE_RECOMMENDS : SOLVER_ALTERNATIVE_TYPE_RULE;
-}
-
-const char *
-solver_select2str(Pool *pool, Id select, Id what)
-{
- const char *s;
- char *b;
- select &= SOLVER_SELECTMASK;
- if (select == SOLVER_SOLVABLE)
- return pool_solvid2str(pool, what);
- if (select == SOLVER_SOLVABLE_NAME)
- return pool_dep2str(pool, what);
- if (select == SOLVER_SOLVABLE_PROVIDES)
- {
- s = pool_dep2str(pool, what);
- b = pool_alloctmpspace(pool, 11 + strlen(s));
- sprintf(b, "providing %s", s);
- return b;
- }
- if (select == SOLVER_SOLVABLE_ONE_OF)
- {
- Id p;
- b = 0;
- while ((p = pool->whatprovidesdata[what++]) != 0)
- {
- s = pool_solvid2str(pool, p);
- if (b)
- b = pool_tmpappend(pool, b, ", ", s);
- else
- b = pool_tmpjoin(pool, s, 0, 0);
- pool_freetmpspace(pool, s);
- }
- return b ? b : "nothing";
- }
- if (select == SOLVER_SOLVABLE_REPO)
- {
- b = pool_alloctmpspace(pool, 20);
- sprintf(b, "repo #%d", what);
- return b;
- }
- if (select == SOLVER_SOLVABLE_ALL)
- return "all packages";
- return "unknown job select";
-}
-
-const char *
-pool_job2str(Pool *pool, Id how, Id what, Id flagmask)
-{
- Id select = how & SOLVER_SELECTMASK;
- const char *strstart = 0, *strend = 0;
- char *s;
- int o;
-
- switch (how & SOLVER_JOBMASK)
- {
- case SOLVER_NOOP:
- return "do nothing";
- case SOLVER_INSTALL:
- if (select == SOLVER_SOLVABLE && pool->installed && pool->solvables[what].repo == pool->installed)
- strstart = "keep ", strend = " installed";
- else if (select == SOLVER_SOLVABLE || select == SOLVER_SOLVABLE_NAME)
- strstart = "install ";
- else if (select == SOLVER_SOLVABLE_PROVIDES)
- strstart = "install a package ";
- else
- strstart = "install one of ";
- break;
- case SOLVER_ERASE:
- if (select == SOLVER_SOLVABLE && !(pool->installed && pool->solvables[what].repo == pool->installed))
- strstart = "keep ", strend = " uninstalled";
- else if (select == SOLVER_SOLVABLE_PROVIDES)
- strstart = "deinstall all packages ";
- else
- strstart = "deinstall ";
- break;
- case SOLVER_UPDATE:
- strstart = "update ";
- break;
- case SOLVER_WEAKENDEPS:
- strstart = "weaken deps of ";
- break;
- case SOLVER_MULTIVERSION:
- strstart = "multi version ";
- break;
- case SOLVER_LOCK:
- strstart = "lock ";
- break;
- case SOLVER_DISTUPGRADE:
- strstart = "dist upgrade ";
- break;
- case SOLVER_VERIFY:
- strstart = "verify ";
- break;
- case SOLVER_DROP_ORPHANED:
- strstart = "deinstall ", strend = " if orphaned";
- break;
- case SOLVER_USERINSTALLED:
- strstart = "regard ", strend = " as userinstalled";
- break;
- case SOLVER_ALLOWUNINSTALL:
- strstart = "allow deinstallation of ";
- break;
- default:
- strstart = "unknown job ";
- break;
- }
- s = pool_tmpjoin(pool, strstart, solver_select2str(pool, select, what), strend);
- how &= flagmask;
- if ((how & ~(SOLVER_SELECTMASK|SOLVER_JOBMASK)) == 0)
- return s;
- o = strlen(s);
- s = pool_tmpappend(pool, s, " ", 0);
- if (how & SOLVER_WEAK)
- s = pool_tmpappend(pool, s, ",weak", 0);
- if (how & SOLVER_ESSENTIAL)
- s = pool_tmpappend(pool, s, ",essential", 0);
- if (how & SOLVER_CLEANDEPS)
- s = pool_tmpappend(pool, s, ",cleandeps", 0);
- if (how & SOLVER_ORUPDATE)
- s = pool_tmpappend(pool, s, ",orupdate", 0);
- if (how & SOLVER_FORCEBEST)
- s = pool_tmpappend(pool, s, ",forcebest", 0);
- if (how & SOLVER_TARGETED)
- s = pool_tmpappend(pool, s, ",targeted", 0);
- if (how & SOLVER_SETEV)
- s = pool_tmpappend(pool, s, ",setev", 0);
- if (how & SOLVER_SETEVR)
- s = pool_tmpappend(pool, s, ",setevr", 0);
- if (how & SOLVER_SETARCH)
- s = pool_tmpappend(pool, s, ",setarch", 0);
- if (how & SOLVER_SETVENDOR)
- s = pool_tmpappend(pool, s, ",setvendor", 0);
- if (how & SOLVER_SETREPO)
- s = pool_tmpappend(pool, s, ",setrepo", 0);
- if (how & SOLVER_SETNAME)
- s = pool_tmpappend(pool, s, ",setname", 0);
- if (how & SOLVER_NOAUTOSET)
- s = pool_tmpappend(pool, s, ",noautoset", 0);
- if (s[o + 1] != ',')
- s = pool_tmpappend(pool, s, ",?", 0);
- s[o + 1] = '[';
- return pool_tmpappend(pool, s, "]", 0);
-}
-
-const char *
-solver_alternative2str(Solver *solv, int type, Id id, Id from)
-{
- Pool *pool = solv->pool;
- if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
- {
- const char *s = pool_dep2str(pool, id);
- return pool_tmpappend(pool, s, ", recommended by ", pool_solvid2str(pool, from));
- }
- if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
- {
- int rtype;
- Id depfrom, depto, dep;
- char buf[64];
- if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
- id = solver_rule2pkgrule(solv, id);
- rtype = solver_ruleinfo(solv, id, &depfrom, &depto, &dep);
- if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
- {
- if ((depto & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES)
- return pool_dep2str(pool, dep);
- return solver_select2str(pool, depto & SOLVER_SELECTMASK, dep);
- }
- if (rtype == SOLVER_RULE_PKG_REQUIRES)
- {
- const char *s = pool_dep2str(pool, dep);
- return pool_tmpappend(pool, s, ", required by ", pool_solvid2str(pool, depfrom));
- }
- sprintf(buf, "Rule #%d", id);
- return pool_tmpjoin(pool, buf, 0, 0);
- }
- return "unknown alternative type";
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * solver.h
- *
- */
-
-#ifndef LIBSOLV_SOLVER_H
-#define LIBSOLV_SOLVER_H
-
-#include "pooltypes.h"
-#include "pool.h"
-#include "repo.h"
-#include "queue.h"
-#include "bitmap.h"
-#include "transaction.h"
-#include "rules.h"
-#include "problems.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct _Solver {
- Pool *pool; /* back pointer to pool */
- Queue job; /* copy of the job we're solving */
-
- int (*solution_callback)(struct _Solver *solv, void *data);
- void *solution_callback_data;
-
- int pooljobcnt; /* number of pooljob entries in job queue */
-
-#ifdef LIBSOLV_INTERNAL
- Repo *installed; /* copy of pool->installed */
-
- /* list of rules, ordered
- * pkg rules first, then features, updates, jobs, learnt
- * see start/end offsets below
- */
- Rule *rules; /* all rules */
- Id nrules; /* [Offset] index of the last rule */
-
- Queue ruleassertions; /* Queue of all assertion rules */
-
- /* start/end offset for rule 'areas' */
-
- Id pkgrules_end; /* [Offset] dep rules end */
-
- Id featurerules; /* feature rules start/end */
- Id featurerules_end;
-
- Id updaterules; /* policy rules, e.g. keep packages installed or update. All literals > 0 */
- Id updaterules_end;
-
- Id jobrules; /* user rules */
- Id jobrules_end;
-
- Id infarchrules; /* inferior arch rules */
- Id infarchrules_end;
-
- Id duprules; /* dist upgrade rules */
- Id duprules_end;
-
- Id bestrules; /* rules from SOLVER_FORCEBEST */
- Id bestrules_end;
- Id *bestrules_pkg;
-
- Id yumobsrules; /* rules from yum obsoletes handling */
- Id yumobsrules_end;
- Id *yumobsrules_info; /* the dependency for each rule */
-
- Id choicerules; /* choice rules (always weak) */
- Id choicerules_end;
- Id *choicerules_ref;
-
- Id learntrules; /* learnt rules, (end == nrules) */
-
- Map noupdate; /* don't try to update these
- installed solvables */
- Map multiversion; /* ignore obsoletes for these (multiinstall) */
-
- Map updatemap; /* bring these installed packages to the newest version */
- int updatemap_all; /* bring all packages to the newest version */
-
- Map bestupdatemap; /* create best rule for those packages */
- int bestupdatemap_all; /* bring all packages to the newest version */
-
- Map fixmap; /* fix these packages */
- int fixmap_all; /* fix all packages */
-
- Queue weakruleq; /* index into 'rules' for weak ones */
- Map weakrulemap; /* map rule# to '1' for weak rules, 1..learntrules */
-
- 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
- */
-
- Queue ruletojob; /* index into job queue: jobs for which a rule exits */
-
- /* our decisions: */
- Queue decisionq; /* >0:install, <0:remove/conflict */
- Queue decisionq_why; /* index of rule, Offset into rules */
-
- Id *decisionmap; /* map for all available solvables,
- * = 0: undecided
- * > 0: level of decision when installed,
- * < 0: level of decision when conflict */
-
- int decisioncnt_jobs;
- int decisioncnt_update;
- int decisioncnt_keep;
- int decisioncnt_resolve;
- int decisioncnt_weak;
- int decisioncnt_orphan;
-
- /* learnt rule history */
- Queue learnt_why;
- Queue learnt_pool;
-
- Queue branches;
- int propagate_index; /* index into decisionq for non-propagated decisions */
-
- Queue problems; /* list of lists of conflicting rules, < 0 for job rules */
- Queue solutions; /* refined problem storage space */
-
- Queue orphaned; /* orphaned packages (to be removed?) */
-
- int stats_learned; /* statistic */
- int stats_unsolvable; /* statistic */
-
- Map recommendsmap; /* recommended packages from decisionmap */
- Map suggestsmap; /* suggested packages from decisionmap */
- int recommends_index; /* recommendsmap/suggestsmap is created up to this level */
- Queue *recommendscplxq;
- Queue *suggestscplxq;
-
- Id *obsoletes; /* obsoletes for each installed solvable */
- Id *obsoletes_data; /* data area for obsoletes */
- Id *specialupdaters; /* updaters for packages with a limited update rule */
-
- /*-------------------------------------------------------------------------------------------------------------
- * Solver configuration
- *-------------------------------------------------------------------------------------------------------------*/
-
- int allowdowngrade; /* allow to downgrade installed solvable */
- int allownamechange; /* allow to change name of installed solvables */
- int allowarchchange; /* allow to change architecture of installed solvables */
- int allowvendorchange; /* allow to change vendor of installed solvables */
- int allowuninstall; /* allow removal of installed solvables */
- int noupdateprovide; /* true: update packages needs not to provide old package */
- int needupdateprovide; /* true: update packages must provide old package */
- int dosplitprovides; /* true: consider legacy split provides */
- int dontinstallrecommended; /* true: do not install recommended packages */
- int addalreadyrecommended; /* true: also install recommended packages that were already recommended by the installed packages */
- int dontshowinstalledrecommended; /* true: do not show recommended packages that are already installed */
-
- int noinfarchcheck; /* true: do not forbid inferior architectures */
- int keepexplicitobsoletes; /* true: honor obsoletes during multiinstall */
- int bestobeypolicy; /* true: stay in policy with the best rules */
- int noautotarget; /* true: do not assume targeted for up/dup jobs that contain no installed solvable */
- int focus_installed; /* true: resolve update rules first */
- int do_yum_obsoletes; /* true: add special yumobs rules */
-
- Map dupmap; /* dup these packages*/
- int dupmap_all; /* dup all packages */
- Map dupinvolvedmap; /* packages involved in dup process */
- int dup_allowdowngrade; /* dup mode: allow to downgrade installed solvable */
- int dup_allownamechange; /* dup mode: allow to change name of installed solvable */
- int dup_allowarchchange; /* dup mode: allow to change architecture of installed solvables */
- int dup_allowvendorchange; /* dup mode: allow to change vendor of installed solvables */
-
- Map droporphanedmap; /* packages to drop in dup mode */
- int droporphanedmap_all;
-
- Map cleandepsmap; /* try to drop these packages as of cleandeps erases */
-
- Queue *ruleinfoq; /* tmp space for solver_ruleinfo() */
-
- Queue *cleandeps_updatepkgs; /* packages we update in cleandeps mode */
- Queue *cleandeps_mistakes; /* mistakes we made */
-
- Queue *update_targets; /* update to specific packages */
-
- Queue *installsuppdepq; /* deps from the install namespace provides hack */
-
- Queue addedmap_deduceq; /* deduce addedmap from pkg rules */
- Id *instbuddy; /* buddies of installed packages */
- int keep_orphans; /* how to treat orphans */
- int break_orphans; /* how to treat orphans */
- Queue *brokenorphanrules; /* broken rules of orphaned packages */
-
- Map allowuninstallmap; /* ok to uninstall those */
- int allowuninstall_all;
-
-#endif /* LIBSOLV_INTERNAL */
-};
-
-typedef struct _Solver Solver;
-
-/*
- * queue commands
- */
-
-#define SOLVER_SOLVABLE 0x01
-#define SOLVER_SOLVABLE_NAME 0x02
-#define SOLVER_SOLVABLE_PROVIDES 0x03
-#define SOLVER_SOLVABLE_ONE_OF 0x04
-#define SOLVER_SOLVABLE_REPO 0x05
-#define SOLVER_SOLVABLE_ALL 0x06
-
-#define SOLVER_SELECTMASK 0xff
-
-#define SOLVER_NOOP 0x0000
-#define SOLVER_INSTALL 0x0100
-#define SOLVER_ERASE 0x0200
-#define SOLVER_UPDATE 0x0300
-#define SOLVER_WEAKENDEPS 0x0400
-#define SOLVER_MULTIVERSION 0x0500
-#define SOLVER_LOCK 0x0600
-#define SOLVER_DISTUPGRADE 0x0700
-#define SOLVER_VERIFY 0x0800
-#define SOLVER_DROP_ORPHANED 0x0900
-#define SOLVER_USERINSTALLED 0x0a00
-#define SOLVER_ALLOWUNINSTALL 0x0b00
-
-#define SOLVER_JOBMASK 0xff00
-
-#define SOLVER_WEAK 0x010000
-#define SOLVER_ESSENTIAL 0x020000
-#define SOLVER_CLEANDEPS 0x040000
-/* ORUPDATE makes SOLVER_INSTALL not prune to installed
- * packages, thus updating installed packages */
-#define SOLVER_ORUPDATE 0x080000
-/* FORCEBEST makes the solver insist on best packages, so
- * you will get problem reported if the best package is
- * not installable. This can be used with INSTALL, UPDATE
- * and DISTUPGRADE */
-#define SOLVER_FORCEBEST 0x100000
-/* TARGETED is used in up/dup jobs to make the solver
- * treat the specified selection as target packages.
- * It is automatically assumed if the selection does not
- * contain an "installed" package unless the
- * NO_AUTOTARGET solver flag is set */
-#define SOLVER_TARGETED 0x200000
-/* This (SOLVER_INSTALL) job was automatically added
- * and thus does not the add to the userinstalled packages */
-#define SOLVER_NOTBYUSER 0x400000
-
-#define SOLVER_SETEV 0x01000000
-#define SOLVER_SETEVR 0x02000000
-#define SOLVER_SETARCH 0x04000000
-#define SOLVER_SETVENDOR 0x08000000
-#define SOLVER_SETREPO 0x10000000
-#define SOLVER_NOAUTOSET 0x20000000
-#define SOLVER_SETNAME 0x40000000
-
-#define SOLVER_SETMASK 0x7f000000
-
-/* legacy */
-#define SOLVER_NOOBSOLETES SOLVER_MULTIVERSION
-
-#define SOLVER_REASON_UNRELATED 0
-#define SOLVER_REASON_UNIT_RULE 1
-#define SOLVER_REASON_KEEP_INSTALLED 2
-#define SOLVER_REASON_RESOLVE_JOB 3
-#define SOLVER_REASON_UPDATE_INSTALLED 4
-#define SOLVER_REASON_CLEANDEPS_ERASE 5
-#define SOLVER_REASON_RESOLVE 6
-#define SOLVER_REASON_WEAKDEP 7
-#define SOLVER_REASON_RESOLVE_ORPHAN 8
-
-#define SOLVER_REASON_RECOMMENDED 16
-#define SOLVER_REASON_SUPPLEMENTED 17
-
-
-#define SOLVER_FLAG_ALLOW_DOWNGRADE 1
-#define SOLVER_FLAG_ALLOW_ARCHCHANGE 2
-#define SOLVER_FLAG_ALLOW_VENDORCHANGE 3
-#define SOLVER_FLAG_ALLOW_UNINSTALL 4
-#define SOLVER_FLAG_NO_UPDATEPROVIDE 5
-#define SOLVER_FLAG_SPLITPROVIDES 6
-#define SOLVER_FLAG_IGNORE_RECOMMENDED 7
-#define SOLVER_FLAG_ADD_ALREADY_RECOMMENDED 8
-#define SOLVER_FLAG_NO_INFARCHCHECK 9
-#define SOLVER_FLAG_ALLOW_NAMECHANGE 10
-#define SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES 11
-#define SOLVER_FLAG_BEST_OBEY_POLICY 12
-#define SOLVER_FLAG_NO_AUTOTARGET 13
-#define SOLVER_FLAG_DUP_ALLOW_DOWNGRADE 14
-#define SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE 15
-#define SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE 16
-#define SOLVER_FLAG_DUP_ALLOW_NAMECHANGE 17
-#define SOLVER_FLAG_KEEP_ORPHANS 18
-#define SOLVER_FLAG_BREAK_ORPHANS 19
-#define SOLVER_FLAG_FOCUS_INSTALLED 20
-#define SOLVER_FLAG_YUM_OBSOLETES 21
-#define SOLVER_FLAG_NEED_UPDATEPROVIDE 22
-
-#define GET_USERINSTALLED_NAMES (1 << 0) /* package names instead of ids */
-#define GET_USERINSTALLED_INVERTED (1 << 1) /* autoinstalled */
-#define GET_USERINSTALLED_NAMEARCH (1 << 2) /* package/arch tuples instead of ids */
-
-#define SOLVER_ALTERNATIVE_TYPE_RULE 1
-#define SOLVER_ALTERNATIVE_TYPE_RECOMMENDS 2
-#define SOLVER_ALTERNATIVE_TYPE_SUGGESTS 3
-
-extern Solver *solver_create(Pool *pool);
-extern void solver_free(Solver *solv);
-extern int solver_solve(Solver *solv, Queue *job);
-extern Transaction *solver_create_transaction(Solver *solv);
-extern int solver_set_flag(Solver *solv, int flag, int value);
-extern int solver_get_flag(Solver *solv, int flag);
-
-extern int solver_get_decisionlevel(Solver *solv, Id p);
-extern void solver_get_decisionqueue(Solver *solv, Queue *decisionq);
-extern int solver_get_lastdecisionblocklevel(Solver *solv);
-extern void solver_get_decisionblock(Solver *solv, int level, Queue *decisionq);
-
-extern void solver_get_orphaned(Solver *solv, Queue *orphanedq);
-extern void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *suggestionsq, int noselected);
-extern void solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered);
-extern void solver_get_userinstalled(Solver *solv, Queue *q, int flags);
-extern void pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags);
-
-extern int solver_describe_decision(Solver *solv, Id p, Id *infop);
-extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq);
-
-extern int solver_alternatives_count(Solver *solv);
-extern int solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp);
-
-extern void solver_calculate_multiversionmap(Pool *pool, Queue *job, Map *multiversionmap);
-extern void solver_calculate_noobsmap(Pool *pool, Queue *job, Map *multiversionmap); /* obsolete */
-extern void solver_create_state_maps(Solver *solv, Map *installedmap, Map *conflictsmap);
-
-extern void solver_calc_duchanges(Solver *solv, DUChanges *mps, int nmps);
-extern int solver_calc_installsizechange(Solver *solv);
-extern void solver_trivial_installable(Solver *solv, Queue *pkgs, Queue *res);
-
-extern void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what);
-extern int pool_isemptyupdatejob(Pool *pool, Id how, Id what);
-
-extern const char *solver_select2str(Pool *pool, Id select, Id what);
-extern const char *pool_job2str(Pool *pool, Id how, Id what, Id flagmask);
-extern const char *solver_alternative2str(Solver *solv, int type, Id id, Id from);
-
-
-/* iterate over all literals of a rule */
-#define FOR_RULELITERALS(l, pp, r) \
- for (pp = r->d < 0 ? -r->d - 1 : r->d, \
- l = r->p; l; l = (pp <= 0 ? (pp-- ? 0 : r->w2) : \
- pool->whatprovidesdata[pp++]))
-
-
-
-
-/* XXX: this currently doesn't work correctly for SOLVER_SOLVABLE_REPO and
- SOLVER_SOLVABLE_ALL */
-
-/* iterate over all packages selected by a job */
-#define FOR_JOB_SELECT(p, pp, select, what) \
- if (select == SOLVER_SOLVABLE_REPO || select == SOLVER_SOLVABLE_ALL) \
- p = pp = 0; \
- else for (pp = (select == SOLVER_SOLVABLE ? 0 : \
- select == SOLVER_SOLVABLE_ONE_OF ? what : \
- pool_whatprovides(pool, what)), \
- p = (select == SOLVER_SOLVABLE ? what : pool->whatprovidesdata[pp++]); \
- p ; p = pool->whatprovidesdata[pp++]) \
- if (select == SOLVER_SOLVABLE_NAME && \
- pool_match_nevr(pool, pool->solvables + p, what) == 0) \
- continue; \
- else
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_SOLVER_H */
+++ /dev/null
-/*
- * Copyright (c) 2011, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * solver_private.h - private functions
- *
- */
-
-#ifndef LIBSOLV_SOLVER_PRIVATE_H
-#define LIBSOLV_SOLVER_PRIVATE_H
-
-extern void solver_run_sat(Solver *solv, int disablerules, int doweak);
-extern void solver_reset(Solver *solv);
-
-extern int solver_splitprovides(Solver *solv, Id dep, Map *m);
-
-static inline int
-solver_dep_fulfilled(Solver *solv, Id dep)
-{
- Pool *pool = solv->pool;
- Id p, pp;
-
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (rd->flags == REL_COND)
- {
- if (ISRELDEP(rd->evr))
- {
- Reldep *rd2 = GETRELDEP(pool, rd->evr);
- if (rd2->flags == REL_ELSE)
- {
- if (solver_dep_fulfilled(solv, rd2->name))
- return solver_dep_fulfilled(solv, rd->name);
- return solver_dep_fulfilled(solv, rd2->evr);
- }
- }
- if (solver_dep_fulfilled(solv, rd->name))
- return 1;
- return !solver_dep_fulfilled(solv, rd->evr);
- }
- if (rd->flags == REL_AND)
- {
- if (!solver_dep_fulfilled(solv, rd->name))
- return 0;
- return solver_dep_fulfilled(solv, rd->evr);
- }
- if (rd->flags == REL_OR)
- {
- if (solver_dep_fulfilled(solv, rd->name))
- return 1;
- return solver_dep_fulfilled(solv, rd->evr);
- }
- if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
- return solver_splitprovides(solv, rd->evr, 0);
- }
- FOR_PROVIDES(p, pp, dep)
- {
- if (solv->decisionmap[p] > 0)
- return 1;
- }
- return 0;
-}
-
-static inline int
-solver_is_supplementing(Solver *solv, Solvable *s)
-{
- Id sup, *supp;
- if (!s->supplements)
- return 0;
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (solver_dep_fulfilled(solv, sup))
- return 1;
- return 0;
-}
-
-static inline int
-solver_is_enhancing(Solver *solv, Solvable *s)
-{
- Id enh, *enhp;
- if (!s->enhances)
- return 0;
- enhp = s->repo->idarraydata + s->enhances;
- while ((enh = *enhp++) != 0)
- if (solver_dep_fulfilled(solv, enh))
- return 1;
- return 0;
-}
-
-#endif /* LIBSOLV_SOLVER_PRIVATE_H */
+++ /dev/null
-/*
- * Copyright (c) 2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * solverdebug.c
- *
- * debug functions for the SAT solver
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-
-#include "solver.h"
-#include "solver_private.h"
-#include "solverdebug.h"
-#include "bitmap.h"
-#include "pool.h"
-#include "poolarch.h"
-#include "util.h"
-#include "evr.h"
-#include "policy.h"
-
-
-void
-solver_printruleelement(Solver *solv, int type, Rule *r, Id v)
-{
- Pool *pool = solv->pool;
- Solvable *s;
- if (v < 0)
- {
- s = pool->solvables + -v;
- POOL_DEBUG(type, " !%s [%d]", pool_solvable2str(pool, s), -v);
- }
- else
- {
- s = pool->solvables + v;
- POOL_DEBUG(type, " %s [%d]", pool_solvable2str(pool, s), v);
- }
- if (pool->installed && s->repo == pool->installed)
- POOL_DEBUG(type, "I");
- if (r)
- {
- if (r->w1 == v)
- POOL_DEBUG(type, " (w1)");
- if (r->w2 == v)
- POOL_DEBUG(type, " (w2)");
- }
- if (solv->decisionmap[s - pool->solvables] > 0)
- POOL_DEBUG(type, " Install.level%d", solv->decisionmap[s - pool->solvables]);
- if (solv->decisionmap[s - pool->solvables] < 0)
- POOL_DEBUG(type, " Conflict.level%d", -solv->decisionmap[s - pool->solvables]);
- POOL_DEBUG(type, "\n");
-}
-
-
-/*
- * print rule
- */
-
-void
-solver_printrule(Solver *solv, int type, Rule *r)
-{
- Pool *pool = solv->pool;
- int i;
- Id d, v;
-
- if (r >= solv->rules && r < solv->rules + solv->nrules) /* r is a solver rule */
- POOL_DEBUG(type, "Rule #%d:", (int)(r - solv->rules));
- else
- POOL_DEBUG(type, "Rule:"); /* r is any rule */
- if (r->d < 0)
- POOL_DEBUG(type, " (disabled)");
- POOL_DEBUG(type, "\n");
- d = r->d < 0 ? -r->d - 1 : r->d;
- for (i = 0; ; i++)
- {
- if (i == 0)
- /* print direct literal */
- v = r->p;
- else if (!d)
- {
- if (i == 2)
- break;
- /* binary rule --> print w2 as second literal */
- v = r->w2;
- }
- else
- /* every other which is in d */
- v = solv->pool->whatprovidesdata[d + i - 1];
- if (v == ID_NULL)
- break;
- solver_printruleelement(solv, type, r, v);
- }
- POOL_DEBUG(type, " next rules: %d %d\n", r->n1, r->n2);
-}
-
-void
-solver_printruleclass(Solver *solv, int type, Rule *r)
-{
- Pool *pool = solv->pool;
- Id p = r - solv->rules;
- assert(p >= 0);
- if (p < solv->learntrules)
- if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, p))
- POOL_DEBUG(type, "WEAK ");
- if (solv->learntrules && p >= solv->learntrules)
- POOL_DEBUG(type, "LEARNT ");
- else if (p >= solv->bestrules && p < solv->bestrules_end)
- POOL_DEBUG(type, "BEST ");
- else if (p >= solv->choicerules && p < solv->choicerules_end)
- POOL_DEBUG(type, "CHOICE ");
- else if (p >= solv->infarchrules && p < solv->infarchrules_end)
- POOL_DEBUG(type, "INFARCH ");
- else if (p >= solv->duprules && p < solv->duprules_end)
- POOL_DEBUG(type, "DUP ");
- else if (p >= solv->jobrules && p < solv->jobrules_end)
- POOL_DEBUG(type, "JOB ");
- else if (p >= solv->updaterules && p < solv->updaterules_end)
- POOL_DEBUG(type, "UPDATE ");
- else if (p >= solv->featurerules && p < solv->featurerules_end)
- POOL_DEBUG(type, "FEATURE ");
- else if (p >= solv->yumobsrules && p < solv->yumobsrules_end)
- POOL_DEBUG(type, "YUMOBS ");
- solver_printrule(solv, type, r);
-}
-
-void
-solver_printproblem(Solver *solv, Id v)
-{
- Pool *pool = solv->pool;
- int i;
- Rule *r;
- Id *jp;
-
- if (v > 0)
- solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, solv->rules + v);
- else
- {
- v = -(v + 1);
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "JOB %d\n", v);
- jp = solv->ruletojob.elements;
- for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
- if (*jp == v)
- {
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "- ");
- solver_printrule(solv, SOLV_DEBUG_SOLUTIONS, r);
- }
- POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "ENDJOB\n");
- }
-}
-
-void
-solver_printwatches(Solver *solv, int type)
-{
- Pool *pool = solv->pool;
- int counter;
-
- POOL_DEBUG(type, "Watches: \n");
- for (counter = -(pool->nsolvables - 1); counter < pool->nsolvables; counter++)
- POOL_DEBUG(type, " solvable [%d] -- rule [%d]\n", counter, solv->watches[counter + pool->nsolvables]);
-}
-
-void
-solver_printdecisionq(Solver *solv, int type)
-{
- Pool *pool = solv->pool;
- int i;
- Id p, why;
-
- POOL_DEBUG(type, "Decisions:\n");
- for (i = 0; i < solv->decisionq.count; i++)
- {
- p = solv->decisionq.elements[i];
- if (p > 0)
- POOL_DEBUG(type, "%d %d install %s, ", i, solv->decisionmap[p], pool_solvid2str(pool, p));
- else
- POOL_DEBUG(type, "%d %d conflict %s, ", i, -solv->decisionmap[-p], pool_solvid2str(pool, -p));
- why = solv->decisionq_why.elements[i];
- if (why > 0)
- {
- POOL_DEBUG(type, "forced by ");
- solver_printruleclass(solv, type, solv->rules + why);
- }
- else if (why < 0)
- {
- POOL_DEBUG(type, "chosen from ");
- solver_printruleclass(solv, type, solv->rules - why);
- }
- else
- POOL_DEBUG(type, "picked for some unknown reason.\n");
- }
-}
-
-/*
- * printdecisions
- */
-
-void
-solver_printdecisions(Solver *solv)
-{
- Pool *pool = solv->pool;
- Repo *installed = solv->installed;
- Transaction *trans = solver_create_transaction(solv);
- Id p, type;
- int i, j;
- Solvable *s;
- Queue iq;
- Queue recommendations;
- Queue suggestions;
- Queue orphaned;
-
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- POOL_DEBUG(SOLV_DEBUG_RESULT, "transaction:\n");
-
- queue_init(&iq);
- for (i = 0; i < trans->steps.count; i++)
- {
- p = trans->steps.elements[i];
- s = pool->solvables + p;
- type = transaction_type(trans, p, SOLVER_TRANSACTION_SHOW_ACTIVE|SOLVER_TRANSACTION_SHOW_ALL|SOLVER_TRANSACTION_SHOW_OBSOLETES|SOLVER_TRANSACTION_SHOW_MULTIINSTALL);
- switch(type)
- {
- case SOLVER_TRANSACTION_MULTIINSTALL:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " multi install %s", pool_solvable2str(pool, s));
- break;
- case SOLVER_TRANSACTION_MULTIREINSTALL:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " multi reinstall %s", pool_solvable2str(pool, s));
- break;
- case SOLVER_TRANSACTION_INSTALL:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " install %s", pool_solvable2str(pool, s));
- break;
- case SOLVER_TRANSACTION_REINSTALL:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " reinstall %s", pool_solvable2str(pool, s));
- break;
- case SOLVER_TRANSACTION_DOWNGRADE:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " downgrade %s", pool_solvable2str(pool, s));
- break;
- case SOLVER_TRANSACTION_CHANGE:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " change %s", pool_solvable2str(pool, s));
- break;
- case SOLVER_TRANSACTION_UPGRADE:
- case SOLVER_TRANSACTION_OBSOLETES:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " upgrade %s", pool_solvable2str(pool, s));
- break;
- case SOLVER_TRANSACTION_ERASE:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " erase %s", pool_solvable2str(pool, s));
- break;
- default:
- break;
- }
- switch(type)
- {
- case SOLVER_TRANSACTION_INSTALL:
- case SOLVER_TRANSACTION_ERASE:
- case SOLVER_TRANSACTION_MULTIINSTALL:
- case SOLVER_TRANSACTION_MULTIREINSTALL:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- break;
- case SOLVER_TRANSACTION_REINSTALL:
- case SOLVER_TRANSACTION_DOWNGRADE:
- case SOLVER_TRANSACTION_CHANGE:
- case SOLVER_TRANSACTION_UPGRADE:
- case SOLVER_TRANSACTION_OBSOLETES:
- transaction_all_obs_pkgs(trans, p, &iq);
- if (iq.count)
- {
- POOL_DEBUG(SOLV_DEBUG_RESULT, " (obsoletes");
- for (j = 0; j < iq.count; j++)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s", pool_solvid2str(pool, iq.elements[j]));
- POOL_DEBUG(SOLV_DEBUG_RESULT, ")");
- }
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- break;
- default:
- break;
- }
- }
- queue_free(&iq);
-
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
-
- queue_init(&recommendations);
- queue_init(&suggestions);
- queue_init(&orphaned);
- solver_get_recommendations(solv, &recommendations, &suggestions, 0);
- solver_get_orphaned(solv, &orphaned);
- if (recommendations.count)
- {
- POOL_DEBUG(SOLV_DEBUG_RESULT, "recommended packages:\n");
- for (i = 0; i < recommendations.count; i++)
- {
- s = pool->solvables + recommendations.elements[i];
- if (solv->decisionmap[recommendations.elements[i]] > 0)
- {
- if (installed && s->repo == installed)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (installed)\n", pool_solvable2str(pool, s));
- else
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (selected)\n", pool_solvable2str(pool, s));
- }
- else
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", pool_solvable2str(pool, s));
- }
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- }
-
- if (suggestions.count)
- {
- POOL_DEBUG(SOLV_DEBUG_RESULT, "suggested packages:\n");
- for (i = 0; i < suggestions.count; i++)
- {
- s = pool->solvables + suggestions.elements[i];
- if (solv->decisionmap[suggestions.elements[i]] > 0)
- {
- if (installed && s->repo == installed)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (installed)\n", pool_solvable2str(pool, s));
- else
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (selected)\n", pool_solvable2str(pool, s));
- }
- else
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", pool_solvable2str(pool, s));
- }
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- }
- if (orphaned.count)
- {
- POOL_DEBUG(SOLV_DEBUG_RESULT, "orphaned packages:\n");
- for (i = 0; i < orphaned.count; i++)
- {
- s = pool->solvables + orphaned.elements[i];
- if (solv->decisionmap[solv->orphaned.elements[i]] > 0)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (kept)\n", pool_solvable2str(pool, s));
- else
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s (erased)\n", pool_solvable2str(pool, s));
- }
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- }
- queue_free(&recommendations);
- queue_free(&suggestions);
- queue_free(&orphaned);
- transaction_free(trans);
-}
-
-static inline
-const char *id2strnone(Pool *pool, Id id)
-{
- return !id || id == 1 ? "(none)" : pool_id2str(pool, id);
-}
-
-void
-transaction_print(Transaction *trans)
-{
- Pool *pool = trans->pool;
- Queue classes, pkgs;
- int i, j, mode, l, linel;
- char line[76];
- const char *n;
-
- queue_init(&classes);
- queue_init(&pkgs);
- mode = SOLVER_TRANSACTION_SHOW_OBSOLETES | SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE;
- transaction_classify(trans, mode, &classes);
- for (i = 0; i < classes.count; i += 4)
- {
- Id class = classes.elements[i];
- Id cnt = classes.elements[i + 1];
- switch(class)
- {
- case SOLVER_TRANSACTION_ERASE:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%d erased packages:\n", cnt);
- break;
- case SOLVER_TRANSACTION_INSTALL:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%d installed packages:\n", cnt);
- break;
- case SOLVER_TRANSACTION_REINSTALLED:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%d reinstalled packages:\n", cnt);
- break;
- case SOLVER_TRANSACTION_DOWNGRADED:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%d downgraded packages:\n", cnt);
- break;
- case SOLVER_TRANSACTION_CHANGED:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%d changed packages:\n", cnt);
- break;
- case SOLVER_TRANSACTION_UPGRADED:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%d upgraded packages:\n", cnt);
- break;
- case SOLVER_TRANSACTION_VENDORCHANGE:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%d vendor changes from '%s' to '%s':\n", cnt, id2strnone(pool, classes.elements[i + 2]), id2strnone(pool, classes.elements[i + 3]));
- break;
- case SOLVER_TRANSACTION_ARCHCHANGE:
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%d arch changes from %s to %s:\n", cnt, pool_id2str(pool, classes.elements[i + 2]), pool_id2str(pool, classes.elements[i + 3]));
- break;
- default:
- class = SOLVER_TRANSACTION_IGNORE;
- break;
- }
- if (class == SOLVER_TRANSACTION_IGNORE)
- continue;
- transaction_classify_pkgs(trans, mode, class, classes.elements[i + 2], classes.elements[i + 3], &pkgs);
- *line = 0;
- linel = 0;
- for (j = 0; j < pkgs.count; j++)
- {
- Id p = pkgs.elements[j];
- Solvable *s = pool->solvables + p;
- Solvable *s2;
-
- switch(class)
- {
- case SOLVER_TRANSACTION_DOWNGRADED:
- case SOLVER_TRANSACTION_UPGRADED:
- s2 = pool->solvables + transaction_obs_pkg(trans, p);
- POOL_DEBUG(SOLV_DEBUG_RESULT, " - %s -> %s\n", pool_solvable2str(pool, s), pool_solvable2str(pool, s2));
- break;
- case SOLVER_TRANSACTION_VENDORCHANGE:
- case SOLVER_TRANSACTION_ARCHCHANGE:
- n = pool_id2str(pool, s->name);
- l = strlen(n);
- if (l + linel > sizeof(line) - 3)
- {
- if (*line)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", line);
- *line = 0;
- linel = 0;
- }
- if (l + linel > sizeof(line) - 3)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", n);
- else
- {
- if (*line)
- {
- strcpy(line + linel, ", ");
- linel += 2;
- }
- strcpy(line + linel, n);
- linel += l;
- }
- break;
- default:
- POOL_DEBUG(SOLV_DEBUG_RESULT, " - %s\n", pool_solvable2str(pool, s));
- break;
- }
- }
- if (*line)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s\n", line);
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- }
- queue_free(&classes);
- queue_free(&pkgs);
-}
-
-void
-solver_printproblemruleinfo(Solver *solv, Id probr)
-{
- Pool *pool = solv->pool;
- Id dep, source, target;
- SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep);
-
- POOL_DEBUG(SOLV_DEBUG_RESULT, "%s\n", solver_problemruleinfo2str(solv, type, source, target, dep));
-}
-
-void
-solver_printprobleminfo(Solver *solv, Id problem)
-{
- solver_printproblemruleinfo(solv, solver_findproblemrule(solv, problem));
-}
-
-void
-solver_printcompleteprobleminfo(Solver *solv, Id problem)
-{
- Queue q;
- Id probr;
- int i, nobad = 0;
-
- queue_init(&q);
- solver_findallproblemrules(solv, problem, &q);
- for (i = 0; i < q.count; i++)
- {
- probr = q.elements[i];
- if (!(probr >= solv->updaterules && probr < solv->updaterules_end) && !(probr >= solv->jobrules && probr < solv->jobrules_end))
- {
- nobad = 1;
- break;
- }
- }
- for (i = 0; i < q.count; i++)
- {
- probr = q.elements[i];
- if (nobad && ((probr >= solv->updaterules && probr < solv->updaterules_end) || (probr >= solv->jobrules && probr < solv->jobrules_end)))
- continue;
- solver_printproblemruleinfo(solv, probr);
- }
- queue_free(&q);
-}
-
-static int illegals[] = {
- POLICY_ILLEGAL_DOWNGRADE,
- POLICY_ILLEGAL_NAMECHANGE,
- POLICY_ILLEGAL_ARCHCHANGE,
- POLICY_ILLEGAL_VENDORCHANGE,
- 0
-};
-
-void
-solver_printsolution(Solver *solv, Id problem, Id solution)
-{
- Pool *pool = solv->pool;
- Id p, rp, element;
-
- element = 0;
- while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
- {
- if (p > 0 && rp > 0)
- {
- /* for replacements we want to know why it was illegal */
- Solvable *s = pool->solvables + p, *rs = pool->solvables + rp;
- int illegal = policy_is_illegal(solv, s, rs, 0);
- if (illegal)
- {
- int i;
- for (i = 0; illegals[i]; i++)
- if ((illegal & illegals[i]) != 0)
- {
- POOL_DEBUG(SOLV_DEBUG_RESULT, " - allow %s\n", policy_illegal2str(solv, illegals[i], s, rs));
- illegal ^= illegals[i];
- }
- if (!illegal)
- continue;
- }
- }
- POOL_DEBUG(SOLV_DEBUG_RESULT, " - %s\n", solver_solutionelement2str(solv, p, rp));
- }
-}
-
-void
-solver_printallsolutions(Solver *solv)
-{
- Pool *pool = solv->pool;
- int pcnt;
- Id problem, solution;
-
- POOL_DEBUG(SOLV_DEBUG_RESULT, "Encountered problems! Here are the solutions:\n\n");
- pcnt = 0;
- problem = 0;
- while ((problem = solver_next_problem(solv, problem)) != 0)
- {
- pcnt++;
- POOL_DEBUG(SOLV_DEBUG_RESULT, "Problem %d:\n", pcnt);
- POOL_DEBUG(SOLV_DEBUG_RESULT, "====================================\n");
-#if 1
- solver_printprobleminfo(solv, problem);
-#else
- solver_printcompleteprobleminfo(solv, problem);
-#endif
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- solution = 0;
- while ((solution = solver_next_solution(solv, problem, solution)) != 0)
- {
- solver_printsolution(solv, problem, solution);
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- }
- }
-}
-
-void
-solver_printtrivial(Solver *solv)
-{
- Pool *pool = solv->pool;
- Queue in, out;
- Id p;
- const char *n;
- Solvable *s;
- int i;
-
- queue_init(&in);
- for (p = 1, s = pool->solvables + p; p < solv->pool->nsolvables; p++, s++)
- {
- n = pool_id2str(pool, s->name);
- if (strncmp(n, "patch:", 6) != 0 && strncmp(n, "pattern:", 8) != 0)
- continue;
- queue_push(&in, p);
- }
- if (!in.count)
- {
- queue_free(&in);
- return;
- }
- queue_init(&out);
- solver_trivial_installable(solv, &in, &out);
- POOL_DEBUG(SOLV_DEBUG_RESULT, "trivial installable status:\n");
- for (i = 0; i < in.count; i++)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %s: %d\n", pool_solvid2str(pool, in.elements[i]), out.elements[i]);
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\n");
- queue_free(&in);
- queue_free(&out);
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2008, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * solverdebug.h
- *
- */
-
-#ifndef LIBSOLV_SOLVERDEBUG_H
-#define LIBSOLV_SOLVERDEBUG_H
-
-#include "pooltypes.h"
-#include "pool.h"
-#include "solver.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern void solver_printruleelement(Solver *solv, int type, Rule *r, Id v);
-extern void solver_printrule(Solver *solv, int type, Rule *r);
-extern void solver_printruleclass(Solver *solv, int type, Rule *r);
-extern void solver_printproblem(Solver *solv, Id v);
-extern void solver_printwatches(Solver *solv, int type);
-extern void solver_printdecisionq(Solver *solv, int type);
-extern void solver_printdecisions(Solver *solv);
-extern void solver_printproblemruleinfo(Solver *solv, Id rule);
-extern void solver_printprobleminfo(Solver *solv, Id problem);
-extern void solver_printcompleteprobleminfo(Solver *solv, Id problem);
-extern void solver_printsolution(Solver *solv, Id problem, Id solution);
-extern void solver_printallsolutions(Solver *solv);
-extern void solver_printtrivial(Solver *solv);
-
-extern void transaction_print(Transaction *trans);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_SOLVERDEBUG_H */
-
+++ /dev/null
-/*
- * Copyright (c) 2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include "solvversion.h"
-
-const char solv_version[] = LIBSOLV_VERSION_STRING;
-int solv_version_major = LIBSOLV_VERSION_MAJOR;
-int solv_version_minor = LIBSOLV_VERSION_MINOR;
-int solv_version_patch = LIBSOLV_VERSION_PATCH;
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * solvversion.h
- *
- */
-
-#ifndef LIBSOLV_SOLVVERSION_H
-#define LIBSOLV_SOLVVERSION_H
-
-#define LIBSOLV_VERSION_STRING "@VERSION@"
-#define LIBSOLV_VERSION_MAJOR @LIBSOLV_MAJOR@
-#define LIBSOLV_VERSION_MINOR @LIBSOLV_MINOR@
-#define LIBSOLV_VERSION_PATCH @LIBSOLV_PATCH@
-#define LIBSOLV_VERSION (LIBSOLV_VERSION_MAJOR * 10000 + LIBSOLV_VERSION_MINOR * 100 + LIBSOLV_VERSION_PATCH)
-
-extern const char solv_version[];
-extern int solv_version_major;
-extern int solv_version_minor;
-extern int solv_version_patch;
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <string.h>
-#include "util.h"
-#include "strpool.h"
-
-#define STRING_BLOCK 2047
-#define STRINGSPACE_BLOCK 65535
-
-void
-stringpool_init(Stringpool *ss, const char *strs[])
-{
- unsigned totalsize = 0;
- unsigned count;
-
- memset(ss, 0, sizeof(*ss));
- /* count number and total size of predefined strings */
- for (count = 0; strs[count]; count++)
- totalsize += strlen(strs[count]) + 1;
-
- /* alloc appropriate space */
- ss->stringspace = solv_extend_resize(0, totalsize, 1, STRINGSPACE_BLOCK);
- ss->strings = solv_extend_resize(0, count, sizeof(Offset), STRING_BLOCK);
-
- /* now copy predefined strings into allocated space */
- ss->sstrings = 0;
- for (count = 0; strs[count]; count++)
- {
- strcpy(ss->stringspace + ss->sstrings, strs[count]);
- ss->strings[count] = ss->sstrings;
- ss->sstrings += strlen(strs[count]) + 1;
- }
- ss->nstrings = count;
-}
-
-void
-stringpool_free(Stringpool *ss)
-{
- solv_free(ss->strings);
- solv_free(ss->stringspace);
- solv_free(ss->stringhashtbl);
-}
-
-void
-stringpool_freehash(Stringpool *ss)
-{
- ss->stringhashtbl = solv_free(ss->stringhashtbl);
- ss->stringhashmask = 0;
-}
-
-void
-stringpool_init_empty(Stringpool *ss)
-{
- const char *emptystrs[] = {
- "<NULL>",
- "",
- 0,
- };
- stringpool_init(ss, emptystrs);
-}
-
-void
-stringpool_clone(Stringpool *ss, Stringpool *from)
-{
- memset(ss, 0, sizeof(*ss));
- ss->strings = solv_extend_resize(0, from->nstrings, sizeof(Offset), STRING_BLOCK);
- memcpy(ss->strings, from->strings, from->nstrings * sizeof(Offset));
- ss->stringspace = solv_extend_resize(0, from->sstrings, 1, STRINGSPACE_BLOCK);
- memcpy(ss->stringspace, from->stringspace, from->sstrings);
- ss->nstrings = from->nstrings;
- ss->sstrings = from->sstrings;
-}
-
-Id
-stringpool_strn2id(Stringpool *ss, const char *str, unsigned int len, int create)
-{
- Hashval h, hh, hashmask, oldhashmask;
- int i;
- Id id;
- Hashtable hashtbl;
-
- if (!str)
- return STRID_NULL;
- if (!len)
- return STRID_EMPTY;
-
- hashmask = oldhashmask = ss->stringhashmask;
- hashtbl = ss->stringhashtbl;
-
- /* expand hashtable if needed */
- if ((Hashval)ss->nstrings * 2 > hashmask)
- {
- solv_free(hashtbl);
-
- /* realloc hash table */
- ss->stringhashmask = hashmask = mkmask(ss->nstrings + STRING_BLOCK);
- ss->stringhashtbl = hashtbl = (Hashtable)solv_calloc(hashmask + 1, sizeof(Id));
-
- /* rehash all strings into new hashtable */
- for (i = 1; i < ss->nstrings; i++)
- {
- h = strhash(ss->stringspace + ss->strings[i]) & hashmask;
- hh = HASHCHAIN_START;
- while (hashtbl[h] != 0)
- h = HASHCHAIN_NEXT(h, hh, hashmask);
- hashtbl[h] = i;
- }
- }
-
- /* compute hash and check for match */
- h = strnhash(str, len) & hashmask;
- hh = HASHCHAIN_START;
- while ((id = hashtbl[h]) != 0)
- {
- if(!memcmp(ss->stringspace + ss->strings[id], str, len)
- && ss->stringspace[ss->strings[id] + len] == 0)
- break;
- h = HASHCHAIN_NEXT(h, hh, hashmask);
- }
- if (id || !create) /* exit here if string found */
- return id;
-
- /* this should be a test for a flag that tells us if the
- * correct blocking is used, but adding a flag would break
- * the ABI. So we use the existance of the hash area as
- * indication instead */
- if (!oldhashmask)
- {
- ss->stringspace = solv_extend_resize(ss->stringspace, ss->sstrings + len + 1, 1, STRINGSPACE_BLOCK);
- ss->strings = solv_extend_resize(ss->strings, ss->nstrings + 1, sizeof(Offset), STRING_BLOCK);
- }
-
- /* generate next id and save in table */
- id = ss->nstrings++;
- hashtbl[h] = id;
-
- ss->strings = solv_extend(ss->strings, id, 1, sizeof(Offset), STRING_BLOCK);
- ss->strings[id] = ss->sstrings; /* we will append to the end */
-
- /* append string to stringspace */
- ss->stringspace = solv_extend(ss->stringspace, ss->sstrings, len + 1, 1, STRINGSPACE_BLOCK);
- memcpy(ss->stringspace + ss->sstrings, str, len);
- ss->stringspace[ss->sstrings + len] = 0;
- ss->sstrings += len + 1;
- return id;
-}
-
-Id
-stringpool_str2id(Stringpool *ss, const char *str, int create)
-{
- if (!str)
- return STRID_NULL;
- if (!*str)
- return STRID_EMPTY;
- return stringpool_strn2id(ss, str, (unsigned int)strlen(str), create);
-}
-
-void
-stringpool_shrink(Stringpool *ss)
-{
- ss->stringspace = solv_extend_resize(ss->stringspace, ss->sstrings, 1, STRINGSPACE_BLOCK);
- ss->strings = solv_extend_resize(ss->strings, ss->nstrings, sizeof(Offset), STRING_BLOCK);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-#ifndef LIBSOLV_STRINGPOOL_H
-#define LIBSOLV_STRINGPOOL_H
-
-#include "pooltypes.h"
-#include "hash.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define STRID_NULL 0
-#define STRID_EMPTY 1
-
-struct _Stringpool
-{
- Offset *strings; /* table of offsets into stringspace, indexed by Id: Id -> Offset */
- int nstrings; /* number of ids in strings table */
- char *stringspace; /* space for all unique strings: stringspace + Offset = string */
- Offset sstrings; /* size of used stringspace */
-
- Hashtable stringhashtbl; /* hash table: (string ->) Hash -> Id */
- Hashval stringhashmask; /* modulo value for hash table (size of table - 1) */
-};
-
-void stringpool_init(Stringpool *ss, const char *strs[]);
-void stringpool_init_empty(Stringpool *ss);
-void stringpool_clone(Stringpool *ss, Stringpool *from);
-void stringpool_free(Stringpool *ss);
-void stringpool_freehash(Stringpool *ss);
-
-Id stringpool_str2id(Stringpool *ss, const char *str, int create);
-Id stringpool_strn2id(Stringpool *ss, const char *str, unsigned int len, int create);
-
-void stringpool_shrink(Stringpool *ss);
-
-
-static inline const char *
-stringpool_id2str(Stringpool *ss, Id id)
-{
- return ss->stringspace + ss->strings[id];
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2007-2015, SUSE LLC
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * transaction.c
- *
- * Transaction handling
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <assert.h>
-
-#include "transaction.h"
-#include "solver.h"
-#include "bitmap.h"
-#include "pool.h"
-#include "poolarch.h"
-#include "evr.h"
-#include "util.h"
-
-static int
-obsq_sortcmp(const void *ap, const void *bp, void *dp)
-{
- Id a, b, oa, ob;
- Pool *pool = dp;
- Solvable *s, *oas, *obs;
- int r;
-
- a = ((Id *)ap)[0];
- oa = ((Id *)ap)[1];
- b = ((Id *)bp)[0];
- ob = ((Id *)bp)[1];
- if (a != b)
- return a - b;
- if (oa == ob)
- return 0;
- s = pool->solvables + a;
- oas = pool->solvables + oa;
- obs = pool->solvables + ob;
- if (oas->name != obs->name)
- {
- /* bring "same name" obsoleters (i.e. upgraders) to front */
- if (oas->name == s->name)
- return -1;
- if (obs->name == s->name)
- return 1;
- return strcmp(pool_id2str(pool, oas->name), pool_id2str(pool, obs->name));
- }
- r = pool_evrcmp(pool, oas->evr, obs->evr, EVRCMP_COMPARE);
- if (r)
- return -r; /* highest version first */
- return oa - ob;
-}
-
-void
-transaction_all_obs_pkgs(Transaction *trans, Id p, Queue *pkgs)
-{
- Pool *pool = trans->pool;
- Solvable *s = pool->solvables + p;
- Queue *ti = &trans->transaction_info;
- Id q;
- int i;
-
- queue_empty(pkgs);
- if (p <= 0 || !s->repo)
- return;
- if (s->repo == pool->installed)
- {
- q = trans->transaction_installed[p - pool->installed->start];
- if (!q)
- return;
- if (q > 0)
- {
- /* only a single obsoleting package */
- queue_push(pkgs, q);
- return;
- }
- /* find which packages obsolete us */
- for (i = 0; i < ti->count; i += 2)
- if (ti->elements[i + 1] == p)
- queue_push2(pkgs, p, ti->elements[i]);
- /* sort obsoleters */
- if (pkgs->count > 2)
- solv_sort(pkgs->elements, pkgs->count / 2, 2 * sizeof(Id), obsq_sortcmp, pool);
- for (i = 0; i < pkgs->count; i += 2)
- pkgs->elements[i / 2] = pkgs->elements[i + 1];
- queue_truncate(pkgs, pkgs->count / 2);
- }
- else
- {
- /* find the packages we obsolete */
- for (i = 0; i < ti->count; i += 2)
- {
- if (ti->elements[i] == p)
- queue_push(pkgs, ti->elements[i + 1]);
- else if (pkgs->count)
- break;
- }
- }
-}
-
-Id
-transaction_obs_pkg(Transaction *trans, Id p)
-{
- Pool *pool = trans->pool;
- Solvable *s = pool->solvables + p;
- Queue *ti;
- int i;
-
- if (p <= 0 || !s->repo)
- return 0;
- if (s->repo == pool->installed)
- {
- p = trans->transaction_installed[p - pool->installed->start];
- return p < 0 ? -p : p;
- }
- ti = &trans->transaction_info;
- for (i = 0; i < ti->count; i += 2)
- if (ti->elements[i] == p)
- return ti->elements[i + 1];
- return 0;
-}
-
-
-/*
- * calculate base type of transaction element
- */
-
-static Id
-transaction_base_type(Transaction *trans, Id p)
-{
- Pool *pool = trans->pool;
- Solvable *s, *s2;
- int r;
- Id p2;
-
- if (!MAPTST(&trans->transactsmap, p))
- return SOLVER_TRANSACTION_IGNORE;
- p2 = transaction_obs_pkg(trans, p);
- if (pool->installed && pool->solvables[p].repo == pool->installed)
- {
- /* erase */
- if (!p2)
- return SOLVER_TRANSACTION_ERASE;
- s = pool->solvables + p;
- s2 = pool->solvables + p2;
- if (s->name == s2->name)
- {
- if (s->evr == s2->evr && solvable_identical(s, s2))
- return SOLVER_TRANSACTION_REINSTALLED;
- r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
- if (r < 0)
- return SOLVER_TRANSACTION_UPGRADED;
- else if (r > 0)
- return SOLVER_TRANSACTION_DOWNGRADED;
- return SOLVER_TRANSACTION_CHANGED;
- }
- return SOLVER_TRANSACTION_OBSOLETED;
- }
- else
- {
- /* install or multiinstall */
- int multi = trans->multiversionmap.size && MAPTST(&trans->multiversionmap, p);
- if (multi)
- {
- if (p2)
- {
- s = pool->solvables + p;
- s2 = pool->solvables + p2;
- if (s->name == s2->name && s->arch == s2->arch && s->evr == s2->evr)
- return SOLVER_TRANSACTION_MULTIREINSTALL;
- }
- return SOLVER_TRANSACTION_MULTIINSTALL;
- }
- if (!p2)
- return SOLVER_TRANSACTION_INSTALL;
- s = pool->solvables + p;
- s2 = pool->solvables + p2;
- if (s->name == s2->name)
- {
- if (s->evr == s2->evr && solvable_identical(s, s2))
- return SOLVER_TRANSACTION_REINSTALL;
- r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
- if (r > 0)
- return SOLVER_TRANSACTION_UPGRADE;
- else if (r < 0)
- return SOLVER_TRANSACTION_DOWNGRADE;
- else
- return SOLVER_TRANSACTION_CHANGE;
- }
- return SOLVER_TRANSACTION_OBSOLETES;
- }
-}
-
-/* these packages do not get installed by the package manager */
-static inline int
-is_pseudo_package(Pool *pool, Solvable *s)
-{
- const char *n = pool_id2str(pool, s->name);
- if (*n == 'p' && !strncmp(n, "patch:", 6))
- return 1;
- if (*n == 'p' && !strncmp(n, "pattern:", 8))
- return 1;
- if (*n == 'p' && !strncmp(n, "product:", 8))
- return 1;
- if (*n == 'a' && !strncmp(n, "application:", 12))
- return 1;
- return 0;
-}
-
-/* these packages will never show up installed */
-static inline int
-is_noinst_pseudo_package(Pool *pool, Solvable *s)
-{
- const char *n = pool_id2str(pool, s->name);
- if (!strncmp(n, "patch:", 6))
- return 1;
- if (!strncmp(n, "pattern:", 8))
- {
-#if defined(SUSE) && defined(ENABLE_LINKED_PKGS)
- /* unlike normal patterns, autopatterns *can* be installed (via the package link),
- so do not filter them */
- if (s->provides)
- {
- Id prv, *prvp = s->repo->idarraydata + s->provides;
- while ((prv = *prvp++) != 0)
- if (ISRELDEP(prv) && !strcmp(pool_id2str(pool, prv), "autopattern()"))
- return 0;
- }
-#endif
- return 1;
- }
- return 0;
-}
-
-static int
-obsoleted_by_pseudos_only(Transaction *trans, Id p)
-{
- Pool *pool = trans->pool;
- Queue q;
- Id op;
- int i;
-
- op = transaction_obs_pkg(trans, p);
- if (op && !is_pseudo_package(pool, pool->solvables + op))
- return 0;
- queue_init(&q);
- transaction_all_obs_pkgs(trans, p, &q);
- for (i = 0; i < q.count; i++)
- if (!is_pseudo_package(pool, pool->solvables + q.elements[i]))
- break;
- i = !q.count || i < q.count ? 0 : 1;
- queue_free(&q);
- return i;
-}
-
-/*
- * return type of transaction element
- *
- * filtering is needed if either not all packages are shown
- * or replaces are not shown, as otherwise parts of the
- * transaction might not be shown to the user */
-
-Id
-transaction_type(Transaction *trans, Id p, int mode)
-{
- Pool *pool = trans->pool;
- Solvable *s = pool->solvables + p;
- Queue oq, rq;
- Id type, q;
- int i, j, ref = 0;
-
- if (!s->repo)
- return SOLVER_TRANSACTION_IGNORE;
-
- /* XXX: SUSE only? */
- if (!(mode & SOLVER_TRANSACTION_KEEP_PSEUDO) && is_noinst_pseudo_package(pool, s))
- return SOLVER_TRANSACTION_IGNORE;
-
- type = transaction_base_type(trans, p);
-
- if (type == SOLVER_TRANSACTION_IGNORE)
- return SOLVER_TRANSACTION_IGNORE; /* not part of the transaction */
-
- if ((mode & SOLVER_TRANSACTION_RPM_ONLY) != 0)
- {
- /* application wants to know what to feed to the package manager */
- if (!(mode & SOLVER_TRANSACTION_KEEP_PSEUDO) && is_pseudo_package(pool, s))
- return SOLVER_TRANSACTION_IGNORE;
- if (type == SOLVER_TRANSACTION_ERASE || type == SOLVER_TRANSACTION_INSTALL || type == SOLVER_TRANSACTION_MULTIINSTALL)
- return type;
- if (s->repo == pool->installed)
- {
- /* check if we're a real package that is obsoleted by pseudos */
- if (!is_pseudo_package(pool, s) && obsoleted_by_pseudos_only(trans, s - pool->solvables))
- return SOLVER_TRANSACTION_ERASE;
- return SOLVER_TRANSACTION_IGNORE; /* ignore as we're being obsoleted */
- }
- if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
- return SOLVER_TRANSACTION_MULTIINSTALL;
- return SOLVER_TRANSACTION_INSTALL;
- }
-
- if ((mode & SOLVER_TRANSACTION_SHOW_MULTIINSTALL) == 0)
- {
- /* application wants to make no difference between install
- * and multiinstall */
- if (type == SOLVER_TRANSACTION_MULTIINSTALL)
- type = SOLVER_TRANSACTION_INSTALL;
- if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
- type = SOLVER_TRANSACTION_REINSTALL;
- }
-
- if ((mode & SOLVER_TRANSACTION_CHANGE_IS_REINSTALL) != 0)
- {
- /* application wants to make no difference between change
- * and reinstall */
- if (type == SOLVER_TRANSACTION_CHANGED)
- type = SOLVER_TRANSACTION_REINSTALLED;
- else if (type == SOLVER_TRANSACTION_CHANGE)
- type = SOLVER_TRANSACTION_REINSTALL;
- }
-
- if (type == SOLVER_TRANSACTION_ERASE || type == SOLVER_TRANSACTION_INSTALL || type == SOLVER_TRANSACTION_MULTIINSTALL)
- return type;
-
- if (s->repo == pool->installed && (mode & SOLVER_TRANSACTION_SHOW_ACTIVE) == 0)
- {
- /* erase element and we're showing the passive side */
- if (type == SOLVER_TRANSACTION_OBSOLETED && (mode & SOLVER_TRANSACTION_SHOW_OBSOLETES) == 0)
- type = SOLVER_TRANSACTION_ERASE;
- if (type == SOLVER_TRANSACTION_OBSOLETED && (mode & SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE) != 0)
- type = SOLVER_TRANSACTION_UPGRADED;
- return type;
- }
- if (s->repo != pool->installed && (mode & SOLVER_TRANSACTION_SHOW_ACTIVE) != 0)
- {
- /* install element and we're showing the active side */
- if (type == SOLVER_TRANSACTION_OBSOLETES && (mode & SOLVER_TRANSACTION_SHOW_OBSOLETES) == 0)
- type = SOLVER_TRANSACTION_INSTALL;
- if (type == SOLVER_TRANSACTION_OBSOLETES && (mode & SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE) != 0)
- type = SOLVER_TRANSACTION_UPGRADE;
- return type;
- }
-
- /* the element doesn't match the show mode */
-
- /* if we're showing all references, we can ignore this package */
- if ((mode & (SOLVER_TRANSACTION_SHOW_ALL|SOLVER_TRANSACTION_SHOW_OBSOLETES)) == (SOLVER_TRANSACTION_SHOW_ALL|SOLVER_TRANSACTION_SHOW_OBSOLETES))
- return SOLVER_TRANSACTION_IGNORE;
-
- /* we're not showing all refs. check if some other package
- * references us. If yes, it's safe to ignore this package,
- * otherwise we need to map the type */
-
- /* most of the time there's only one reference, so check it first */
- q = transaction_obs_pkg(trans, p);
-
- if ((mode & SOLVER_TRANSACTION_SHOW_OBSOLETES) == 0)
- {
- Solvable *sq = pool->solvables + q;
- if (sq->name != s->name)
- {
- /* it's a replace but we're not showing replaces. map type. */
- if (s->repo == pool->installed)
- return SOLVER_TRANSACTION_ERASE;
- else if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
- return SOLVER_TRANSACTION_MULTIINSTALL;
- else
- return SOLVER_TRANSACTION_INSTALL;
- }
- }
-
- /* if there's a match, p will be shown when q
- * is processed */
- if (transaction_obs_pkg(trans, q) == p)
- return SOLVER_TRANSACTION_IGNORE;
-
- /* too bad, a miss. check em all */
- queue_init(&oq);
- queue_init(&rq);
- transaction_all_obs_pkgs(trans, p, &oq);
- for (i = 0; i < oq.count; i++)
- {
- q = oq.elements[i];
- if ((mode & SOLVER_TRANSACTION_SHOW_OBSOLETES) == 0)
- {
- Solvable *sq = pool->solvables + q;
- if (sq->name != s->name)
- continue;
- }
- /* check if we are referenced? */
- if ((mode & SOLVER_TRANSACTION_SHOW_ALL) != 0)
- {
- transaction_all_obs_pkgs(trans, q, &rq);
- for (j = 0; j < rq.count; j++)
- if (rq.elements[j] == p)
- {
- ref = 1;
- break;
- }
- if (ref)
- break;
- }
- else if (transaction_obs_pkg(trans, q) == p)
- {
- ref = 1;
- break;
- }
- }
- queue_free(&oq);
- queue_free(&rq);
-
- if (!ref)
- {
- /* we're not referenced. map type */
- if (s->repo == pool->installed)
- return SOLVER_TRANSACTION_ERASE;
- else if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
- return SOLVER_TRANSACTION_MULTIINSTALL;
- else
- return SOLVER_TRANSACTION_INSTALL;
- }
- /* there was a ref, so p is shown with some other package */
- return SOLVER_TRANSACTION_IGNORE;
-}
-
-
-
-static int
-classify_cmp(const void *ap, const void *bp, void *dp)
-{
- Transaction *trans = dp;
- Pool *pool = trans->pool;
- const Id *a = ap;
- const Id *b = bp;
- int r;
-
- r = a[0] - b[0];
- if (r)
- return r;
- r = a[2] - b[2];
- if (r)
- return a[2] && b[2] ? strcmp(pool_id2str(pool, a[2]), pool_id2str(pool, b[2])) : r;
- r = a[3] - b[3];
- if (r)
- return a[3] && b[3] ? strcmp(pool_id2str(pool, a[3]), pool_id2str(pool, b[3])) : r;
- return 0;
-}
-
-static int
-classify_cmp_pkgs(const void *ap, const void *bp, void *dp)
-{
- Transaction *trans = dp;
- Pool *pool = trans->pool;
- Id a = *(Id *)ap;
- Id b = *(Id *)bp;
- Solvable *sa, *sb;
-
- sa = pool->solvables + a;
- sb = pool->solvables + b;
- if (sa->name != sb->name)
- return strcmp(pool_id2str(pool, sa->name), pool_id2str(pool, sb->name));
- if (sa->evr != sb->evr)
- {
- int r = pool_evrcmp(pool, sa->evr, sb->evr, EVRCMP_COMPARE);
- if (r)
- return r;
- }
- return a - b;
-}
-
-static inline void
-queue_push4(Queue *q, Id id1, Id id2, Id id3, Id id4)
-{
- queue_push(q, id1);
- queue_push(q, id2);
- queue_push(q, id3);
- queue_push(q, id4);
-}
-
-static inline void
-queue_unshift4(Queue *q, Id id1, Id id2, Id id3, Id id4)
-{
- queue_unshift(q, id4);
- queue_unshift(q, id3);
- queue_unshift(q, id2);
- queue_unshift(q, id1);
-}
-
-void
-transaction_classify(Transaction *trans, int mode, Queue *classes)
-{
- Pool *pool = trans->pool;
- int ntypes[SOLVER_TRANSACTION_MAXTYPE + 1];
- Solvable *s, *sq;
- Id v, vq, type, p, q;
- int i, j;
-
- queue_empty(classes);
- memset(ntypes, 0, sizeof(ntypes));
- /* go through transaction and classify each step */
- for (i = 0; i < trans->steps.count; i++)
- {
- p = trans->steps.elements[i];
- s = pool->solvables + p;
- type = transaction_type(trans, p, mode);
- ntypes[type]++;
- if (!pool->installed || s->repo != pool->installed)
- continue;
- /* don't report vendor/arch changes if we were mapped to erase. */
- if (type == SOLVER_TRANSACTION_ERASE)
- continue;
- /* look at arch/vendor changes */
- q = transaction_obs_pkg(trans, p);
- if (!q)
- continue;
- sq = pool->solvables + q;
-
- v = s->arch;
- vq = sq->arch;
- if (v != vq)
- {
- if ((mode & SOLVER_TRANSACTION_MERGE_ARCHCHANGES) != 0)
- v = vq = 0;
- for (j = 0; j < classes->count; j += 4)
- if (classes->elements[j] == SOLVER_TRANSACTION_ARCHCHANGE && classes->elements[j + 2] == v && classes->elements[j + 3] == vq)
- break;
- if (j == classes->count)
- queue_push4(classes, SOLVER_TRANSACTION_ARCHCHANGE, 1, v, vq);
- else
- classes->elements[j + 1]++;
- }
-
- v = s->vendor ? s->vendor : 1;
- vq = sq->vendor ? sq->vendor : 1;
- if (v != vq)
- {
- if ((mode & SOLVER_TRANSACTION_MERGE_VENDORCHANGES) != 0)
- v = vq = 0;
- for (j = 0; j < classes->count; j += 4)
- if (classes->elements[j] == SOLVER_TRANSACTION_VENDORCHANGE && classes->elements[j + 2] == v && classes->elements[j + 3] == vq)
- break;
- if (j == classes->count)
- queue_push4(classes, SOLVER_TRANSACTION_VENDORCHANGE, 1, v, vq);
- else
- classes->elements[j + 1]++;
- }
- }
- /* now sort all vendor/arch changes */
- if (classes->count > 4)
- solv_sort(classes->elements, classes->count / 4, 4 * sizeof(Id), classify_cmp, trans);
- /* finally add all classes. put erases last */
- i = SOLVER_TRANSACTION_ERASE;
- if (ntypes[i])
- queue_unshift4(classes, i, ntypes[i], 0, 0);
- for (i = SOLVER_TRANSACTION_MAXTYPE; i > 0; i--)
- {
- if (!ntypes[i])
- continue;
- if (i == SOLVER_TRANSACTION_ERASE)
- continue;
- queue_unshift4(classes, i, ntypes[i], 0, 0);
- }
-}
-
-void
-transaction_classify_pkgs(Transaction *trans, int mode, Id class, Id from, Id to, Queue *pkgs)
-{
- Pool *pool = trans->pool;
- int i;
- Id type, p, q;
- Solvable *s, *sq;
-
- queue_empty(pkgs);
- for (i = 0; i < trans->steps.count; i++)
- {
- p = trans->steps.elements[i];
- s = pool->solvables + p;
- if (class <= SOLVER_TRANSACTION_MAXTYPE)
- {
- type = transaction_type(trans, p, mode);
- if (type == class)
- queue_push(pkgs, p);
- continue;
- }
- if (!pool->installed || s->repo != pool->installed)
- continue;
- q = transaction_obs_pkg(trans, p);
- if (!q)
- continue;
- sq = pool->solvables + q;
- if (class == SOLVER_TRANSACTION_ARCHCHANGE)
- {
- if ((!from && !to) || (s->arch == from && sq->arch == to))
- queue_push(pkgs, p);
- continue;
- }
- if (class == SOLVER_TRANSACTION_VENDORCHANGE)
- {
- Id v = s->vendor ? s->vendor : 1;
- Id vq = sq->vendor ? sq->vendor : 1;
- if ((!from && !to) || (v == from && vq == to))
- queue_push(pkgs, p);
- continue;
- }
- }
- if (pkgs->count > 1)
- solv_sort(pkgs->elements, pkgs->count, sizeof(Id), classify_cmp_pkgs, trans);
-}
-
-static void
-create_transaction_info(Transaction *trans, Queue *decisionq)
-{
- Pool *pool = trans->pool;
- Queue *ti = &trans->transaction_info;
- Repo *installed = pool->installed;
- int i, j, multi;
- Id p, p2, pp2;
- Solvable *s, *s2;
-
- queue_empty(ti);
- trans->transaction_installed = solv_free(trans->transaction_installed);
- if (!installed)
- return; /* no info needed */
- for (i = 0; i < decisionq->count; i++)
- {
- p = decisionq->elements[i];
- if (p <= 0 || p == SYSTEMSOLVABLE)
- continue;
- s = pool->solvables + p;
- if (!s->repo || s->repo == installed)
- continue;
- multi = trans->multiversionmap.size && MAPTST(&trans->multiversionmap, p);
- FOR_PROVIDES(p2, pp2, s->name)
- {
- if (!MAPTST(&trans->transactsmap, p2))
- continue;
- s2 = pool->solvables + p2;
- if (s2->repo != installed)
- continue;
- if (multi && (s->name != s2->name || s->evr != s2->evr || s->arch != s2->arch))
- continue;
- if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
- continue;
- if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
- continue;
- queue_push2(ti, p, p2);
- }
- if (s->obsoletes && !multi)
- {
- Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- {
- FOR_PROVIDES(p2, pp2, obs)
- {
- if (!MAPTST(&trans->transactsmap, p2))
- continue;
- s2 = pool->solvables + p2;
- if (s2->repo != installed)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, s2, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
- continue;
- queue_push2(ti, p, p2);
- }
- }
- }
- }
- if (ti->count > 2)
- {
- /* sort and unify */
- solv_sort(ti->elements, ti->count / 2, 2 * sizeof(Id), obsq_sortcmp, pool);
- for (i = j = 2; i < ti->count; i += 2)
- {
- if (ti->elements[i] == ti->elements[j - 2] && ti->elements[i + 1] == ti->elements[j - 1])
- continue;
- ti->elements[j++] = ti->elements[i];
- ti->elements[j++] = ti->elements[i + 1];
- }
- queue_truncate(ti, j);
- }
-
- /* create transaction_installed helper */
- /* entry > 0: exactly one obsoleter, entry < 0: multiple obsoleters, -entry is "best" */
- trans->transaction_installed = solv_calloc(installed->end - installed->start, sizeof(Id));
- for (i = 0; i < ti->count; i += 2)
- {
- j = ti->elements[i + 1] - installed->start;
- if (!trans->transaction_installed[j])
- trans->transaction_installed[j] = ti->elements[i];
- else
- {
- /* more than one package obsoletes us. compare to find "best" */
- Id q[4];
- if (trans->transaction_installed[j] > 0)
- trans->transaction_installed[j] = -trans->transaction_installed[j];
- q[0] = q[2] = ti->elements[i + 1];
- q[1] = ti->elements[i];
- q[3] = -trans->transaction_installed[j];
- if (obsq_sortcmp(q, q + 2, pool) < 0)
- trans->transaction_installed[j] = -ti->elements[i];
- }
- }
-}
-
-/* create a transaction from the decisionq */
-Transaction *
-transaction_create_decisionq(Pool *pool, Queue *decisionq, Map *multiversionmap)
-{
- Repo *installed = pool->installed;
- int i, needmulti;
- Id p;
- Solvable *s;
- Transaction *trans;
-
- trans = transaction_create(pool);
- if (multiversionmap && !multiversionmap->size)
- multiversionmap = 0; /* ignore empty map */
- queue_empty(&trans->steps);
- map_init(&trans->transactsmap, pool->nsolvables);
- needmulti = 0;
- for (i = 0; i < decisionq->count; i++)
- {
- p = decisionq->elements[i];
- s = pool->solvables + (p > 0 ? p : -p);
- if (!s->repo)
- continue;
- if (installed && s->repo == installed && p < 0)
- MAPSET(&trans->transactsmap, -p);
- if (!(installed && s->repo == installed) && p > 0)
- {
- MAPSET(&trans->transactsmap, p);
- if (multiversionmap && MAPTST(multiversionmap, p))
- needmulti = 1;
- }
- }
- MAPCLR(&trans->transactsmap, SYSTEMSOLVABLE);
- if (needmulti)
- map_init_clone(&trans->multiversionmap, multiversionmap);
-
- create_transaction_info(trans, decisionq);
-
- if (installed)
- {
- FOR_REPO_SOLVABLES(installed, p, s)
- {
- if (MAPTST(&trans->transactsmap, p))
- queue_push(&trans->steps, p);
- }
- }
- for (i = 0; i < decisionq->count; i++)
- {
- p = decisionq->elements[i];
- if (p > 0 && MAPTST(&trans->transactsmap, p))
- queue_push(&trans->steps, p);
- }
- return trans;
-}
-
-int
-transaction_installedresult(Transaction *trans, Queue *installedq)
-{
- Pool *pool = trans->pool;
- Repo *installed = pool->installed;
- Solvable *s;
- int i, cutoff;
- Id p;
-
- queue_empty(installedq);
- /* first the new installs, than the kept packages */
- for (i = 0; i < trans->steps.count; i++)
- {
- p = trans->steps.elements[i];
- s = pool->solvables + p;
- if (installed && s->repo == installed)
- continue;
- queue_push(installedq, p);
- }
- cutoff = installedq->count;
- if (installed)
- {
- FOR_REPO_SOLVABLES(installed, p, s)
- if (!MAPTST(&trans->transactsmap, p))
- queue_push(installedq, p);
- }
- return cutoff;
-}
-
-static void
-transaction_make_installedmap(Transaction *trans, Map *installedmap)
-{
- Pool *pool = trans->pool;
- Repo *installed = pool->installed;
- Solvable *s;
- Id p;
- int i;
-
- map_init(installedmap, pool->nsolvables);
- for (i = 0; i < trans->steps.count; i++)
- {
- p = trans->steps.elements[i];
- s = pool->solvables + p;
- if (!installed || s->repo != installed)
- MAPSET(installedmap, p);
- }
- if (installed)
- {
- FOR_REPO_SOLVABLES(installed, p, s)
- if (!MAPTST(&trans->transactsmap, p))
- MAPSET(installedmap, p);
- }
-}
-
-int
-transaction_calc_installsizechange(Transaction *trans)
-{
- Map installedmap;
- int change;
-
- transaction_make_installedmap(trans, &installedmap);
- change = pool_calc_installsizechange(trans->pool, &installedmap);
- map_free(&installedmap);
- return change;
-}
-
-void
-transaction_calc_duchanges(Transaction *trans, DUChanges *mps, int nmps)
-{
- Map installedmap;
-
- transaction_make_installedmap(trans, &installedmap);
- pool_calc_duchanges(trans->pool, &installedmap, mps, nmps);
- map_free(&installedmap);
-}
-
-Transaction *
-transaction_create(Pool *pool)
-{
- Transaction *trans = solv_calloc(1, sizeof(*trans));
- trans->pool = pool;
- return trans;
-}
-
-Transaction *
-transaction_create_clone(Transaction *srctrans)
-{
- Transaction *trans = transaction_create(srctrans->pool);
- queue_init_clone(&trans->steps, &srctrans->steps);
- queue_init_clone(&trans->transaction_info, &srctrans->transaction_info);
- if (srctrans->transaction_installed)
- {
- Repo *installed = srctrans->pool->installed;
- trans->transaction_installed = solv_memdup2(srctrans->transaction_installed, installed->end - installed->start, sizeof(Id));
- }
- map_init_clone(&trans->transactsmap, &srctrans->transactsmap);
- map_init_clone(&trans->multiversionmap, &srctrans->multiversionmap);
- if (srctrans->orderdata)
- transaction_clone_orderdata(trans, srctrans);
- return trans;
-}
-
-void
-transaction_free(Transaction *trans)
-{
- queue_free(&trans->steps);
- queue_free(&trans->transaction_info);
- trans->transaction_installed = solv_free(trans->transaction_installed);
- map_free(&trans->transactsmap);
- map_free(&trans->multiversionmap);
- if (trans->orderdata)
- transaction_free_orderdata(trans);
- free(trans);
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * transaction.h
- *
- */
-
-#ifndef LIBSOLV_TRANSACTION_H
-#define LIBSOLV_TRANSACTION_H
-
-#include "pooltypes.h"
-#include "queue.h"
-#include "bitmap.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct _Pool;
-struct _DUChanges;
-struct _TransactionOrderdata;
-
-typedef struct _Transaction {
- struct _Pool *pool; /* back pointer to pool */
-
- Queue steps; /* the transaction steps */
-
-#ifdef LIBSOLV_INTERNAL
- Queue transaction_info;
- Id *transaction_installed;
- Map transactsmap;
- Map multiversionmap;
-
- struct _TransactionOrderdata *orderdata;
-#endif
-
-} Transaction;
-
-
-/* step types */
-#define SOLVER_TRANSACTION_IGNORE 0x00
-
-#define SOLVER_TRANSACTION_ERASE 0x10
-#define SOLVER_TRANSACTION_REINSTALLED 0x11
-#define SOLVER_TRANSACTION_DOWNGRADED 0x12
-#define SOLVER_TRANSACTION_CHANGED 0x13
-#define SOLVER_TRANSACTION_UPGRADED 0x14
-#define SOLVER_TRANSACTION_OBSOLETED 0x15
-
-#define SOLVER_TRANSACTION_INSTALL 0x20
-#define SOLVER_TRANSACTION_REINSTALL 0x21
-#define SOLVER_TRANSACTION_DOWNGRADE 0x22
-#define SOLVER_TRANSACTION_CHANGE 0x23
-#define SOLVER_TRANSACTION_UPGRADE 0x24
-#define SOLVER_TRANSACTION_OBSOLETES 0x25
-
-#define SOLVER_TRANSACTION_MULTIINSTALL 0x30
-#define SOLVER_TRANSACTION_MULTIREINSTALL 0x31
-
-#define SOLVER_TRANSACTION_MAXTYPE 0x3f
-
-/* modes */
-#define SOLVER_TRANSACTION_SHOW_ACTIVE (1 << 0)
-#define SOLVER_TRANSACTION_SHOW_ALL (1 << 1)
-#define SOLVER_TRANSACTION_SHOW_OBSOLETES (1 << 2)
-#define SOLVER_TRANSACTION_SHOW_MULTIINSTALL (1 << 3)
-#define SOLVER_TRANSACTION_CHANGE_IS_REINSTALL (1 << 4)
-#define SOLVER_TRANSACTION_MERGE_VENDORCHANGES (1 << 5)
-#define SOLVER_TRANSACTION_MERGE_ARCHCHANGES (1 << 6)
-
-#define SOLVER_TRANSACTION_RPM_ONLY (1 << 7)
-
-#define SOLVER_TRANSACTION_KEEP_PSEUDO (1 << 8)
-
-#define SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE (1 << 9)
-
-/* extra classifications */
-#define SOLVER_TRANSACTION_ARCHCHANGE 0x100
-#define SOLVER_TRANSACTION_VENDORCHANGE 0x101
-
-/* order flags */
-#define SOLVER_TRANSACTION_KEEP_ORDERDATA (1 << 0)
-#define SOLVER_TRANSACTION_KEEP_ORDERCYCLES (1 << 1)
-
-/* cycle severities */
-#define SOLVER_ORDERCYCLE_HARMLESS 0
-#define SOLVER_ORDERCYCLE_NORMAL 1
-#define SOLVER_ORDERCYCLE_CRITICAL 2
-
-extern Transaction *transaction_create(struct _Pool *pool);
-extern Transaction *transaction_create_decisionq(struct _Pool *pool, Queue *decisionq, Map *multiversionmap);
-extern Transaction *transaction_create_clone(Transaction *srctrans);
-extern void transaction_free(Transaction *trans);
-
-/* if p is installed, returns with pkg(s) obsolete p */
-/* if p is not installed, returns with pkg(s) we obsolete */
-extern Id transaction_obs_pkg(Transaction *trans, Id p);
-extern void transaction_all_obs_pkgs(Transaction *trans, Id p, Queue *pkgs);
-
-/* return step type of a transaction element */
-extern Id transaction_type(Transaction *trans, Id p, int mode);
-
-/* return sorted collection of all step types */
-/* classify_pkgs can be used to return all packages of a type */
-extern void transaction_classify(Transaction *trans, int mode, Queue *classes);
-extern void transaction_classify_pkgs(Transaction *trans, int mode, Id type, Id from, Id to, Queue *pkgs);
-
-/* return all packages that will be installed after the transaction is run*/
-/* The new packages are put at the head of the queue, the number of new
- packages is returned */
-extern int transaction_installedresult(Transaction *trans, Queue *installedq);
-
-int transaction_calc_installsizechange(Transaction *trans);
-void transaction_calc_duchanges(Transaction *trans, struct _DUChanges *mps, int nmps);
-
-
-
-/* order a transaction */
-extern void transaction_order(Transaction *trans, int flags);
-
-/* roll your own order funcion:
- * add pkgs free for installation to queue choices after chosen was
- * installed. start with chosen = 0
- * needs an ordered transaction created with SOLVER_TRANSACTION_KEEP_ORDERDATA */
-extern int transaction_order_add_choices(Transaction *trans, Id chosen, Queue *choices);
-/* add obsoleted packages into transaction steps */
-extern void transaction_add_obsoleted(Transaction *trans);
-
-/* debug function, report problems found in the order */
-extern void transaction_check_order(Transaction *trans);
-
-/* order cycle introspection */
-extern void transaction_order_get_cycleids(Transaction *trans, Queue *q, int minseverity);
-extern int transaction_order_get_cycle(Transaction *trans, Id cid, Queue *q);
-
-extern void transaction_free_orderdata(Transaction *trans);
-extern void transaction_clone_orderdata(Transaction *trans, Transaction *srctrans);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/time.h>
-
-#include "util.h"
-
-void
-solv_oom(size_t num, size_t len)
-{
- if (num)
- fprintf(stderr, "Out of memory allocating %zu*%zu bytes!\n", num, len);
- else
- fprintf(stderr, "Out of memory allocating %zu bytes!\n", len);
- abort();
- exit(1);
-}
-
-void *
-solv_malloc(size_t len)
-{
- void *r = malloc(len ? len : 1);
- if (!r)
- solv_oom(0, len);
- return r;
-}
-
-void *
-solv_malloc2(size_t num, size_t len)
-{
- if (len && (num * len) / len != num)
- solv_oom(num, len);
- return solv_malloc(num * len);
-}
-
-void *
-solv_realloc(void *old, size_t len)
-{
- if (old == 0)
- old = malloc(len ? len : 1);
- else
- old = realloc(old, len ? len : 1);
- if (!old)
- solv_oom(0, len);
- return old;
-}
-
-void *
-solv_realloc2(void *old, size_t num, size_t len)
-{
- if (len && (num * len) / len != num)
- solv_oom(num, len);
- return solv_realloc(old, num * len);
-}
-
-void *
-solv_calloc(size_t num, size_t len)
-{
- void *r;
- if (num == 0 || len == 0)
- r = malloc(1);
- else
- r = calloc(num, len);
- if (!r)
- solv_oom(num, len);
- return r;
-}
-
-/* this was solv_realloc2(old, len, size), but we now overshoot
- * for huge len sizes */
-void *
-solv_extend_realloc(void *old, size_t len, size_t size, size_t block)
-{
- size_t xblock = (block + 1) << 5;
- len = (len + block) & ~block;
- if (len >= xblock && xblock)
- {
- xblock <<= 1;
- while (len >= xblock && xblock)
- xblock <<= 1;
- if (xblock)
- {
- size_t nlen;
- xblock = (xblock >> 5) - 1;
- nlen = (len + xblock) & ~xblock;
- if (nlen > len)
- len = nlen;
- }
- }
- return solv_realloc2(old, len, size);
-}
-
-void *
-solv_free(void *mem)
-{
- if (mem)
- free(mem);
- return 0;
-}
-
-char *
-solv_strdup(const char *s)
-{
- char *r;
- if (!s)
- return 0;
- r = strdup(s);
- if (!r)
- solv_oom(0, strlen(s));
- return r;
-}
-
-unsigned int
-solv_timems(unsigned int subtract)
-{
- struct timeval tv;
- unsigned int r;
-
- if (gettimeofday(&tv, 0))
- return 0;
- r = (((unsigned int)tv.tv_sec >> 16) * 1000) << 16;
- r += ((unsigned int)tv.tv_sec & 0xffff) * 1000;
- r += (unsigned int)tv.tv_usec / 1000;
- return r - subtract;
-}
-
-/* bsd's qsort_r has different arguments, so we define our
- own version in case we need to do some clever mapping
-
- see also: http://sources.redhat.com/ml/libc-alpha/2008-12/msg00003.html
- */
-#if defined(__GLIBC__) && (defined(HAVE_QSORT_R) || defined(HAVE___QSORT_R))
-
-void
-solv_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *compard)
-{
-# if defined(HAVE_QSORT_R)
- qsort_r(base, nmemb, size, compar, compard);
-# else
- /* backported for SLE10-SP2 */
- __qsort_r(base, nmemb, size, compar, compard);
-# endif
-
-}
-
-#elif defined(HAVE_QSORT_R) /* not glibc, but has qsort_r() */
-
-struct solv_sort_data {
- int (*compar)(const void *, const void *, void *);
- void *compard;
-};
-
-static int
-solv_sort_helper(void *compard, const void *a, const void *b)
-{
- struct solv_sort_data *d = compard;
- return (*d->compar)(a, b, d->compard);
-}
-
-void
-solv_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *compard)
-{
- struct solv_sort_data d;
- d.compar = compar;
- d.compard = compard;
- qsort_r(base, nmemb, size, &d, solv_sort_helper);
-}
-
-#else /* not glibc and no qsort_r() */
-/* use own version of qsort if none available */
-#include "qsort_r.c"
-#endif
-
-char *
-solv_dupjoin(const char *str1, const char *str2, const char *str3)
-{
- int l1, l2, l3;
- char *s, *str;
- l1 = str1 ? strlen(str1) : 0;
- l2 = str2 ? strlen(str2) : 0;
- l3 = str3 ? strlen(str3) : 0;
- s = str = solv_malloc(l1 + l2 + l3 + 1);
- if (l1)
- {
- strcpy(s, str1);
- s += l1;
- }
- if (l2)
- {
- strcpy(s, str2);
- s += l2;
- }
- if (l3)
- {
- strcpy(s, str3);
- s += l3;
- }
- *s = 0;
- return str;
-}
-
-char *
-solv_dupappend(const char *str1, const char *str2, const char *str3)
-{
- char *str = solv_dupjoin(str1, str2, str3);
- solv_free((void *)str1);
- return str;
-}
-
-int
-solv_hex2bin(const char **strp, unsigned char *buf, int bufl)
-{
- const char *str = *strp;
- int i;
-
- for (i = 0; i < bufl; i++)
- {
- int c = *str;
- int d;
- if (c >= '0' && c <= '9')
- d = c - '0';
- else if (c >= 'a' && c <= 'f')
- d = c - ('a' - 10);
- else if (c >= 'A' && c <= 'F')
- d = c - ('A' - 10);
- else
- break;
- c = *++str;
- d <<= 4;
- if (c >= '0' && c <= '9')
- d |= c - '0';
- else if (c >= 'a' && c <= 'f')
- d |= c - ('a' - 10);
- else if (c >= 'A' && c <= 'F')
- d |= c - ('A' - 10);
- else
- break;
- buf[i] = d;
- ++str;
- }
- *strp = str;
- return i;
-}
-
-char *
-solv_bin2hex(const unsigned char *buf, int l, char *str)
-{
- int i;
- for (i = 0; i < l; i++, buf++)
- {
- int c = *buf >> 4;
- *str++ = c < 10 ? c + '0' : c + ('a' - 10);
- c = *buf & 15;
- *str++ = c < 10 ? c + '0' : c + ('a' - 10);
- }
- *str = 0;
- return str;
-}
-
-size_t
-solv_validutf8(const char *buf)
-{
- const unsigned char *p;
- int x;
-
- for (p = (const unsigned char *)buf; (x = *p) != 0; p++)
- {
- if (x < 0x80)
- continue;
- if (x < 0xc0)
- break;
- if (x < 0xe0)
- {
- /* one byte to follow */
- if ((p[1] & 0xc0) != 0x80)
- break;
- if ((x & 0x1e) == 0)
- break; /* not minimal */
- p += 1;
- continue;
- }
- if (x < 0xf0)
- {
- /* two bytes to follow */
- if ((p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80)
- break;
- if ((x & 0x0f) == 0 && (p[1] & 0x20) == 0)
- break; /* not minimal */
- if (x == 0xed && (p[1] & 0x20) != 0)
- break; /* d800-dfff surrogate */
- if (x == 0xef && p[1] == 0xbf && (p[2] == 0xbe || p[2] == 0xbf))
- break; /* fffe or ffff */
- p += 2;
- continue;
- }
- if (x < 0xf8)
- {
- /* three bytes to follow */
- if ((p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80)
- break;
- if ((x & 0x07) == 0 && (p[1] & 0x30) == 0)
- break; /* not minimal */
- if ((x & 0x07) > 4 || ((x & 0x07) == 4 && (p[1] & 0x30) != 0))
- break; /* above 0x10ffff */
- p += 3;
- continue;
- }
- break; /* maybe valid utf8, but above 0x10ffff */
- }
- return (const char *)p - buf;
-}
-
-char *
-solv_latin1toutf8(const char *buf)
-{
- int l = 1;
- const char *p;
- char *r, *rp;
-
- for (p = buf; *p; p++)
- if ((*(const unsigned char *)p & 128) != 0)
- l++;
- r = rp = solv_malloc(p - buf + l);
- for (p = buf; *p; p++)
- {
- if ((*(const unsigned char *)p & 128) != 0)
- {
- *rp++ = *(const unsigned char *)p & 64 ? 0xc3 : 0xc2;
- *rp++ = *p & 0xbf;
- }
- else
- *rp++ = *p;
- }
- *rp = 0;
- return r;
-}
-
-char *
-solv_replacebadutf8(const char *buf, int replchar)
-{
- size_t l, nl;
- const char *p;
- char *r = 0, *rp = 0;
- int repllen, replin;
-
- if (replchar < 0 || replchar > 0x10ffff)
- replchar = 0xfffd;
- if (!replchar)
- repllen = replin = 0;
- else if (replchar < 0x80)
- {
- repllen = 1;
- replin = (replchar & 0x40) | 0x80;
- }
- else if (replchar < 0x800)
- {
- repllen = 2;
- replin = 0x40;
- }
- else if (replchar < 0x10000)
- {
- repllen = 3;
- replin = 0x60;
- }
- else
- {
- repllen = 4;
- replin = 0x70;
- }
- for (;;)
- {
- for (p = buf, nl = 0; *p; )
- {
- l = solv_validutf8(p);
- if (rp && l)
- {
- memcpy(rp, p, l);
- rp += l;
- }
- nl += l;
- p += l;
- if (!*p)
- break;
- /* found a bad char, replace with replchar */
- if (rp && replchar)
- {
- switch (repllen)
- {
- case 4:
- *rp++ = (replchar >> 18 & 0x3f) | 0x80;
- case 3:
- *rp++ = (replchar >> 12 & 0x3f) | 0x80;
- case 2:
- *rp++ = (replchar >> 6 & 0x3f) | 0x80;
- default:
- *rp++ = (replchar & 0x3f) | 0x80;
- }
- rp[-repllen] ^= replin;
- }
- nl += repllen;
- p++;
- while ((*(const unsigned char *)p & 0xc0) == 0x80)
- p++;
- }
- if (rp)
- break;
- r = rp = solv_malloc(nl + 1);
- }
- *rp = 0;
- return r;
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * util.h
- *
- */
-
-#ifndef LIBSOLV_UTIL_H
-#define LIBSOLV_UTIL_H
-
-#include <stddef.h>
-#include <string.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * malloc
- * exits with error message on error
- */
-extern void *solv_malloc(size_t);
-extern void *solv_malloc2(size_t, size_t);
-extern void *solv_calloc(size_t, size_t);
-extern void *solv_realloc(void *, size_t);
-extern void *solv_realloc2(void *, size_t, size_t);
-extern void *solv_extend_realloc(void *, size_t, size_t, size_t);
-extern void *solv_free(void *);
-extern char *solv_strdup(const char *);
-extern void solv_oom(size_t, size_t);
-extern unsigned int solv_timems(unsigned int subtract);
-extern void solv_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *compard);
-extern char *solv_dupjoin(const char *str1, const char *str2, const char *str3);
-extern char *solv_dupappend(const char *str1, const char *str2, const char *str3);
-extern int solv_hex2bin(const char **strp, unsigned char *buf, int bufl);
-extern char *solv_bin2hex(const unsigned char *buf, int l, char *str);
-extern size_t solv_validutf8(const char *buf);
-extern char *solv_latin1toutf8(const char *buf);
-extern char *solv_replacebadutf8(const char *buf, int replchar);
-
-
-static inline void *solv_extend(void *buf, size_t len, size_t nmemb, size_t size, size_t block)
-{
- if (nmemb == 1)
- {
- if ((len & block) == 0)
- buf = solv_extend_realloc(buf, len + 1, size, block);
- }
- else
- {
- if (((len - 1) | block) != ((len + nmemb - 1) | block))
- buf = solv_extend_realloc(buf, len + nmemb, size, block);
- }
- return buf;
-}
-
-/**
- * extend an array by reallocation and zero's the new section
- * buf old pointer
- * len current size
- * nmbemb number of elements to add
- * size size of each element
- * block block size used to allocate the elements
- */
-static inline void *solv_zextend(void *buf, size_t len, size_t nmemb, size_t size, size_t block)
-{
- buf = solv_extend(buf, len, nmemb, size, block);
- memset((char *)buf + len * size, 0, nmemb * size);
- return buf;
-}
-
-static inline void *solv_extend_resize(void *buf, size_t len, size_t size, size_t block)
-{
- if (len)
- buf = solv_extend_realloc(buf, len, size, block);
- return buf;
-}
-
-static inline void *solv_calloc_block(size_t len, size_t size, size_t block)
-{
- void *buf;
- if (!len)
- return 0;
- buf = solv_extend_realloc((void *)0, len, size, block);
- memset(buf, 0, ((len + block) & ~block) * size);
- return buf;
-}
-
-static inline void *solv_memdup(void *buf, size_t len)
-{
- void *newbuf;
- if (!buf)
- return 0;
- newbuf = solv_malloc(len);
- if (len)
- memcpy(newbuf, buf, len);
- return newbuf;
-}
-
-static inline void *solv_memdup2(void *buf, size_t num, size_t len)
-{
- void *newbuf;
- if (!buf)
- return 0;
- newbuf = solv_malloc2(num, len);
- if (num)
- memcpy(newbuf, buf, num * len);
- return newbuf;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBSOLV_UTIL_H */
+++ /dev/null
-FOREACH(tcdir testcases libsolv-zypptestcases)
- IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}")
- FILE(GLOB dirs "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}/[_a-zA-Z0-9]*")
- FOREACH(dir ${dirs})
- IF(IS_DIRECTORY ${dir})
- FILE(RELATIVE_PATH myname "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}" ${dir})
- ADD_TEST(${myname} ${CMAKE_CURRENT_SOURCE_DIR}/runtestcases ${CMAKE_BINARY_DIR}/tools/testsolv ${dir})
- ENDIF(IS_DIRECTORY ${dir})
- ENDFOREACH(dir)
- ENDIF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${tcdir}")
-ENDFOREACH(tcdir)
+++ /dev/null
-#!/bin/bash
-
-cmd=$1
-dir=$2
-
-if test -z "$cmd" -o -z "$dir"; then
- echo "Usage: runtestcases <cmd> <dir>";
- exit 1
-fi
-
-ex=0
-for tc in $(find $dir -name \*.t) ; do
- $cmd $tc >/dev/null
- tex=$?
- tcn="${tc#$dir/} .................................................."
- tcn="${tcn:0:50}"
- if test "$tex" -eq 0 ; then
- echo "$tcn Passed"
- elif test "$tex" -eq 77 ; then
- echo "$tcn Skipped"
- else
- echo "$tcn***Failed"
- ex=1
- fi
-done
-exit $ex
+++ /dev/null
-repo system 0 empty
-repo test 0 testtags <inline>
-#>=Pkg: X 1 1 noarch
-#>=Req: Y
-#>=Pkg: B 1 1 noarch
-#>=Prv: Y
-#>=Pkg: A 1 1 noarch
-#>=Prv: Y
-system i686 rpm system
-job install name X
-result transaction,problems <inline>
-#>install A-1-1.noarch@test
-#>install X-1-1.noarch@test
+++ /dev/null
-repo system 0 empty
-repo test 0 testtags <inline>
-#>=Pkg: X 1 1 noarch
-#>=Req: Y
-#>=Pkg: B 1 1 noarch
-#>=Prv: Y
-#>=Enh: X
-#>=Pkg: A 1 1 noarch
-#>=Prv: Y
-system i686 rpm system
-job install name X
-result transaction,problems <inline>
-#>install B-1-1.noarch@test
-#>install X-1-1.noarch@test
+++ /dev/null
-repo system 0 empty
-repo test 0 testtags <inline>
-#>=Pkg: X 1 1 noarch
-#>=Req: Y
-#>=Pkg: B 1 1 noarch
-#>=Prv: Y
-#>=Pkg: C 1 1 noarch
-#>=Prv: Y
-#>=Pkg: A 1 1 noarch
-#>=Prv: Y
-#>=Pkg: A 2 1 noarch
-system i686 rpm system
-job install name X
-result transaction,problems <inline>
-#>install B-1-1.noarch@test
-#>install X-1-1.noarch@test
+++ /dev/null
-repo system 0 empty
-repo test 0 testtags <inline>
-#>=Pkg: X 1 1 noarch
-#>=Req: Y
-#>=Sug: B
-#>=Pkg: B 1 1 noarch
-#>=Prv: Y
-#>=Pkg: A 1 1 noarch
-#>=Prv: Y
-system i686 rpm system
-job install name X
-result transaction,problems <inline>
-#>install B-1-1.noarch@test
-#>install X-1-1.noarch@test
+++ /dev/null
-repo system 0 empty
-repo test 0 testtags <inline>
-#>=Pkg: X 1 1 noarch
-#>=Req: Y
-#>=Pkg: B 1 1 noarch
-#>=Prv: Y = 2
-#>=Pkg: C 1 1 noarch
-#>=Prv: Y = 1.1
-#>=Pkg: A 1 1 noarch
-#>=Prv: Y = 1
-system i686 rpm system
-job install name X
-result transaction,problems <inline>
-#>install B-1-1.noarch@test
-#>install X-1-1.noarch@test
+++ /dev/null
-repo system 0 empty
-repo test 0 testtags <inline>
-#>=Pkg: X 1 1 noarch
-#>=Req: Y
-#>=Pkg: B 1 1 noarch
-#>=Prv: Y < 2
-#>=Pkg: C 1 1 noarch
-#>=Prv: Y <= 2
-#>=Pkg: A 1 1 noarch
-#>=Prv: Y = 1
-system i686 rpm system
-job install name X
-result transaction,problems <inline>
-#>install C-1-1.noarch@test
-#>install X-1-1.noarch@test
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Req: B1
-#>=Pkg: B1 1 1 noarch
-repo test 0 testtags <inline>
-#>=Pkg: A 1 2 noarch
-#>=Req: B1
-#>=Pkg: A 2 1 noarch
-#>=Req: B2 = 1
-#>=Pkg: B1 1 1 noarch
-#>=Pkg: B2 1 1 noarch
-system i686 rpm system
-
-# check untargeted
-job distupgrade name A [cleandeps]
-result transaction,problems <inline>
-#>erase B1-1-1.noarch@system
-#>install B2-1-1.noarch@test
-#>upgrade A-1-1.noarch@system A-2-1.noarch@test
-
-# check targeted
-nextjob
-job distupgrade name A = 2 [cleandeps]
-result transaction,problems <inline>
-#>erase B1-1-1.noarch@system
-#>install B2-1-1.noarch@test
-#>upgrade A-1-1.noarch@system A-2-1.noarch@test
-
-# check targeted to 1-2
-nextjob
-job distupgrade name A = 1-2 [cleandeps]
-result transaction,problems <inline>
-#>upgrade A-1-1.noarch@system A-1-2.noarch@test
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Req: B1
-#>=Pkg: B1 1 1 noarch
-repo test 0 testtags <inline>
-#>=Pkg: A 2 1 noarch
-#>=Req: B2 = 1
-#>=Pkg: B1 1 1 noarch
-#>=Pkg: B2 1 1 noarch
-system i686 rpm system
-job install name A = 2 [cleandeps]
-result transaction,problems <inline>
-#>erase B1-1-1.noarch@system
-#>install B2-1-1.noarch@test
-#>upgrade A-1-1.noarch@system A-2-1.noarch@test
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Req: B1
-#>=Pkg: B1 1 1 noarch
-repo test 0 testtags <inline>
-#>=Pkg: A 1 2 noarch
-#>=Req: B1
-#>=Pkg: A 2 1 noarch
-#>=Req: B2 = 1
-#>=Pkg: B1 1 1 noarch
-#>=Pkg: B2 1 1 noarch
-system i686 rpm system
-
-# check untargeted
-job update name A [cleandeps]
-result transaction,problems <inline>
-#>erase B1-1-1.noarch@system
-#>install B2-1-1.noarch@test
-#>upgrade A-1-1.noarch@system A-2-1.noarch@test
-
-# check targeted
-nextjob
-job update name A = 2 [cleandeps]
-result transaction,problems <inline>
-#>erase B1-1-1.noarch@system
-#>install B2-1-1.noarch@test
-#>upgrade A-1-1.noarch@system A-2-1.noarch@test
-
-# check targeted to 1-2
-nextjob
-job update name A = 1-2 [cleandeps]
-result transaction,problems <inline>
-#>upgrade A-1-1.noarch@system A-1-2.noarch@test
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Req: B
-#>=Pkg: B 2 1 noarch
-repo test 0 testtags <inline>
-#>=Pkg: A 2 1 noarch
-#>=Req: B = 1-1
-#>=Pkg: B 1 1 noarch
-system unset deb system
-job install name A = 2-1 [cleandeps]
-result transaction,problems <inline>
-#>problem b5abcb9c info package A-2-1.noarch requires B = 1-1, but none of the providers can be installed
-#>problem b5abcb9c solution 3b3a37c0 deljob install name A = 2-1 [cleandeps]
-#>problem b5abcb9c solution 3c170283 replace B-2-1.noarch@system B-1-1.noarch@test
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: a 1 1 i686
-#>=Pkg: b 2 1 i686
-repo available 0 testtags <inline>
-#>=Pkg: a 2 1 i586
-#>=Con: b = 1-1
-#>=Pkg: b 1 1 i586
-system i686 * system
-solverflags !dupallowarchchange allowuninstall
-job distupgrade all packages
-result transaction,problems <inline>
-#>erase b-2-1.i686@system
-#>upgrade a-1-1.i686@system a-2-1.i586@available
+++ /dev/null
-# test dup with multiversion packages
-#
-# part 1: simple update
-repo system 0 testtags <inline>
-#>=Pkg: a 1 1 i686
-repo available 0 testtags <inline>
-#>=Pkg: a 2 1 i686
-system i686 * system
-
-job multiversion name a
-job distupgrade all packages
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-
-nextjob
-
-job multiversion name a
-job distupgrade repo available
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-
-
-### same with keeporphans
-
-nextjob
-
-solverflags keeporphans
-job multiversion name a
-job distupgrade all packages
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-
-
-nextjob
-
-solverflags keeporphans
-job multiversion name a
-job distupgrade repo available
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-
-
-### same with allowuninstall
-
-nextjob
-
-solverflags allowuninstall
-job multiversion name a
-job distupgrade all packages
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-
-
-nextjob
-
-solverflags allowuninstall
-job multiversion name a
-job distupgrade repo available
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-
-
-### same with allowuninstall and keeporphans
-
-nextjob
-
-solverflags allowuninstall keeporphans
-job multiversion name a
-job distupgrade all packages
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-
-
-nextjob
-
-solverflags allowuninstall keeporphans
-job multiversion name a
-job distupgrade repo available
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-
-
-
+++ /dev/null
-# test dup with multiversion packages
-# same as with dup_multiversion1, but we can't keep the orphan
-
-#
-# part 1: simple update
-repo system 0 testtags <inline>
-#>=Pkg: a 1 1 i686
-#>=Pkg: b 1 1 i686
-repo available 0 testtags <inline>
-#>=Pkg: a 2 1 i686
-#>=Pkg: b 2 1 i686
-#>=Con: a = 1-1
-system i686 * system
-
-job multiversion name a
-job distupgrade all packages
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-#>install a-2-1.i686@available
-#>upgrade b-1-1.i686@system b-2-1.i686@available
-
-nextjob
-
-job multiversion name a
-job distupgrade repo available
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-#>install a-2-1.i686@available
-#>upgrade b-1-1.i686@system b-2-1.i686@available
-
-
-### same with keeporphans, this will result in problems as we cannot keep the orphan
-
-nextjob
-
-solverflags keeporphans
-job multiversion name a
-job distupgrade all packages
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-#>problem 4d4de423 info package b-2-1.i686 conflicts with a = 1-1 provided by a-1-1.i686
-#>problem 4d4de423 solution 2cf4745c erase a-1-1.i686@system
-#>problem 4d4de423 solution 2cf4745c replace a-1-1.i686@system a-2-1.i686@available
-#>problem 4d4de423 solution 5a433aff allow b-1-1.i686@system
-#>problem 4d4de423 solution ce4305f2 erase b-1-1.i686@system
-
-nextjob
-
-solverflags keeporphans
-job multiversion name a
-job distupgrade repo available
-result transaction,problems <inline>
-#>install a-2-1.i686@available
-#>problem 4d4de423 info package b-2-1.i686 conflicts with a = 1-1 provided by a-1-1.i686
-#>problem 4d4de423 solution 2cf4745c erase a-1-1.i686@system
-#>problem 4d4de423 solution 2cf4745c replace a-1-1.i686@system a-2-1.i686@available
-#>problem 4d4de423 solution 5a433aff allow b-1-1.i686@system
-#>problem 4d4de423 solution ce4305f2 erase b-1-1.i686@system
-
-### same with allowuninstall
-
-nextjob
-
-solverflags allowuninstall
-job multiversion name a
-job distupgrade all packages
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-#>install a-2-1.i686@available
-#>upgrade b-1-1.i686@system b-2-1.i686@available
-
-nextjob
-
-solverflags allowuninstall
-job multiversion name a
-job distupgrade repo available
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-#>install a-2-1.i686@available
-#>upgrade b-1-1.i686@system b-2-1.i686@available
-
-
-### same with allowuninstall and keeporphans
-
-nextjob
-
-solverflags allowuninstall keeporphans
-job multiversion name a
-job distupgrade all packages
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>erase b-1-1.i686@system
-#>install a-2-1.i686@available
-
-
-nextjob
-
-solverflags allowuninstall keeporphans
-job multiversion name a
-job distupgrade repo available
-# a-1-1 is treated as orphaned and stays behind
-result transaction,problems <inline>
-#>erase b-1-1.i686@system
-#>install a-2-1.i686@available
-
-
+++ /dev/null
-# test dup with multiversion packages where we cannot install the
-# target. Should give problems except for allowuninstall.
-#
-# part 1: simple update
-repo system 0 testtags <inline>
-#>=Pkg: a 1 1 i686
-repo available 0 testtags <inline>
-#>=Pkg: a 2 1 i686
-#>=Req: c
-system i686 * system
-
-job multiversion name a
-job distupgrade all packages
-result transaction,problems <inline>
-#>problem 251f1f35 info nothing provides c needed by a-2-1.i686
-#>problem 251f1f35 solution 2f2d254c allow a-1-1.i686@system
-
-nextjob
-
-job multiversion name a
-job distupgrade repo available
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-#>problem 251f1f35 info nothing provides c needed by a-2-1.i686
-#>problem 251f1f35 solution 2f2d254c allow a-1-1.i686@system
-
-### same with keeporphans
-
-nextjob
-
-solverflags keeporphans
-job multiversion name a
-job distupgrade all packages
-result transaction,problems <inline>
-#>problem 771581fd info nothing provides c needed by a-2-1.i686
-#>problem 771581fd solution 179b72ed allow a-1-1.i686@system
-#>problem 771581fd solution 2cf4745c erase a-1-1.i686@system
-
-nextjob
-
-solverflags keeporphans
-job multiversion name a
-job distupgrade repo available
-result transaction,problems <inline>
-#>problem 771581fd info nothing provides c needed by a-2-1.i686
-#>problem 771581fd solution 179b72ed allow a-1-1.i686@system
-#>problem 771581fd solution 2cf4745c erase a-1-1.i686@system
-
-### same with allowuninstall
-
-nextjob
-
-solverflags allowuninstall
-job multiversion name a
-job distupgrade all packages
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-
-
-nextjob
-
-solverflags allowuninstall
-job multiversion name a
-job distupgrade repo available
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-
-
-### same with allowuninstall and keeporphans
-
-nextjob
-
-solverflags allowuninstall keeporphans
-job multiversion name a
-job distupgrade all packages
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-
-
-nextjob
-
-solverflags allowuninstall keeporphans
-job multiversion name a
-job distupgrade repo available
-result transaction,problems <inline>
-#>erase a-1-1.i686@system
-
-
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: a 1 1 i686
-#>=Pkg: b 1 1 i686
-repo available 0 testtags <inline>
-#>=Pkg: a 2 1 i586
-#>=Pkg: b 2 1 i586
-#>=Pkg: b 2 1 i686
-system i686 * system
-solverflags !dupallowarchchange
-job distupgrade all packages
-result transaction,problems <inline>
-#>problem c43b1300 info problem with installed package a-1-1.i686
-#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-2-1.i586@available
-#>upgrade a-1-1.i686@system a-2-1.i586@available
-#>upgrade b-1-1.i686@system b-2-1.i686@available
+++ /dev/null
-=Ver: 2.0
-#
-=Pkg: CEQ2 1 1 noarch
-=Con: B = 2
-=Pkg: CEQ22 1 1 noarch
-=Con: B = 2-2
-#
-=Pkg: CLT2 1 1 noarch
-=Con: B < 2
-=Pkg: CLT22 1 1 noarch
-=Con: B < 2-2
-#
-=Pkg: CGT2 1 1 noarch
-=Con: B > 2
-=Pkg: CGT22 1 1 noarch
-=Con: B > 2-2
-#
-=Pkg: CLE2 1 1 noarch
-=Con: B <= 2
-=Pkg: CLE22 1 1 noarch
-=Con: B <= 2-2
-#
-=Pkg: CGE2 1 1 noarch
-=Con: B >= 2
-=Pkg: CGE22 1 1 noarch
-=Con: B >= 2-2
+++ /dev/null
-=Ver: 2.0
-#
-=Pkg: AEQ1 1 1 noarch
-=Prv: B = 1
-=Pkg: AEQ11 1 1 noarch
-=Prv: B = 1-1
-=Pkg: AEQ12 1 1 noarch
-=Prv: B = 1-2
-=Pkg: AEQ13 1 1 noarch
-=Prv: B = 1-3
-=Pkg: AEQ2 1 1 noarch
-=Prv: B = 2
-=Pkg: AEQ21 1 1 noarch
-=Prv: B = 2-1
-=Pkg: AEQ22 1 1 noarch
-=Prv: B = 2-2
-=Pkg: AEQ23 1 1 noarch
-=Prv: B = 2-3
-=Pkg: AEQ3 1 1 noarch
-=Prv: B = 3
-=Pkg: AEQ31 1 1 noarch
-=Prv: B = 3-1
-=Pkg: AEQ32 1 1 noarch
-=Prv: B = 3-2
-=Pkg: AEQ33 1 1 noarch
-=Prv: B = 3-3
-#
-=Pkg: ALT1 1 1 noarch
-=Prv: B < 1
-=Pkg: ALT11 1 1 noarch
-=Prv: B < 1-1
-=Pkg: ALT12 1 1 noarch
-=Prv: B < 1-2
-=Pkg: ALT13 1 1 noarch
-=Prv: B < 1-3
-=Pkg: ALT2 1 1 noarch
-=Prv: B < 2
-=Pkg: ALT21 1 1 noarch
-=Prv: B < 2-1
-=Pkg: ALT22 1 1 noarch
-=Prv: B < 2-2
-=Pkg: ALT23 1 1 noarch
-=Prv: B < 2-3
-=Pkg: ALT3 1 1 noarch
-=Prv: B < 3
-=Pkg: ALT31 1 1 noarch
-=Prv: B < 3-1
-=Pkg: ALT32 1 1 noarch
-=Prv: B < 3-2
-=Pkg: ALT33 1 1 noarch
-=Prv: B < 3-3
-#
-=Pkg: AGT1 1 1 noarch
-=Prv: B > 1
-=Pkg: AGT11 1 1 noarch
-=Prv: B > 1-1
-=Pkg: AGT12 1 1 noarch
-=Prv: B > 1-2
-=Pkg: AGT13 1 1 noarch
-=Prv: B > 1-3
-=Pkg: AGT2 1 1 noarch
-=Prv: B > 2
-=Pkg: AGT21 1 1 noarch
-=Prv: B > 2-1
-=Pkg: AGT22 1 1 noarch
-=Prv: B > 2-2
-=Pkg: AGT23 1 1 noarch
-=Prv: B > 2-3
-=Pkg: AGT3 1 1 noarch
-=Prv: B > 3
-=Pkg: AGT31 1 1 noarch
-=Prv: B > 3-1
-=Pkg: AGT32 1 1 noarch
-=Prv: B > 3-2
-=Pkg: AGT33 1 1 noarch
-=Prv: B > 3-3
-#
-=Pkg: ALE1 1 1 noarch
-=Prv: B <= 1
-=Pkg: ALE11 1 1 noarch
-=Prv: B <= 1-1
-=Pkg: ALE12 1 1 noarch
-=Prv: B <= 1-2
-=Pkg: ALE13 1 1 noarch
-=Prv: B <= 1-3
-=Pkg: ALE2 1 1 noarch
-=Prv: B <= 2
-=Pkg: ALE21 1 1 noarch
-=Prv: B <= 2-1
-=Pkg: ALE22 1 1 noarch
-=Prv: B <= 2-2
-=Pkg: ALE23 1 1 noarch
-=Prv: B <= 2-3
-=Pkg: ALE3 1 1 noarch
-=Prv: B <= 3
-=Pkg: ALE31 1 1 noarch
-=Prv: B <= 3-1
-=Pkg: ALE32 1 1 noarch
-=Prv: B <= 3-2
-=Pkg: ALE33 1 1 noarch
-=Prv: B <= 3-3
-#
-=Pkg: AGE1 1 1 noarch
-=Prv: B >= 1
-=Pkg: AGE11 1 1 noarch
-=Prv: B >= 1-1
-=Pkg: AGE12 1 1 noarch
-=Prv: B >= 1-2
-=Pkg: AGE13 1 1 noarch
-=Prv: B >= 1-3
-=Pkg: AGE2 1 1 noarch
-=Prv: B >= 2
-=Pkg: AGE21 1 1 noarch
-=Prv: B >= 2-1
-=Pkg: AGE22 1 1 noarch
-=Prv: B >= 2-2
-=Pkg: AGE23 1 1 noarch
-=Prv: B >= 2-3
-=Pkg: AGE3 1 1 noarch
-=Prv: B >= 3
-=Pkg: AGE31 1 1 noarch
-=Prv: B >= 3-1
-=Pkg: AGE32 1 1 noarch
-=Prv: B >= 3-2
-=Pkg: AGE33 1 1 noarch
-=Prv: B >= 3-3
-#
+++ /dev/null
-#
-# these tests check all dependency match combinations,
-# both with release present and missing
-#
-repo system 0 testtags system.repo
-repo c 0 testtags conflicts.repo
-system i686 rpm system
-solverflags allowuninstall
-job install name CEQ2
-result transaction,problems <inline>
-#>erase AEQ2-1-1.noarch@system
-#>erase AEQ21-1-1.noarch@system
-#>erase AEQ22-1-1.noarch@system
-#>erase AEQ23-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGE22-1-1.noarch@system
-#>erase AGE23-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase AGT22-1-1.noarch@system
-#>erase AGT23-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE21-1-1.noarch@system
-#>erase ALE22-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT21-1-1.noarch@system
-#>erase ALT22-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CEQ2-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CEQ22
-result transaction,problems <inline>
-#>erase AEQ2-1-1.noarch@system
-#>erase AEQ22-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGE22-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE22-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CEQ22-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CLT2
-result transaction,problems <inline>
-#>erase AEQ1-1-1.noarch@system
-#>erase AEQ11-1-1.noarch@system
-#>erase AEQ12-1-1.noarch@system
-#>erase AEQ13-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase ALE1-1-1.noarch@system
-#>erase ALE11-1-1.noarch@system
-#>erase ALE12-1-1.noarch@system
-#>erase ALE13-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE21-1-1.noarch@system
-#>erase ALE22-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT1-1-1.noarch@system
-#>erase ALT11-1-1.noarch@system
-#>erase ALT12-1-1.noarch@system
-#>erase ALT13-1-1.noarch@system
-#>erase ALT2-1-1.noarch@system
-#>erase ALT21-1-1.noarch@system
-#>erase ALT22-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CLT2-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CLT22
-result transaction,problems <inline>
-#>erase AEQ1-1-1.noarch@system
-#>erase AEQ11-1-1.noarch@system
-#>erase AEQ12-1-1.noarch@system
-#>erase AEQ13-1-1.noarch@system
-#>erase AEQ2-1-1.noarch@system
-#>erase AEQ21-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase ALE1-1-1.noarch@system
-#>erase ALE11-1-1.noarch@system
-#>erase ALE12-1-1.noarch@system
-#>erase ALE13-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE21-1-1.noarch@system
-#>erase ALE22-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT1-1-1.noarch@system
-#>erase ALT11-1-1.noarch@system
-#>erase ALT12-1-1.noarch@system
-#>erase ALT13-1-1.noarch@system
-#>erase ALT2-1-1.noarch@system
-#>erase ALT21-1-1.noarch@system
-#>erase ALT22-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CLT22-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CGT2
-result transaction,problems <inline>
-#>erase AEQ3-1-1.noarch@system
-#>erase AEQ31-1-1.noarch@system
-#>erase AEQ32-1-1.noarch@system
-#>erase AEQ33-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGE22-1-1.noarch@system
-#>erase AGE23-1-1.noarch@system
-#>erase AGE3-1-1.noarch@system
-#>erase AGE31-1-1.noarch@system
-#>erase AGE32-1-1.noarch@system
-#>erase AGE33-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT2-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase AGT22-1-1.noarch@system
-#>erase AGT23-1-1.noarch@system
-#>erase AGT3-1-1.noarch@system
-#>erase AGT31-1-1.noarch@system
-#>erase AGT32-1-1.noarch@system
-#>erase AGT33-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CGT2-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CGT22
-result transaction,problems <inline>
-#>erase AEQ2-1-1.noarch@system
-#>erase AEQ23-1-1.noarch@system
-#>erase AEQ3-1-1.noarch@system
-#>erase AEQ31-1-1.noarch@system
-#>erase AEQ32-1-1.noarch@system
-#>erase AEQ33-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGE22-1-1.noarch@system
-#>erase AGE23-1-1.noarch@system
-#>erase AGE3-1-1.noarch@system
-#>erase AGE31-1-1.noarch@system
-#>erase AGE32-1-1.noarch@system
-#>erase AGE33-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT2-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase AGT22-1-1.noarch@system
-#>erase AGT23-1-1.noarch@system
-#>erase AGT3-1-1.noarch@system
-#>erase AGT31-1-1.noarch@system
-#>erase AGT32-1-1.noarch@system
-#>erase AGT33-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CGT22-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CLE2
-result transaction,problems <inline>
-#>erase AEQ1-1-1.noarch@system
-#>erase AEQ11-1-1.noarch@system
-#>erase AEQ12-1-1.noarch@system
-#>erase AEQ13-1-1.noarch@system
-#>erase AEQ2-1-1.noarch@system
-#>erase AEQ21-1-1.noarch@system
-#>erase AEQ22-1-1.noarch@system
-#>erase AEQ23-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGE22-1-1.noarch@system
-#>erase AGE23-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase AGT22-1-1.noarch@system
-#>erase AGT23-1-1.noarch@system
-#>erase ALE1-1-1.noarch@system
-#>erase ALE11-1-1.noarch@system
-#>erase ALE12-1-1.noarch@system
-#>erase ALE13-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE21-1-1.noarch@system
-#>erase ALE22-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT1-1-1.noarch@system
-#>erase ALT11-1-1.noarch@system
-#>erase ALT12-1-1.noarch@system
-#>erase ALT13-1-1.noarch@system
-#>erase ALT2-1-1.noarch@system
-#>erase ALT21-1-1.noarch@system
-#>erase ALT22-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CLE2-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CLE22
-result transaction,problems <inline>
-#>erase AEQ1-1-1.noarch@system
-#>erase AEQ11-1-1.noarch@system
-#>erase AEQ12-1-1.noarch@system
-#>erase AEQ13-1-1.noarch@system
-#>erase AEQ2-1-1.noarch@system
-#>erase AEQ21-1-1.noarch@system
-#>erase AEQ22-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGE22-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase ALE1-1-1.noarch@system
-#>erase ALE11-1-1.noarch@system
-#>erase ALE12-1-1.noarch@system
-#>erase ALE13-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE21-1-1.noarch@system
-#>erase ALE22-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT1-1-1.noarch@system
-#>erase ALT11-1-1.noarch@system
-#>erase ALT12-1-1.noarch@system
-#>erase ALT13-1-1.noarch@system
-#>erase ALT2-1-1.noarch@system
-#>erase ALT21-1-1.noarch@system
-#>erase ALT22-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CLE22-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CGE2
-result transaction,problems <inline>
-#>erase AEQ2-1-1.noarch@system
-#>erase AEQ21-1-1.noarch@system
-#>erase AEQ22-1-1.noarch@system
-#>erase AEQ23-1-1.noarch@system
-#>erase AEQ3-1-1.noarch@system
-#>erase AEQ31-1-1.noarch@system
-#>erase AEQ32-1-1.noarch@system
-#>erase AEQ33-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGE22-1-1.noarch@system
-#>erase AGE23-1-1.noarch@system
-#>erase AGE3-1-1.noarch@system
-#>erase AGE31-1-1.noarch@system
-#>erase AGE32-1-1.noarch@system
-#>erase AGE33-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT2-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase AGT22-1-1.noarch@system
-#>erase AGT23-1-1.noarch@system
-#>erase AGT3-1-1.noarch@system
-#>erase AGT31-1-1.noarch@system
-#>erase AGT32-1-1.noarch@system
-#>erase AGT33-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE21-1-1.noarch@system
-#>erase ALE22-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT21-1-1.noarch@system
-#>erase ALT22-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CGE2-1-1.noarch@c
-nextjob
-solverflags allowuninstall
-job install name CGE22
-result transaction,problems <inline>
-#>erase AEQ2-1-1.noarch@system
-#>erase AEQ22-1-1.noarch@system
-#>erase AEQ23-1-1.noarch@system
-#>erase AEQ3-1-1.noarch@system
-#>erase AEQ31-1-1.noarch@system
-#>erase AEQ32-1-1.noarch@system
-#>erase AEQ33-1-1.noarch@system
-#>erase AGE1-1-1.noarch@system
-#>erase AGE11-1-1.noarch@system
-#>erase AGE12-1-1.noarch@system
-#>erase AGE13-1-1.noarch@system
-#>erase AGE2-1-1.noarch@system
-#>erase AGE21-1-1.noarch@system
-#>erase AGE22-1-1.noarch@system
-#>erase AGE23-1-1.noarch@system
-#>erase AGE3-1-1.noarch@system
-#>erase AGE31-1-1.noarch@system
-#>erase AGE32-1-1.noarch@system
-#>erase AGE33-1-1.noarch@system
-#>erase AGT1-1-1.noarch@system
-#>erase AGT11-1-1.noarch@system
-#>erase AGT12-1-1.noarch@system
-#>erase AGT13-1-1.noarch@system
-#>erase AGT2-1-1.noarch@system
-#>erase AGT21-1-1.noarch@system
-#>erase AGT22-1-1.noarch@system
-#>erase AGT23-1-1.noarch@system
-#>erase AGT3-1-1.noarch@system
-#>erase AGT31-1-1.noarch@system
-#>erase AGT32-1-1.noarch@system
-#>erase AGT33-1-1.noarch@system
-#>erase ALE2-1-1.noarch@system
-#>erase ALE22-1-1.noarch@system
-#>erase ALE23-1-1.noarch@system
-#>erase ALE3-1-1.noarch@system
-#>erase ALE31-1-1.noarch@system
-#>erase ALE32-1-1.noarch@system
-#>erase ALE33-1-1.noarch@system
-#>erase ALT23-1-1.noarch@system
-#>erase ALT3-1-1.noarch@system
-#>erase ALT31-1-1.noarch@system
-#>erase ALT32-1-1.noarch@system
-#>erase ALT33-1-1.noarch@system
-#>install CGE22-1-1.noarch@c
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Vnd: foo
-#>=Pkg: D 1 1 noarch
-#>=Vnd: foo
-#>=Con: A = 3-1
-repo available 0 testtags <inline>
-#>=Pkg: A 2 1 noarch
-#>=Vnd: foo
-#>=Pkg: A 3 1 noarch
-#>=Vnd: bar
-system i686 rpm system
-
-job distupgrade name A [forcebest]
-result transaction,problems <inline>
-#>erase D-1-1.noarch@system
-#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
-#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
-#>problem 1210fdfb solution d85f7c4e allow A-2-1.noarch@available
-#>upgrade A-1-1.noarch@system A-3-1.noarch@available
-
-# test if bestobeypolicy is a noop for dup jobs
-nextjob
-solverflags bestobeypolicy
-job distupgrade name A [forcebest]
-result transaction,problems <inline>
-#>erase D-1-1.noarch@system
-#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
-#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
-#>problem 1210fdfb solution d85f7c4e allow A-2-1.noarch@available
-#>upgrade A-1-1.noarch@system A-3-1.noarch@available
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: D 1 1 noarch
-#>=Vnd: foo
-#>=Con: A = 3-1
-repo available 0 testtags <inline>
-#>=Pkg: A 2 1 noarch
-#>=Vnd: foo
-#>=Pkg: A 3 1 noarch
-#>=Vnd: bar
-system i686 rpm system
-
-job install name A [forcebest]
-result transaction,problems <inline>
-#>erase D-1-1.noarch@system
-#>install A-3-1.noarch@available
-#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
-#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
-#>problem 1210fdfb solution d85f7c4e deljob install name A [forcebest]
-
-# currently bestobeypolicy is a noop for install jobs
-nextjob
-solverflags bestobeypolicy
-job install name A [forcebest]
-result transaction,problems <inline>
-#>erase D-1-1.noarch@system
-#>install A-3-1.noarch@available
-#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
-#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
-#>problem 1210fdfb solution d85f7c4e deljob install name A [forcebest]
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Vnd: foo
-#>=Pkg: D 1 1 noarch
-#>=Vnd: foo
-#>=Con: A = 3-1
-repo available 0 testtags <inline>
-#>=Pkg: A 2 1 noarch
-#>=Vnd: foo
-#>=Pkg: A 3 1 noarch
-#>=Vnd: bar
-system i686 rpm system
-
-job update name A [forcebest]
-result transaction,problems <inline>
-#>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
-#>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
-#>problem 1210fdfb solution 0d75a914 replace A-1-1.noarch@system A-3-1.noarch@available
-#>problem 1210fdfb solution d85f7c4e allow A-2-1.noarch@available
-#>upgrade A-1-1.noarch@system A-2-1.noarch@available
-
-nextjob
-solverflags bestobeypolicy
-job update name A [forcebest]
-result transaction,problems <inline>
-#>upgrade A-1-1.noarch@system A-2-1.noarch@available
+++ /dev/null
-repo system 0 empty
-repo test 0 testtags <inline>
-#>=Pkg: A 1 1 i586
-#>=Prv: A(x32)
-#>=Pkg: A 1 1 x86_64
-#>=Prv: A(x64)
-system x86_64 rpm system
-poolflags implicitobsoleteusescolors
-job install provides A(x32)
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 i586
-#>=Pkg: A 1 1 x86_64
-repo test 0 testtags <inline>
-#>=Pkg: A 2 1 i586
-#>=Pkg: A 2 1 x86_64
-#>=Pkg: A 3 1 i586
-#>=Pkg: A 4 1 x86_64
-system x86_64 rpm system
-poolflags implicitobsoleteusescolors
-job update all packages
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Pkg: B 1 1 noarch
-repo test 0 testtags <inline>
-#>=Pkg: A 2 1 noarch
-#>=Obs: B
-system i686 rpm system
-
-solverflags keepexplicitobsoletes
-job multiversion name A
-job install name A = 2
-result transaction,problems <inline>
-#>erase B-1-1.noarch@system
-#>install A-2-1.noarch@test
-
-nextjob
-solverflags keepexplicitobsoletes
-poolflags noobsoletesmultiversion
-job multiversion name A
-job install name A = 2
-result transaction,problems <inline>
-#>erase B-1-1.noarch@system
-#>install A-2-1.noarch@test
-
-nextjob
-poolflags !noobsoletesmultiversion
-job multiversion name A
-job install name A = 2
-result transaction,problems <inline>
-#>install A-2-1.noarch@test
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Ver: 2
-#>=Pkg: B 1 1 noarch
-#>=Prv: locale(en)
-#>=Pkg: C 1 1 noarch
-repo test 0 testtags <inline>
-#>=Ver: 2
-#>=Pkg: A 1 1 noarch
-#>=Prv: locale(de)
-#>=Pkg: C-de 1 1 noarch
-#>=Prv: locale(C:de)
-#>=Pkg: C-en 1 1 noarch
-#>=Prv: locale(C:en)
-system i686 rpm system
-
-# first test an empty job
-namespace namespace:language(de) @SYSTEM
-result transaction,problems <inline>
-
-# then test addalreadyrecommended
-nextjob
-namespace namespace:language(de) @SYSTEM
-solverflags addalreadyrecommended
-result transaction,problems <inline>
-#>install A-1-1.noarch@test
-#>install C-de-1-1.noarch@test
-
-nextjob
-namespace namespace:language(de) @SYSTEM
-job install provides namespace:language(de)
-result transaction,problems <inline>
-#>install A-1-1.noarch@test
-#>install C-de-1-1.noarch@test
-
-nextjob
-namespace namespace:language(de) @SYSTEM
-job erase provides namespace:language(en) [cleandeps]
-result transaction,problems <inline>
-#>erase B-1-1.noarch@system
-
-nextjob
-namespace namespace:language(de) @SYSTEM
-job install provides namespace:language(<NULL>)
-result transaction,problems <inline>
-#>install A-1-1.noarch@test
-#>install C-de-1-1.noarch@test
-
-nextjob
-namespace namespace:language(de) @SYSTEM
-job erase provides namespace:language(<NULL>) [cleandeps]
-result transaction,problems <inline>
-#>erase B-1-1.noarch@system
-
-nextjob
-namespace namespace:language(de) @SYSTEM
-job install provides namespace:language(<NULL>)
-job erase provides namespace:language(<NULL>) [cleandeps]
-result transaction,problems <inline>
-#>erase B-1-1.noarch@system
-#>install A-1-1.noarch@test
-#>install C-de-1-1.noarch@test
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 x86_64
-#>=Prv: AA
-#>=Pkg: B 1 1 x86_64
-#>=Prv: AA
-system x86_64 * system
-job erase provides AA [weak]
-job install pkg B-1-1.x86_64@system
-result transaction,problems <inline>
+++ /dev/null
-#
-# testcase to check enabling/disabling of learnt rules
-#
-repo system 0 testtags <inline>
-#>=Ver: 2.0
-#>=Pkg: A 1.0 1 noarch
-#>=Req: D
-#>=Prv: A = 1.0-1
-#>=Con: C
-#>=Pkg: C 1.0 1 noarch
-#>=Prv: foo
-#>=Prv: C = 1.0-1
-#>=Con: D
-#>=Pkg: D 1.0 1 noarch
-#>=Prv: D = 1.0-1
-#>=Pkg: A2 1.0 1 noarch
-#>=Req: D2
-#>=Prv: A2 = 1.0-1
-#>=Con: C2
-#>=Pkg: C2 1.0 1 noarch
-#>=Prv: foo
-#>=Prv: C2 = 1.0-1
-#>=Con: D2
-#>=Pkg: D2 1.0 1 noarch
-#>=Prv: D2 = 1.0-1
-repo test 0 testtags <inline>
-#>=Ver: 2.0
-#>=Pkg: C 2.0 1 noarch
-#>=Prv: C = 2.0-1
-#>=Pkg: A 2.0 1 noarch
-#>=Prv: A = 2.0-1
-#>=Pkg: D 2.0 1 noarch
-#>=Prv: D = 2.0-1
-#>=Pkg: C2 2.0 1 noarch
-#>=Prv: C2 = 2.0-1
-#>=Pkg: A2 2.0 1 noarch
-#>=Prv: A2 = 2.0-1
-#>=Pkg: D2 2.0 1 noarch
-#>=Prv: D2 = 2.0-1
-#>=Pkg: E 2.0 1 noarch
-#>=Req: foo
-#>=Prv: E = 2.0-1
-system unset * system
-job install provides E
-job verify all packages
-result transaction,problems <inline>
-#>erase D-1.0-1.noarch@system
-#>erase D2-1.0-1.noarch@system
-#>problem a3755a16 info package E-2.0-1.noarch requires foo, but none of the providers can be installed
-#>problem a3755a16 solution 6d40bce1 deljob install provides E
-#>problem a3755a16 solution c06ed43e erase D-1.0-1.noarch@system
-#>problem a3755a16 solution c8a04f77 erase D2-1.0-1.noarch@system
-#>upgrade A-1.0-1.noarch@system A-2.0-1.noarch@test
-#>upgrade A2-1.0-1.noarch@system A2-2.0-1.noarch@test
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Pkg: D 1 1 noarch
-#>=Pkg: Z 1 1 noarch
-#>=Con: D = 2-1
-repo available 0 testtags <inline>
-#>=Pkg: A 2 1 noarch
-#>=Pkg: B 1 0 noarch
-#>=Obs: A
-#>=Pkg: C 1 0 noarch
-#>=Obs: A = 1-1
-#>=Pkg: D 2 1 noarch
-#>=Pkg: D 3 1 noarch
-system unset * system
-
-# first check untargeted
-job distupgrade name A = 1-1
-result transaction,problems <inline>
-#>erase A-1-1.noarch@system B-1-0.noarch@available
-#>install B-1-0.noarch@available
-
-# then targeted to A-2-1
-nextjob
-job distupgrade name A = 2-1
-result transaction,problems <inline>
-#>upgrade A-1-1.noarch@system A-2-1.noarch@available
-
-# then targeted to B
-nextjob
-job distupgrade name B
-result transaction,problems <inline>
-#>erase A-1-1.noarch@system B-1-0.noarch@available
-#>install B-1-0.noarch@available
-
-# first check forced to targeted
-nextjob
-job distupgrade name A = 1-1 [targeted]
-result transaction,problems <inline>
-
-# second check forced to untargeted
-nextjob
-solverflags noautotarget
-job distupgrade name A = 2-1
-result transaction,problems <inline>
-
-# then targeted to D
-nextjob
-job distupgrade name D
-result transaction,problems <inline>
-#>upgrade D-1-1.noarch@system D-3-1.noarch@available
-
-# then targeted to D-2-1 (should not go to D-3-1)
-nextjob
-job distupgrade name D = 2-1
-result transaction,problems <inline>
-#>problem 840e2c39 info package Z-1-1.noarch conflicts with D = 2-1 provided by D-2-1.noarch
-#>problem 840e2c39 solution 3158736f erase Z-1-1.noarch@system
-#>problem 840e2c39 solution 42076df5 erase D-1-1.noarch@system
-#>problem 840e2c39 solution cdacbabe allow D-3-1.noarch@available
-#>upgrade D-1-1.noarch@system D-3-1.noarch@available
-
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Pkg: A 1 1 noarch
-#>=Pkg: D 1 1 noarch
-#>=Pkg: Z 1 1 noarch
-#>=Con: D = 2-1
-repo available 0 testtags <inline>
-#>=Pkg: A 2 1 noarch
-#>=Pkg: B 1 0 noarch
-#>=Obs: A
-#>=Pkg: C 1 0 noarch
-#>=Obs: A = 1-1
-#>=Pkg: D 2 1 noarch
-#>=Pkg: D 3 1 noarch
-system unset * system
-
-# first check untargeted
-job update name A = 1-1
-result transaction,problems <inline>
-#>erase A-1-1.noarch@system B-1-0.noarch@available
-#>install B-1-0.noarch@available
-
-# then targeted to A-2-1
-nextjob
-job update name A = 2-1
-result transaction,problems <inline>
-#>upgrade A-1-1.noarch@system A-2-1.noarch@available
-
-# then targeted to B
-nextjob
-job update name B
-result transaction,problems <inline>
-#>erase A-1-1.noarch@system B-1-0.noarch@available
-#>install B-1-0.noarch@available
-
-# first check forced to targeted
-nextjob
-job update name A = 1-1 [targeted]
-result transaction,problems <inline>
-
-# second check forced to untargeted
-nextjob
-solverflags noautotarget
-job distupgrade name A = 2-1
-result transaction,problems <inline>
-
-# then targeted to D
-nextjob
-job update name D
-result transaction,problems <inline>
-#>upgrade D-1-1.noarch@system D-3-1.noarch@available
-
-# then targeted to D-2-1 (should not go to D-3-1)
-nextjob
-job update name D = 2-1
-result transaction,problems <inline>
-
+++ /dev/null
-# testcase for testcase_str2dep and testcase_dep2str
-
-#
-# first test literal escaping
-#
-genid dep <NULL>
-result genid <inline>
-#>genid 1: genid null
-#>genid dep <NULL>
-nextjob
-
-genid dep \00
-result genid <inline>
-#>genid 1: genid lit
-#>genid dep \00
-nextjob
-
-genid dep \21\20\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f\3a\3b\3c\3d\3e\3f\40\5b\5c\5d\5e\5f\60\7b\7c\7d\7e
-result genid <inline>
-#>genid 1: genid lit ! "#$%&'()*+,-./:;<=>?@[\]^_`{|}~
-#>genid dep \21\20"#$%&'\28\29*+,-./:;<=>?@[\5c]^_`{|}~
-# make vim happy again: '
-nextjob
-
-genid dep foo(bar)
-result genid <inline>
-#>genid 1: genid lit foo(bar)
-#>genid dep foo(bar)
-nextjob
-
-genid dep foo()bar\29
-result genid <inline>
-#>genid 1: genid lit foo()bar)
-#>genid dep foo\28\29bar\29
-nextjob
-
-#
-# test namespace hack
-#
-genid dep namespace:foo(bar)
-result genid <inline>
-#>genid 1: genid lit namespace:foo
-#>genid 2: genid lit bar
-#>genid 3: genid op <NAMESPACE>
-#>genid dep namespace:foo(bar)
-nextjob
-genid lit namespace:foo(bar)
-result genid <inline>
-#>genid 1: genid lit namespace:foo(bar)
-#>genid dep namespace\3afoo\28bar\29
-nextjob
-
-#
-# test :any hack
-#
-genid dep foo:any
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit any
-#>genid 3: genid op <MULTIARCH>
-#>genid dep foo:any
-nextjob
-genid lit foo:any
-result genid <inline>
-#>genid 1: genid lit foo:any
-#>genid dep foo\3aany
-nextjob
-
-#
-# test simple ops
-#
-genid dep foo < 1-1
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit 1-1
-#>genid 3: genid op <
-#>genid dep foo < 1-1
-nextjob
-
-genid dep foo = 1-1
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit 1-1
-#>genid 3: genid op =
-#>genid dep foo = 1-1
-nextjob
-
-genid dep foo > 1-1
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit 1-1
-#>genid 3: genid op >
-#>genid dep foo > 1-1
-nextjob
-
-genid dep foo >= 1-1
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit 1-1
-#>genid 3: genid op >=
-#>genid dep foo >= 1-1
-nextjob
-
-genid dep foo <= 1-1
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit 1-1
-#>genid 3: genid op <=
-#>genid dep foo <= 1-1
-nextjob
-
-# test arch op
-genid dep foo . i586
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit i586
-#>genid 3: genid op .
-#>genid dep foo . i586
-nextjob
-
-# test haiku compat dep
-genid dep foo = 2-1 compat >= 1-1
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit 2-1
-#>genid 3: genid lit 1-1
-#>genid 4: genid op compat >=
-#>genid 5: genid op =
-#>genid dep foo = 2-1 compat >= 1-1
-nextjob
-
-#
-# test complex (aka rich) deps
-#
-
-genid dep foo & bar
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit bar
-#>genid 3: genid op &
-#>genid dep foo & bar
-nextjob
-
-genid dep foo & bar & baz
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit bar
-#>genid 3: genid lit baz
-#>genid 4: genid op &
-#>genid 5: genid op &
-#>genid dep foo & bar & baz
-nextjob
-
-genid dep foo & bar | baz
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit bar
-#>genid 3: genid lit baz
-#>genid 4: genid op |
-#>genid 5: genid op &
-#>genid dep foo & (bar | baz)
-nextjob
-
-genid dep (foo & bar) | baz
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit bar
-#>genid 3: genid op &
-#>genid 4: genid lit baz
-#>genid 5: genid op |
-#>genid dep (foo & bar) | baz
-nextjob
-
-genid dep (foo & bar > 2) | baz
-result genid <inline>
-#>genid 1: genid lit foo
-#>genid 2: genid lit bar
-#>genid 3: genid lit 2
-#>genid 4: genid op >
-#>genid 5: genid op &
-#>genid 6: genid lit baz
-#>genid 7: genid op |
-#>genid dep (foo & bar > 2) | baz
-nextjob
-
+++ /dev/null
-repo system 0 testtags <inline>
-#>=Ver: 2.0
-#>=Pkg: c 27 1 x86_64
-repo available 0 testtags <inline>
-#>=Ver: 2.0
-#>=Pkg: d 28 1 x86_64
-#>=Obs: c
-#>=Pkg: e 28 1 x86_64
-#>=Obs: c
-
-system x86_64 rpm system
-
-job update all packages
-result transaction,problems <inline>
-#>erase c-27-1.x86_64@system d-28-1.x86_64@available
-#>install d-28-1.x86_64@available
-
-nextjob
-solverflags yumobsoletes
-job update all packages
-result transaction,problems <inline>
-#>erase c-27-1.x86_64@system d-28-1.x86_64@available
-#>install d-28-1.x86_64@available
-#>install e-28-1.x86_64@available
+++ /dev/null
-#
-# CMakeLists.txt for tools
-#
-
-ADD_LIBRARY (toolstuff STATIC common_write.c)
-
-SET (tools_list mergesolv dumpsolv installcheck testsolv)
-
-IF (ENABLE_RPMDB)
-ADD_EXECUTABLE (rpmdb2solv rpmdb2solv.c)
-TARGET_LINK_LIBRARIES (rpmdb2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-ADD_EXECUTABLE (rpms2solv rpms2solv.c)
-TARGET_LINK_LIBRARIES (rpms2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-ADD_EXECUTABLE (findfileconflicts findfileconflicts.c)
-TARGET_LINK_LIBRARIES (findfileconflicts libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} rpmdb2solv rpms2solv)
-ENDIF (ENABLE_RPMDB)
-
-IF (ENABLE_RPMMD)
-ADD_EXECUTABLE (repomdxml2solv repomdxml2solv.c)
-TARGET_LINK_LIBRARIES (repomdxml2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-ADD_EXECUTABLE (rpmmd2solv rpmmd2solv.c)
-TARGET_LINK_LIBRARIES (rpmmd2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-ADD_EXECUTABLE (updateinfoxml2solv updateinfoxml2solv.c)
-TARGET_LINK_LIBRARIES (updateinfoxml2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-ADD_EXECUTABLE (deltainfoxml2solv deltainfoxml2solv.c)
-TARGET_LINK_LIBRARIES (deltainfoxml2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} repomdxml2solv rpmmd2solv updateinfoxml2solv deltainfoxml2solv)
-ENDIF (ENABLE_RPMMD)
-
-IF (ENABLE_HELIXREPO)
-ADD_EXECUTABLE (helix2solv helix2solv.c)
-TARGET_LINK_LIBRARIES (helix2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} helix2solv)
-ENDIF (ENABLE_HELIXREPO)
-
-IF (ENABLE_SUSEREPO)
-ADD_EXECUTABLE (susetags2solv susetags2solv.c)
-TARGET_LINK_LIBRARIES (susetags2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} susetags2solv)
-ENDIF (ENABLE_SUSEREPO)
-
-IF (ENABLE_COMPS)
-ADD_EXECUTABLE (comps2solv comps2solv.c)
-TARGET_LINK_LIBRARIES (comps2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} comps2solv)
-ENDIF (ENABLE_COMPS)
-
-IF (ENABLE_DEBIAN)
-ADD_EXECUTABLE (deb2solv deb2solv.c)
-TARGET_LINK_LIBRARIES (deb2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} deb2solv)
-ENDIF (ENABLE_DEBIAN)
-
-IF (ENABLE_MDKREPO)
-ADD_EXECUTABLE (mdk2solv mdk2solv.c)
-TARGET_LINK_LIBRARIES (mdk2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} mdk2solv)
-ENDIF (ENABLE_MDKREPO)
-
-IF (ENABLE_ARCHREPO)
-ADD_EXECUTABLE (archpkgs2solv archpkgs2solv.c)
-TARGET_LINK_LIBRARIES (archpkgs2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-ADD_EXECUTABLE (archrepo2solv archrepo2solv.c)
-TARGET_LINK_LIBRARIES (archrepo2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} archpkgs2solv archrepo2solv)
-ENDIF (ENABLE_ARCHREPO)
-
-IF (ENABLE_CUDFREPO)
-ADD_EXECUTABLE (cudftest cudftest.c)
-TARGET_LINK_LIBRARIES (cudftest libsolvext libsolv ${SYSTEM_LIBRARIES})
-ENDIF (ENABLE_CUDFREPO)
-
-ADD_EXECUTABLE (installcheck installcheck.c)
-TARGET_LINK_LIBRARIES (installcheck libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-IF (SUSE)
-ADD_EXECUTABLE (patchcheck patchcheck.c)
-TARGET_LINK_LIBRARIES (patchcheck libsolvext libsolv ${SYSTEM_LIBRARIES})
-ENDIF (SUSE)
-
-IF (ENABLE_APPDATA)
-ADD_EXECUTABLE (appdata2solv appdata2solv.c)
-TARGET_LINK_LIBRARIES (appdata2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-SET (tools_list ${tools_list} appdata2solv)
-ENDIF (ENABLE_APPDATA)
-
-ADD_EXECUTABLE (dumpsolv dumpsolv.c )
-TARGET_LINK_LIBRARIES (dumpsolv libsolv)
-
-ADD_EXECUTABLE (mergesolv mergesolv.c )
-TARGET_LINK_LIBRARIES (mergesolv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-ADD_EXECUTABLE (testsolv testsolv.c)
-TARGET_LINK_LIBRARIES (testsolv libsolvext libsolv ${SYSTEM_LIBRARIES})
-
-INSTALL (TARGETS ${tools_list} DESTINATION ${BIN_INSTALL_DIR})
-
-INSTALL (PROGRAMS repo2solv.sh DESTINATION ${BIN_INSTALL_DIR})
+++ /dev/null
-/*
- * Copyright (c) 2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * appdata2solv.c
- *
- * parse AppStream appdata type xml and write out .solv file
- *
- * reads from stdin
- * writes to stdout
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_appdata.h"
-#include "common_write.h"
-
-int
-main(int argc, char **argv)
-{
- Pool *pool = pool_create();
- Repo *repo;
- int c;
- const char *appdatadir = 0;
- const char *root = 0;
-
- while ((c = getopt(argc, argv, "hd:r:")) >= 0)
- {
- switch (c)
- {
- case 'd':
- appdatadir = optarg;
- break;
- case 'r':
- root = optarg;
- break;
- default:
- fprintf(stderr, "usage: appdata2solv [-d appdatadir]");
- exit(c == 'h' ? 0 : 1);
- }
- }
-
- if (root)
- pool_set_rootdir(pool, root);
-
- repo = repo_create(pool, "<stdin>");
- if (!appdatadir)
- {
- if (repo_add_appdata(repo, stdin, 0))
- {
- fprintf(stderr, "appdata2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
- else
- {
- if (repo_add_appdata_dir(repo, appdatadir, REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "appdata2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
- tool_write(repo, 0, 0);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * archpkgs2solv - create a solv file from multiple arch packages
- *
- */
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "util.h"
-#include "pool.h"
-#include "repo.h"
-#include "repo_arch.h"
-#include "repo_solv.h"
-#include "common_write.h"
-
-static char *
-fgets0(char *s, int size, FILE *stream)
-{
- char *p = s;
- int c;
-
- while (--size > 0)
- {
- c = getc(stream);
- if (c == EOF)
- {
- if (p == s)
- return 0;
- c = 0;
- }
- *p++ = c;
- if (!c)
- return s;
- }
- *p = 0;
- return s;
-}
-
-int
-main(int argc, char **argv)
-{
- const char **pkgs = 0;
- char *manifest = 0;
- int manifest0 = 0;
- int i, c, res, npkgs = 0;
- Pool *pool = pool_create();
- Repo *repo;
- FILE *fp;
- char buf[4096], *p;
- const char *basefile = 0;
- int flags = 0;
-
- while ((c = getopt(argc, argv, "0b:m:i")) >= 0)
- {
- switch(c)
- {
- case 'b':
- basefile = optarg;
- break;
- case 'm':
- manifest = optarg;
- break;
- case '0':
- manifest0 = 1;
- break;
- case 'i':
- flags |= ARCH_ADD_WITH_PKGID;
- break;
- default:
- exit(1);
- }
- }
- if (manifest)
- {
- if (!strcmp(manifest, "-"))
- fp = stdin;
- else if ((fp = fopen(manifest, "r")) == 0)
- {
- perror(manifest);
- exit(1);
- }
- for (;;)
- {
- if (manifest0)
- {
- if (!fgets0(buf, sizeof(buf), fp))
- break;
- }
- else
- {
- if (!fgets(buf, sizeof(buf), fp))
- break;
- if ((p = strchr(buf, '\n')) != 0)
- *p = 0;
- }
- pkgs = solv_extend(pkgs, npkgs, 1, sizeof(char *), 15);
- pkgs[npkgs++] = strdup(buf);
- }
- if (fp != stdin)
- fclose(fp);
- }
- while (optind < argc)
- {
- pkgs = solv_extend(pkgs, npkgs, 1, sizeof(char *), 15);
- pkgs[npkgs++] = solv_strdup(argv[optind++]);
- }
- repo = repo_create(pool, "archpkgs2solv");
- repo_add_repodata(repo, 0);
- res = 0;
- for (i = 0; i < npkgs; i++)
- if (repo_add_arch_pkg(repo, pkgs[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|flags) == 0)
- {
- fprintf(stderr, "archpkgs2solv: %s\n", pool_errstr(pool));
- res = 1;
- }
- repo_internalize(repo);
- tool_write(repo, basefile, 0);
- pool_free(pool);
- for (c = 0; c < npkgs; c++)
- solv_free((char *)pkgs[c]);
- solv_free(pkgs);
- exit(res);
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * archrepo2solv.c
- *
- * parse archlinux repo file
- *
- * reads from stdin
- * writes to stdout
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_arch.h"
-#include "solv_xfopen.h"
-#include "common_write.h"
-
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "archrepo2solv\n"
- " reads a repository from <stdin> and writes a .solv file to <stdout>\n"
- " -l <dbdir> : read local database\n"
- " -h : print help & exit\n"
- );
- exit(status);
-}
-
-int
-main(int argc, char **argv)
-{
- Pool *pool;
- Repo *repo;
- int c, ret;
- const char *localdb = 0;
-
- while ((c = getopt(argc, argv, "hl:")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- case 'l':
- localdb = optarg;
- break;
- default:
- usage(1);
- break;
- }
- }
- pool = pool_create();
- repo = repo_create(pool, "<stdin>");
- if (localdb)
- ret = repo_add_arch_local(repo, localdb, 0);
- else
- ret = repo_add_arch_repo(repo, stdin, 0);
- if (ret)
- {
- fprintf(stderr, "archrepo2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- tool_write(repo, 0, 0);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_write.h"
-#include "common_write.h"
-
-#define LIBSOLV_TOOLVERSION "1.0"
-
-static Id verticals[] = {
- SOLVABLE_AUTHORS,
- SOLVABLE_DESCRIPTION,
- SOLVABLE_MESSAGEDEL,
- SOLVABLE_MESSAGEINS,
- SOLVABLE_EULA,
- SOLVABLE_DISKUSAGE,
- SOLVABLE_FILELIST,
- SOLVABLE_CHANGELOG_AUTHOR,
- SOLVABLE_CHANGELOG_TEXT,
- 0
-};
-
-static char *languagetags[] = {
- "solvable:summary:",
- "solvable:description:",
- "solvable:messageins:",
- "solvable:messagedel:",
- "solvable:eula:",
- 0
-};
-
-static int test_separate = 0;
-
-struct keyfilter_data {
- char **languages;
- int nlanguages;
- int haveaddedfileprovides;
- int haveexternal;
-};
-
-static int
-keyfilter_solv(Repo *data, Repokey *key, void *kfdata)
-{
- struct keyfilter_data *kd = kfdata;
- int i;
- const char *keyname;
-
- if (test_separate && key->storage != KEY_STORAGE_SOLVABLE)
- return KEY_STORAGE_DROPPED;
- if (!kd->haveaddedfileprovides && key->name == REPOSITORY_ADDEDFILEPROVIDES)
- return KEY_STORAGE_DROPPED;
- if (!kd->haveexternal && key->name == REPOSITORY_EXTERNAL)
- return KEY_STORAGE_DROPPED;
- for (i = 0; verticals[i]; i++)
- if (key->name == verticals[i])
- return KEY_STORAGE_VERTICAL_OFFSET;
- keyname = pool_id2str(data->pool, key->name);
- for (i = 0; languagetags[i] != 0; i++)
- if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
- return KEY_STORAGE_VERTICAL_OFFSET;
- return KEY_STORAGE_INCORE;
-}
-
-static int
-keyfilter_attr(Repo *data, Repokey *key, void *kfdata)
-{
- int i;
- const char *keyname;
- if (key->storage == KEY_STORAGE_SOLVABLE)
- return KEY_STORAGE_DROPPED;
- /* those must only be in the main solv file */
- if (key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_TOOLVERSION)
- return KEY_STORAGE_DROPPED;
- for (i = 0; verticals[i]; i++)
- if (key->name == verticals[i])
- return KEY_STORAGE_VERTICAL_OFFSET;
- keyname = pool_id2str(data->pool, key->name);
- for (i = 0; languagetags[i] != 0; i++)
- if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
- return KEY_STORAGE_VERTICAL_OFFSET;
- return KEY_STORAGE_INCORE;
-}
-
-static int
-keyfilter_language(Repo *repo, Repokey *key, void *kfdata)
-{
- Pool *pool = repo->pool;
- const char *name, *p;
- char *lang = kfdata;
- int i;
-
- name = pool_id2str(repo->pool, key->name);
- p = strrchr(name, ':');
- if (!p || strcmp(p + 1, lang) != 0)
- return KEY_STORAGE_DROPPED;
- for (i = 0; verticals[i]; i++)
- {
- const char *vname = pool_id2str(pool, verticals[i]);
- if (!strncmp(name, vname, p - name) && vname[p - name] == 0)
- return KEY_STORAGE_VERTICAL_OFFSET;
- }
- return KEY_STORAGE_INCORE;
-}
-
-static int
-keyfilter_DU(Repo *repo, Repokey *key, void *kfdata)
-{
- int i;
- if (key->name != SOLVABLE_DISKUSAGE)
- return KEY_STORAGE_DROPPED;
- for (i = 0; verticals[i]; i++)
- if (key->name == verticals[i])
- return KEY_STORAGE_VERTICAL_OFFSET;
- return KEY_STORAGE_INCORE;
-}
-
-static int
-keyfilter_FL(Repo *repo, Repokey *key, void *kfdata)
-{
- int i;
- if (key->name != SOLVABLE_FILELIST)
- return KEY_STORAGE_DROPPED;
- for (i = 0; verticals[i]; i++)
- if (key->name == verticals[i])
- return KEY_STORAGE_VERTICAL_OFFSET;
- return KEY_STORAGE_INCORE;
-}
-
-static int
-keyfilter_other(Repo *repo, Repokey *key, void *kfdata)
-{
- const char *name, *p;
- struct keyfilter_data *kd = kfdata;
- int i;
-
- if (!kd->haveaddedfileprovides && key->name == REPOSITORY_ADDEDFILEPROVIDES)
- return KEY_STORAGE_DROPPED;
- if (!kd->haveexternal && key->name == REPOSITORY_EXTERNAL)
- return KEY_STORAGE_DROPPED;
-
- if (key->name == SOLVABLE_FILELIST || key->name == SOLVABLE_DISKUSAGE)
- return KEY_STORAGE_DROPPED;
-
- name = pool_id2str(repo->pool, key->name);
- p = strrchr(name, ':');
- if (p)
- {
- for (i = 0; i < kd->nlanguages; i++)
- if (!strcmp(p + 1, kd->languages[i]))
- return KEY_STORAGE_DROPPED;
- }
- for (i = 0; verticals[i]; i++)
- if (key->name == verticals[i])
- return KEY_STORAGE_VERTICAL_OFFSET;
- return KEY_STORAGE_INCORE;
-}
-
-/*
- * Write <repo> to stdout
- * If <attrname> is given, write attributes to <attrname>
- * If <basename> is given, split attributes
- */
-
-#define REPODATAFILE_BLOCK 15
-
-static void
-write_info(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Repodata *info, const char *location)
-{
- Id h;
- Queue keyq;
-
- queue_init(&keyq);
- if (repo_write_filtered(repo, fp, keyfilter, kfdata, &keyq) != 0)
- {
- fprintf(stderr, "repo_write failed\n");
- exit(1);
- }
- h = repodata_new_handle(info);
- if (keyq.count)
- repodata_set_idarray(info, h, REPOSITORY_KEYS, &keyq);
- queue_free(&keyq);
- repodata_set_str(info, h, REPOSITORY_LOCATION, location);
- repodata_add_flexarray(info, SOLVID_META, REPOSITORY_EXTERNAL, h);
-}
-
-void
-tool_write(Repo *repo, const char *basename, const char *attrname)
-{
- Repodata *data;
- Repodata *info = 0;
- Repokey *key;
- char **languages = 0;
- int nlanguages = 0;
- int i, j, k, l;
- struct keyfilter_data kd;
- Queue addedfileprovides;
-
- memset(&kd, 0, sizeof(kd));
- info = repo_add_repodata(repo, 0);
- repodata_set_str(info, SOLVID_META, REPOSITORY_TOOLVERSION, LIBSOLV_TOOLVERSION);
- queue_init(&addedfileprovides);
- pool_addfileprovides_queue(repo->pool, &addedfileprovides, 0);
- if (addedfileprovides.count)
- {
- kd.haveaddedfileprovides = 1;
- repodata_set_idarray(info, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &addedfileprovides);
- }
- queue_free(&addedfileprovides);
-
- pool_freeidhashes(repo->pool); /* free some mem */
-
- if (basename)
- {
- char fn[4096];
- FILE *fp;
- int has_DU = 0;
- int has_FL = 0;
-
- /* find languages and other info */
- FOR_REPODATAS(repo, i, data)
- {
- for (j = 1, key = data->keys + j; j < data->nkeys; j++, key++)
- {
- const char *keyname = pool_id2str(repo->pool, key->name);
- if (key->name == SOLVABLE_DISKUSAGE)
- has_DU = 1;
- if (key->name == SOLVABLE_FILELIST)
- has_FL = 1;
- for (k = 0; languagetags[k] != 0; k++)
- if (!strncmp(keyname, languagetags[k], strlen(languagetags[k])))
- break;
- if (!languagetags[k])
- continue;
- l = strlen(languagetags[k]);
- if (strlen(keyname + l) > 5)
- continue;
- for (k = 0; k < nlanguages; k++)
- if (!strcmp(languages[k], keyname + l))
- break;
- if (k < nlanguages)
- continue;
- languages = solv_realloc2(languages, nlanguages + 1, sizeof(char *));
- languages[nlanguages++] = strdup(keyname + l);
- }
- }
- /* write language subfiles */
- for (i = 0; i < nlanguages; i++)
- {
- sprintf(fn, "%s.%s.solv", basename, languages[i]);
- if (!(fp = fopen(fn, "w")))
- {
- perror(fn);
- exit(1);
- }
- write_info(repo, fp, keyfilter_language, languages[i], info, fn);
- fclose(fp);
- kd.haveexternal = 1;
- }
- /* write DU subfile */
- if (has_DU)
- {
- sprintf(fn, "%s.DU.solv", basename);
- if (!(fp = fopen(fn, "w")))
- {
- perror(fn);
- exit(1);
- }
- write_info(repo, fp, keyfilter_DU, 0, info, fn);
- fclose(fp);
- kd.haveexternal = 1;
- }
- /* write filelist */
- if (has_FL)
- {
- sprintf(fn, "%s.FL.solv", basename);
- if (!(fp = fopen(fn, "w")))
- {
- perror(fn);
- exit(1);
- }
- write_info(repo, fp, keyfilter_FL, 0, info, fn);
- fclose(fp);
- kd.haveexternal = 1;
- }
- /* write everything else */
- sprintf(fn, "%s.solv", basename);
- if (!(fp = fopen(fn, "w")))
- {
- perror(fn);
- exit(1);
- }
- kd.languages = languages;
- kd.nlanguages = nlanguages;
- repodata_internalize(info);
- if (repo_write_filtered(repo, fp, keyfilter_other, &kd, 0) != 0)
- {
- fprintf(stderr, "repo_write failed\n");
- exit(1);
- }
- if (fclose(fp) != 0)
- {
- perror("fclose");
- exit(1);
- }
- for (i = 0; i < nlanguages; i++)
- free(languages[i]);
- solv_free(languages);
- repodata_free(info);
- }
- if (attrname)
- {
- FILE *fp;
- test_separate = 1;
- fp = fopen(attrname, "w");
- write_info(repo, fp, keyfilter_attr, 0, info, attrname);
- fclose(fp);
- kd.haveexternal = 1;
- }
- repodata_internalize(info);
- if (repo_write_filtered(repo, stdout, keyfilter_solv, &kd, 0) != 0)
- {
- fprintf(stderr, "repo_write failed\n");
- exit(1);
- }
- repodata_free(info);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#ifndef COMMON_WRITE_H
-#define COMMON_WRITE_H
-
-#include "repo.h"
-
-void tool_write(Repo *repo, const char *basename, const char *attrname);
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * comps2solv.c
- *
- * parse Fedora Comps type xml and write out .solv file
- *
- * reads from stdin
- * writes to stdout
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_comps.h"
-#include "common_write.h"
-
-int
-main(int argc, char **argv)
-{
- Pool *pool = pool_create();
- Repo *repo = repo_create(pool, "<stdin>");
- if (repo_add_comps(repo, stdin, 0))
- {
- fprintf(stderr, "comps2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- tool_write(repo, 0, 0);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "evr.h"
-#include "solver.h"
-#include "solverdebug.h"
-#include "repo_cudf.h"
-#include "repo_write.h"
-#include "solv_xfopen.h"
-
-static void
-dump_repo(Repo *repo, char *name)
-{
- FILE *fp;
- if ((fp = fopen(name, "w")) == 0)
- {
- perror(name);
- exit(1);
- }
- repo_write(repo, fp);
- fclose(fp);
-}
-
-static int
-sortfunc(const void *ap, const void *bp, void *dp)
-{
- Pool *pool = dp;
- Solvable *sa, *sb;
- sa = pool->solvables + *(Id *)ap;
- sb = pool->solvables + *(Id *)bp;
- if (sa->name != sb->name)
- {
- int r = strcmp(pool_id2str(pool, sa->name), pool_id2str(pool, sb->name));
- if (r)
- return r;
- }
- if (sa->evr != sb->evr)
- {
- int r = pool_evrcmp(pool, sa->evr, sb->evr, EVRCMP_COMPARE);
- if (r)
- return r;
- }
- return *(Id *)ap - *(Id *)bp;
-}
-
-int
-main(int argc, char **argv)
-{
- char *cudfin;
- char *cudfout = 0;
- Pool *pool;
- Repo *installed, *repo;
- FILE *fp, *ofp;
- Solver *solv;
- Transaction *trans;
- Queue job;
- Queue dq;
- int i;
- int debug = 0;
-
- while (argc > 1 && !strcmp(argv[1], "-d"))
- {
- debug++;
- argc--;
- argv++;
- }
- if (argc < 2)
- {
- fprintf(stderr, "Usage: cudftest <cudfin> [cudfout]\n");
- exit(1);
- }
- cudfin = argv[1];
- cudfout = argc > 2 ? argv[2] : 0;
-
- if ((fp = solv_xfopen(cudfin, 0)) == 0)
- {
- perror(cudfin);
- exit(1);
- }
- pool = pool_create();
- if (debug > 1)
- pool_setdebuglevel(pool, debug - 1);
- installed = repo_create(pool, "installed");
- pool_set_installed(pool, installed);
- repo = repo_create(pool, "repo");
- queue_init(&job);
- repo_add_cudf(repo, installed, fp, &job, 0);
- fclose(fp);
-
- pool_createwhatprovides(pool);
-
- /* debug */
- if (debug)
- {
- dump_repo(installed, "cudf_installed.solv");
- dump_repo(repo, "cudf_repo.solv");
- }
-
- solv = solver_create(pool);
- solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);
- /* solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1); */
-
- queue_push2(&job, SOLVER_VERIFY | SOLVER_SOLVABLE_ALL, 0);
- if (solver_solve(solv, &job) != 0)
- {
- int problem;
- int pcnt = solver_problem_count(solv);
- printf("Found %d problems:\n", pcnt);
- for (problem = 1; problem <= pcnt; problem++)
- {
- printf("Problem %d:\n", problem);
- solver_printprobleminfo(solv, problem);
- printf("\n");
- }
- }
- trans = solver_create_transaction(solv);
- solver_free(solv);
-
- if (debug)
- transaction_print(trans);
-
- queue_init(&dq);
- transaction_installedresult(trans, &dq);
- solv_sort(dq.elements, dq.count, sizeof(Id), sortfunc, pool);
-
- ofp = stdout;
- if (cudfout && ((ofp = fopen(cudfout, "w")) == 0))
- {
- perror(cudfout);
- exit(1);
- }
- for (i = 0; i < dq.count; i++)
- {
- Solvable *s = pool_id2solvable(pool, dq.elements[i]);
- fprintf(ofp, "package: %s\n", pool_id2str(pool, s->name));
- fprintf(ofp, "version: %s\n", pool_id2str(pool, s->evr));
- fprintf(ofp, "installed: true\n");
- if (s->repo == pool->installed)
- fprintf(ofp, "was-installed: true\n");
- fprintf(ofp, "\n");
- }
- queue_free(&dq);
- transaction_free(trans);
- queue_free(&job);
- pool_free(pool);
- if (ofp != stdout)
- {
- if (fclose(ofp))
- {
- perror("fclose");
- exit(1);
- }
- }
- exit(0);
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * deb2solv - create a solv file from one or multiple debs
- *
- */
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "util.h"
-#include "pool.h"
-#include "repo.h"
-#include "repo_deb.h"
-#include "repo_solv.h"
-#include "common_write.h"
-
-static char *
-fgets0(char *s, int size, FILE *stream)
-{
- char *p = s;
- int c;
-
- while (--size > 0)
- {
- c = getc(stream);
- if (c == EOF)
- {
- if (p == s)
- return 0;
- c = 0;
- }
- *p++ = c;
- if (!c)
- return s;
- }
- *p = 0;
- return s;
-}
-
-int
-main(int argc, char **argv)
-{
- const char **debs = 0;
- char *manifest = 0;
- int manifest0 = 0;
- int c, i, res, ndebs = 0;
- Pool *pool = pool_create();
- Repo *repo;
- FILE *fp;
- char buf[4096], *p;
- const char *basefile = 0;
-
- while ((c = getopt(argc, argv, "0b:m:")) >= 0)
- {
- switch(c)
- {
- case 'b':
- basefile = optarg;
- break;
- case 'm':
- manifest = optarg;
- break;
- case '0':
- manifest0 = 1;
- break;
- default:
- exit(1);
- }
- }
- if (manifest)
- {
- if (!strcmp(manifest, "-"))
- fp = stdin;
- else if ((fp = fopen(manifest, "r")) == 0)
- {
- perror(manifest);
- exit(1);
- }
- for (;;)
- {
- if (manifest0)
- {
- if (!fgets0(buf, sizeof(buf), fp))
- break;
- }
- else
- {
- if (!fgets(buf, sizeof(buf), fp))
- break;
- if ((p = strchr(buf, '\n')) != 0)
- *p = 0;
- }
- debs = solv_extend(debs, ndebs, 1, sizeof(char *), 15);
- debs[ndebs++] = strdup(buf);
- }
- if (fp != stdin)
- fclose(fp);
- }
- while (optind < argc)
- {
- debs = solv_extend(debs, ndebs, 1, sizeof(char *), 15);
- debs[ndebs++] = strdup(argv[optind++]);
- }
- repo = repo_create(pool, "deb2solv");
- repo_add_repodata(repo, 0);
- res = 0;
- for (i = 0; i < ndebs; i++)
- {
- if (repo_add_deb(repo, debs[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE) == 0)
- {
- fprintf(stderr, "deb2solv: %s\n", pool_errstr(pool));
- res = 1;
- }
- }
- repo_internalize(repo);
- tool_write(repo, basefile, 0);
- pool_free(pool);
- for (c = 0; c < ndebs; c++)
- free((char *)debs[c]);
- solv_free(debs);
- exit(res);
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_deltainfoxml.h"
-#include "common_write.h"
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "deltainfoxml2solv [-a][-h][-n <attrname>]\n"
- " reads a 'deltainfo.xml' file from <stdin> and writes a .solv file to <stdout>\n"
- " -h : print help & exit\n"
- " -n <name>: save attributes as <name>.attr\n"
- );
- exit(status);
-}
-
-int
-main(int argc, char **argv)
-{
- int c, flags = 0;
- char *attrname = 0;
-
- Pool *pool = pool_create();
- Repo *repo = repo_create(pool, "<stdin>");
-
- while ((c = getopt(argc, argv, "hn:")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- case 'n':
- attrname = optarg;
- break;
- default:
- usage(1);
- break;
- }
- }
- if (repo_add_deltainfoxml(repo, stdin, flags))
- {
- fprintf(stderr, "deltainfoxml2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- tool_write(repo, 0, attrname);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_diskusagexml.h"
-#include "common_write.h"
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "diskusagexml2solv [-a][-h][-n <attrname>]\n"
- " reads a 'diskusage.xml' file from <stdin> and writes a .solv file to <stdout>\n"
- " -h : print help & exit\n"
- " -n <name>: save attributes as <name>.attr\n"
- );
- exit(status);
-}
-
-int
-main(int argc, char **argv)
-{
- int c, flags = 0;
- char *attrname = 0;
-
- Pool *pool = pool_create();
- Repo *repo = repo_create(pool, "<stdin>");
-
- while ((c = getopt(argc, argv, "hn:")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- case 'n':
- attrname = optarg;
- break;
- default:
- usage(1);
- break;
- }
- }
- if (repo_add_diskusagexml(repo, stdin, flags))
- {
- fprintf(stderr, "diskusagexml2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- tool_write(repo, 0, attrname);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-static int with_attr;
-static int dump_json;
-
-#include "pool.h"
-#include "chksum.h"
-#include "repo_solv.h"
-
-
-static int
-dump_attr(Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
-{
- const char *keyname;
- KeyValue *kvp;
- int indent = 0;
-
- keyname = pool_id2str(repo->pool, key->name);
- for (kvp = kv; (kvp = kvp->parent) != 0; indent += 2)
- printf(" ");
- switch(key->type)
- {
- case REPOKEY_TYPE_ID:
- if (data && data->localpool)
- kv->str = stringpool_id2str(&data->spool, kv->id);
- else
- kv->str = pool_dep2str(repo->pool, kv->id);
- printf("%s: %s\n", keyname, kv->str);
- break;
- case REPOKEY_TYPE_CONSTANTID:
- printf("%s: %s\n", keyname, pool_dep2str(repo->pool, kv->id));
- break;
- case REPOKEY_TYPE_IDARRAY:
- if (!kv->entry)
- printf("%s:\n%*s", keyname, indent, "");
- if (data && data->localpool)
- printf(" %s\n", stringpool_id2str(&data->spool, kv->id));
- else
- printf(" %s\n", pool_dep2str(repo->pool, kv->id));
- break;
- case REPOKEY_TYPE_STR:
- printf("%s: %s\n", keyname, kv->str);
- break;
- case REPOKEY_TYPE_VOID:
- printf("%s: (void)\n", keyname);
- break;
- case REPOKEY_TYPE_U32:
- case REPOKEY_TYPE_CONSTANT:
- printf("%s: %u\n", keyname, kv->num);
- break;
- case REPOKEY_TYPE_NUM:
- printf("%s: %llu\n", keyname, SOLV_KV_NUM64(kv));
- break;
- case REPOKEY_TYPE_BINARY:
- if (kv->num)
- printf("%s: %02x..%02x len %u\n", keyname, (unsigned char)kv->str[0], (unsigned char)kv->str[kv->num - 1], kv->num);
- else
- printf("%s: len 0\n", keyname);
- break;
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- if (!kv->entry)
- printf("%s:\n%*s", keyname, indent, "");
- printf(" %s %u %u\n", repodata_dir2str(data, kv->id, 0), kv->num, kv->num2);
- break;
- case REPOKEY_TYPE_DIRSTRARRAY:
- if (!kv->entry)
- printf("%s:\n%*s", keyname, indent, "");
- printf(" %s\n", repodata_dir2str(data, kv->id, kv->str));
- break;
- case REPOKEY_TYPE_FIXARRAY:
- case REPOKEY_TYPE_FLEXARRAY:
- if (!kv->entry)
- printf("%s:\n", keyname);
- else
- printf("\n");
- break;
- default:
- if (solv_chksum_len(key->type))
- {
- printf("%s: %s (%s)\n", keyname, repodata_chk2str(data, key->type, (unsigned char *)kv->str), solv_chksum_type2str(key->type));
- break;
- }
- printf("%s: ?\n", keyname);
- break;
- }
- return 0;
-}
-
-static const char *
-jsonstring(Pool *pool, const char *s)
-{
- int needed = 0;
- const unsigned char *s1;
- char *r, *rp;
-
- for (s1 = (const unsigned char *)s; *s1; s1++)
- {
- if (*s1 < 32)
- needed += *s1 == '\n' ? 2 : 6;
- else if (*s1 == '\\' || *s1 == '\"')
- needed += 2;
- else
- needed++;
- }
- r = rp = pool_alloctmpspace(pool, needed + 3);
- *rp++ = '\"';
- for (s1 = (const unsigned char *)s; *s1; s1++)
- {
- if (*s1 < 32)
- {
- int x;
- if (*s1 == '\n')
- {
- *rp++ = '\\';
- *rp++ = 'n';
- continue;
- }
- *rp++ = '\\';
- *rp++ = 'u';
- *rp++ = '0';
- *rp++ = '0';
- x = *s1 / 16;
- *rp++ = (x < 10 ? '0' : 'a' - 10) + x;
- x = *s1 & 15;
- *rp++ = (x < 10 ? '0' : 'a' - 10) + x;
- }
- else if (*s1 == '\\' || *s1 == '\"')
- {
- *rp++ = '\\';
- *rp++ = *s1;
- }
- else
- *rp++ = *s1;
- }
- *rp++ = '\"';
- *rp = 0;
- return r;
-}
-
-struct cbdata {
- unsigned char *first;
- int nfirst;
- int baseindent;
-};
-
-static int
-dump_attr_json(Repo *repo, Repodata *data, Repokey *key, KeyValue *kv, struct cbdata *cbdata)
-{
- Pool *pool = repo->pool;
- const char *keyname;
- KeyValue *kvp;
- int indent = cbdata->baseindent;
- int isarray = 0;
- const char *str;
- int depth = 0;
-
- keyname = pool_id2str(repo->pool, key->name);
- for (kvp = kv; (kvp = kvp->parent) != 0; indent += 4)
- depth++;
- if (cbdata->nfirst < depth + 1)
- {
- cbdata->first = solv_realloc(cbdata->first, depth + 16);
- memset(cbdata->first + cbdata->nfirst, 0, depth + 16 - cbdata->nfirst);
- cbdata->nfirst = depth + 16;
- }
- switch(key->type)
- {
- case REPOKEY_TYPE_IDARRAY:
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- case REPOKEY_TYPE_DIRSTRARRAY:
- isarray = 1;
- break;
- case REPOKEY_TYPE_FIXARRAY:
- case REPOKEY_TYPE_FLEXARRAY:
- isarray = 2;
- break;
- default:
- break;
- }
- if (!isarray || !kv->entry)
- {
- if (cbdata->first[depth])
- printf(",\n");
- printf("%*s%s: ", indent, "", jsonstring(pool, keyname));
- cbdata->first[depth] = 1;
- }
- if (isarray == 1 && !kv->entry)
- printf("[\n%*s", indent + 2, "");
- else if (isarray == 1 && kv->entry)
- printf("%*s", indent + 2, "");
- switch(key->type)
- {
- case REPOKEY_TYPE_ID:
- if (data && data->localpool)
- str = stringpool_id2str(&data->spool, kv->id);
- else
- str = pool_dep2str(repo->pool, kv->id);
- printf("%s", jsonstring(pool, str));
- break;
- case REPOKEY_TYPE_CONSTANTID:
- str = pool_dep2str(repo->pool, kv->id);
- printf("%s", jsonstring(pool, str));
- break;
- case REPOKEY_TYPE_IDARRAY:
- if (data && data->localpool)
- str = stringpool_id2str(&data->spool, kv->id);
- else
- str = pool_dep2str(repo->pool, kv->id);
- printf("%s", jsonstring(pool, str));
- break;
- case REPOKEY_TYPE_STR:
- str = kv->str;
- printf("%s", jsonstring(pool, str));
- break;
- case REPOKEY_TYPE_VOID:
- printf("null");
- break;
- case REPOKEY_TYPE_U32:
- case REPOKEY_TYPE_CONSTANT:
- printf("%u", kv->num);
- break;
- case REPOKEY_TYPE_NUM:
- printf("%llu", SOLV_KV_NUM64(kv));
- break;
- case REPOKEY_TYPE_BINARY:
- printf("\"<binary>\"");
- break;
- case REPOKEY_TYPE_DIRNUMNUMARRAY:
- printf("{\n");
- printf("%*s \"dir\": %s,\n", indent, "", jsonstring(pool, repodata_dir2str(data, kv->id, 0)));
- printf("%*s \"num1\": %u,\n", indent, "", kv->num);
- printf("%*s \"num2\": %u\n", indent, "", kv->num2);
- printf("%*s }", indent, "");
- break;
- case REPOKEY_TYPE_DIRSTRARRAY:
- printf("%s", jsonstring(pool, repodata_dir2str(data, kv->id, kv->str)));
- break;
- case REPOKEY_TYPE_FIXARRAY:
- case REPOKEY_TYPE_FLEXARRAY:
- cbdata->first[depth + 1] = 0;
- if (!kv->entry)
- printf("[\n");
- else
- {
- if (kv->eof != 2)
- printf("\n%*s },\n", indent, "");
- else
- printf("\n%*s }\n", indent, "");
- }
- if (kv->eof != 2)
- printf("%*s {\n", indent, "");
- else
- printf("%*s]", indent, "");
- break;
- default:
- if (solv_chksum_len(key->type))
- {
- printf("{\n");
- printf("%*s \"value\": %s,\n", indent, "", jsonstring(pool, repodata_chk2str(data, key->type, (unsigned char *)kv->str)));
- printf("%*s \"type\": %s\n", indent, "", jsonstring(pool, solv_chksum_type2str(key->type)));
- printf("%*s}", indent, "");
- break;
- }
- printf("\"?\"");
- break;
- }
- if (isarray == 1)
- {
- if (!kv->eof)
- printf(",\n");
- else
- printf("\n%*s]", indent, "");
- }
- return 0;
-}
-
-static int
-dump_repodata_cb(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
-{
- if (key->name == REPOSITORY_SOLVABLES)
- return SEARCH_NEXT_SOLVABLE;
- if (!dump_json)
- return dump_attr(data->repo, data, key, kv);
- else
- return dump_attr_json(data->repo, data, key, kv, vcbdata);
-}
-
-static void
-dump_repodata(Repo *repo)
-{
- int i;
- Repodata *data;
- if (repo->nrepodata == 0)
- return;
- printf("repo contains %d repodata sections:\n", repo->nrepodata - 1);
- FOR_REPODATAS(repo, i, data)
- {
- unsigned int j;
- printf("\nrepodata %d has %d keys, %d schemata\n", i, data->nkeys - 1, data->nschemata - 1);
- for (j = 1; j < data->nkeys; j++)
- printf(" %s (type %s size %d storage %d)\n", pool_id2str(repo->pool, data->keys[j].name), pool_id2str(repo->pool, data->keys[j].type), data->keys[j].size, data->keys[j].storage);
- if (data->localpool)
- printf(" localpool has %d strings, size is %d\n", data->spool.nstrings, data->spool.sstrings);
- if (data->dirpool.ndirs)
- printf(" localpool has %d directories\n", data->dirpool.ndirs);
- printf("\n");
- repodata_search(data, SOLVID_META, 0, SEARCH_ARRAYSENTINEL|SEARCH_SUB, dump_repodata_cb, 0);
- }
- printf("\n");
-}
-
-static void
-dump_repodata_json(Repo *repo, struct cbdata *cbdata)
-{
- int i;
- Repodata *data;
- if (repo->nrepodata == 0)
- return;
- cbdata->baseindent = 6;
- FOR_REPODATAS(repo, i, data)
- repodata_search(data, SOLVID_META, 0, SEARCH_ARRAYSENTINEL|SEARCH_SUB, dump_repodata_cb, cbdata);
-}
-
-/*
- * dump all attributes for Id <p>
- */
-
-void
-dump_solvable(Repo *repo, Id p, struct cbdata *cbdata)
-{
- Dataiterator di;
- dataiterator_init(&di, repo->pool, repo, p, 0, 0, SEARCH_ARRAYSENTINEL|SEARCH_SUB);
- if (cbdata && cbdata->first)
- cbdata->first[0] = 0;
- if (cbdata)
- cbdata->baseindent = 10;
- while (dataiterator_step(&di))
- {
- if (!dump_json)
- dump_attr(repo, di.data, di.key, &di.kv);
- else
- dump_attr_json(repo, di.data, di.key, &di.kv, cbdata);
- }
- dataiterator_free(&di);
-}
-
-static int
-loadcallback(Pool *pool, Repodata *data, void *vdata)
-{
- FILE *fp = 0;
- int r;
- const char *location;
-
- location = repodata_lookup_str(data, SOLVID_META, REPOSITORY_LOCATION);
- if (!location || !with_attr)
- return 0;
- fprintf(stderr, "[Loading SOLV file %s]\n", location);
- fp = fopen (location, "r");
- if (!fp)
- {
- perror(location);
- return 0;
- }
- r = repo_add_solv(data->repo, fp, REPO_USE_LOADING|REPO_LOCALPOOL);
- fclose(fp);
- return !r ? 1 : 0;
-}
-
-
-static void
-usage(int status)
-{
- fprintf( stderr, "\nUsage:\n"
- "dumpsolv [-a] [-j] [<solvfile>]\n"
- " -a read attributes.\n"
- " -j dump json format.\n"
- );
- exit(status);
-}
-
-int main(int argc, char **argv)
-{
- Repo *repo;
- Pool *pool;
- int c, i, j, n;
- Solvable *s;
-
- pool = pool_create();
- pool_setloadcallback(pool, loadcallback, 0);
-
- while ((c = getopt(argc, argv, "haj")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- case 'a':
- with_attr = 1;
- break;
- case 'j':
- dump_json = 1;
- break;
- default:
- usage(1);
- break;
- }
- }
- if (!dump_json)
- pool_setdebuglevel(pool, 1);
- if (dump_json)
- pool->debugmask |= SOLV_DEBUG_TO_STDERR;
- for (; optind < argc; optind++)
- {
- if (freopen(argv[optind], "r", stdin) == 0)
- {
- perror(argv[optind]);
- exit(1);
- }
- repo = repo_create(pool, argv[optind]);
- if (repo_add_solv(repo, stdin, 0))
- {
- fprintf(stderr, "could not read repository: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
- if (!pool->urepos)
- {
- repo = repo_create(pool, argc != 1 ? argv[1] : "<stdin>");
- if (repo_add_solv(repo, stdin, 0))
- {
- fprintf(stderr, "could not read repository: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
-
- if (dump_json)
- {
- int openrepo = 0;
- struct cbdata cbdata;
-
- memset(&cbdata, 0, sizeof(cbdata));
- printf("{\n");
- printf(" \"repositories\": [\n");
- FOR_REPOS(j, repo)
- {
- int open = 0;
-
- if (openrepo)
- printf("\n },");
- printf(" {\n");
- openrepo = 1;
- if (cbdata.first)
- cbdata.first[0] = 0;
- dump_repodata_json(repo, &cbdata);
- if (cbdata.first[0])
- printf(",\n");
- printf(" \"solvables\": [\n");
- FOR_REPO_SOLVABLES(repo, i, s)
- {
- if (open)
- printf("\n },\n");
- printf(" {\n");
- open = 1;
- dump_solvable(repo, i, &cbdata);
- }
- if (open)
- printf("\n }\n");
- printf(" ]\n");
- }
- if (openrepo)
- printf(" }\n");
- printf(" ]\n");
- printf("}\n");
- solv_free(cbdata.first);
- }
- else
- {
- printf("pool contains %d strings, %d rels, string size is %d\n", pool->ss.nstrings, pool->nrels, pool->ss.sstrings);
- n = 0;
- FOR_REPOS(j, repo)
- {
- dump_repodata(repo);
- printf("repo %d contains %d solvables\n", j, repo->nsolvables);
- printf("repo start: %d end: %d\n", repo->start, repo->end);
- FOR_REPO_SOLVABLES(repo, i, s)
- {
- n++;
- printf("\n");
- printf("solvable %d (%d):\n", n, i);
- dump_solvable(repo, i, 0);
- }
- }
- }
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/* vim: sw=2 et
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "solver.h"
-#include "solverdebug.h"
-#include "hash.h"
-#include "repo_rpmdb.h"
-#include "pool_fileconflicts.h"
-
-static void *
-iterate_handle(Pool *pool, Id p, void *cbdata)
-{
- Solvable *s = pool->solvables + p;
- Id rpmdbid;
- void *handle;
-
- if (!s->repo->rpmdbid)
- return 0;
- rpmdbid = s->repo->rpmdbid[p - s->repo->start];
- if (!rpmdbid)
- return 0;
- handle = rpm_byrpmdbid(cbdata, rpmdbid);
- if (!handle)
- fprintf(stderr, "rpm_byrpmdbid: %s\n", pool_errstr(pool));
- return handle;
-}
-
-int main(int argc, char **argv)
-{
- Pool *pool;
- Repo *installed;
- Solvable *s;
- Id p;
- int i;
- Queue todo, conflicts;
- void *state = 0;
- char *rootdir = 0;
-
- if (argc == 3 && !strcmp(argv[1], "--root"))
- rootdir = argv[2];
- pool = pool_create();
- if (rootdir)
- pool_set_rootdir(pool, rootdir);
- pool_setdebuglevel(pool, 1);
- installed = repo_create(pool, "@System");
- pool_set_installed(pool, installed);
- if (repo_add_rpmdb(installed, 0, REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "findfileconflicts: %s\n", pool_errstr(pool));
- exit(1);
- }
- queue_init(&todo);
- queue_init(&conflicts);
- FOR_REPO_SOLVABLES(installed, p, s)
- queue_push(&todo, p);
- state = rpm_state_create(pool, pool_get_rootdir(pool));
- pool_findfileconflicts(pool, &todo, 0, &conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &iterate_handle, state);
- rpm_state_free(state);
- queue_free(&todo);
- for (i = 0; i < conflicts.count; i += 6)
- {
- if (conflicts.elements[i] != conflicts.elements[i + 3])
- printf("%s - %s: %s[%s] %s[%s]\n", pool_id2str(pool, conflicts.elements[i]), pool_id2str(pool, conflicts.elements[i + 3]), pool_solvid2str(pool, conflicts.elements[i + 1]), pool_id2str(pool, conflicts.elements[i + 2]), pool_solvid2str(pool, conflicts.elements[i + 4]), pool_id2str(pool, conflicts.elements[i + 5]));
- else
- printf("%s: %s[%s] %s[%s]\n", pool_id2str(pool, conflicts.elements[i]), pool_solvid2str(pool, conflicts.elements[i + 1]), pool_id2str(pool, conflicts.elements[i + 2]), pool_solvid2str(pool, conflicts.elements[i + 4]), pool_id2str(pool, conflicts.elements[i + 5]));
- }
- if (conflicts.count)
- {
- Queue job;
- int problemcnt;
-
- queue_init(&job);
- pool_add_fileconflicts_deps(pool, &conflicts);
- pool_addfileprovides(pool);
- pool_createwhatprovides(pool);
- pool_setdebuglevel(pool, 0);
- Solver *solv = solver_create(pool);
- queue_push2(&job, SOLVER_VERIFY|SOLVER_SOLVABLE_ALL, 0);
-#if 0
- solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);
-#endif
- problemcnt = solver_solve(solv, &job);
- if (problemcnt)
- solver_printallsolutions(solv);
- else
- {
- Transaction *trans = solver_create_transaction(solv);
- transaction_print(trans);
- transaction_free(trans);
- }
- queue_free(&job);
- solver_free(solv);
- }
- queue_free(&conflicts);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * helix2solv.c
- *
- * parse 'helix' type xml and write out .solv file
- *
- * reads from stdin
- * writes to stdout
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "pool.h"
-#include "repo_helix.h"
-#include "common_write.h"
-
-int
-main(int argc, char **argv)
-{
- Pool *pool = pool_create();
- Repo *repo = repo_create(pool, "<stdin>");
- if (repo_add_helix(repo, stdin, 0))
- {
- fprintf(stderr, "helix2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- tool_write(repo, 0, 0);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/* vim: sw=2 et cino=>4,n-2,{1s
- */
-
-/*
- * Copyright (c) 2009-2015, SUSE LLC
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <zlib.h>
-
-#include "pool.h"
-#include "poolarch.h"
-#include "repo_solv.h"
-#ifdef ENABLE_SUSEREPO
-#include "repo_susetags.h"
-#endif
-#ifdef ENABLE_RPMMD
-#include "repo_rpmmd.h"
-#endif
-#ifdef ENABLE_DEBIAN
-#include "repo_deb.h"
-#endif
-#ifdef ENABLE_ARCHREPO
-#include "repo_arch.h"
-#endif
-#include "solver.h"
-#include "solv_xfopen.h"
-
-
-void
-usage(char** argv)
-{
- printf("Usage:\n%s: <arch> [options..] repo [--nocheck repo]...\n"
- "\t--exclude <pattern>\twhitespace-separated list of (sub-)"
- "packagenames to ignore\n"
- "\t--withobsoletes\t\tCheck for obsoletes on packages contained in repos\n"
- "\t--nocheck\t\tDo not warn about all following repos (only use them to fulfill dependencies)\n"
- "\t--withsrc\t\tAlso check dependencies of src.rpm\n\n"
- , argv[0]);
- exit(1);
-}
-
-static int
-strlen_comp(const char *str)
-{
- size_t l = strlen(str);
- if (l > 3 && !strcmp(str + l - 3, ".gz"))
- return l - 3;
- if (l > 3 && !strcmp(str + l - 3, ".xz"))
- return l - 3;
- if (l > 4 && !strcmp(str + l - 4, ".bz2"))
- return l - 4;
- if (l > 5 && !strcmp(str + l - 4, ".lzma"))
- return l - 5;
- return l;
-}
-
-int
-main(int argc, char **argv)
-{
- Pool *pool;
- Solver *solv;
- Repo *repo;
- Queue job;
- Queue rids;
- Queue cand;
- char *arch, *exclude_pat;
- int i, j;
- Id p;
- Id archid, noarchid;
- Id rpmrel;
-#ifndef DEBIAN
- Id rpmid;
-#endif
- int status = 0;
- int nocheck = 0;
- int withsrc = 0;
- int obsoletepkgcheck = 0;
-
- exclude_pat = 0;
- if (argc < 3)
- usage(argv);
-
- arch = argv[1];
- pool = pool_create();
- pool_setarch(pool, arch);
- noarchid = pool->solvables[SYSTEMSOLVABLE].arch;
- for (i = 2; i < argc; i++)
- {
- FILE *fp;
- int r, l;
-
- if (!strcmp(argv[i], "--withsrc"))
- {
- withsrc++;
- continue;
- }
- if (!strcmp(argv[i], "--withobsoletes"))
- {
- obsoletepkgcheck++;
- continue;
- }
- if (!strcmp(argv[i], "--nocheck"))
- {
- if (!nocheck)
- nocheck = pool->nsolvables;
- continue;
- }
- if (!strcmp(argv[i], "--exclude"))
- {
- if (i + 1 >= argc)
- {
- printf("--exclude needs a whitespace separated list of substrings as parameter\n");
- exit(1);
- }
- exclude_pat = argv[i + 1];
- ++i;
- continue;
- }
- l = strlen_comp(argv[i]);
- if (!strcmp(argv[i], "-"))
- fp = stdin;
- else if ((fp = solv_xfopen(argv[i], 0)) == 0)
- {
- perror(argv[i]);
- exit(1);
- }
- repo = repo_create(pool, argv[i]);
- r = 0;
- if (0)
- {
- }
-#ifdef ENABLE_SUSEREPO
- else if (l >= 8 && !strncmp(argv[i] + l - 8, "packages", 8))
- {
- r = repo_add_susetags(repo, fp, 0, 0, 0);
- }
-#endif
-#ifdef ENABLE_RPMMD
- else if (l >= 11 && !strncmp(argv[i] + l - 11, "primary.xml", 11))
- {
- r = repo_add_rpmmd(repo, fp, 0, 0);
- if (!r && i + 1 < argc)
- {
- l = strlen_comp(argv[i + 1]);
- if (l >= 13 && !strncmp(argv[i + 1] + l - 13, "filelists.xml", 13))
- {
- i++;
- fclose(fp);
- if ((fp = solv_xfopen(argv[i], 0)) == 0)
- {
- perror(argv[i]);
- exit(1);
- }
- r = repo_add_rpmmd(repo, fp, 0, REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
- }
- }
- }
-#endif
-#ifdef ENABLE_DEBIAN
- else if (l >= 8 && !strncmp(argv[i] + l - 8, "Packages", 8))
- {
- r = repo_add_debpackages(repo, fp, 0);
- }
-#endif
-#ifdef ENABLE_ARCHREPO
- else if (l >= 7 && (!strncmp(argv[i] + l - 7, ".db.tar", 7)))
- {
- r = repo_add_arch_repo(repo, fp, 0);
- }
-#endif
- else
- r = repo_add_solv(repo, fp, 0);
- if (r)
- {
- fprintf(stderr, "could not add repo %s: %s\n", argv[i], pool_errstr(pool));
- exit(1);
- }
- if (fp != stdin)
- fclose(fp);
- }
- pool_addfileprovides(pool);
- pool_createwhatprovides(pool);
- archid = pool_str2id(pool, arch, 0);
-#ifndef DEBIAN
- rpmid = pool_str2id(pool, "rpm", 0);
- rpmrel = 0;
- if (rpmid && archid)
- {
- for (p = 1; p < pool->nsolvables; p++)
- {
- Solvable *s = pool->solvables + p;
- if (s->name == rpmid && s->arch == archid && pool_installable(pool, s))
- break;
- }
- if (p < pool->nsolvables)
- rpmrel = pool_rel2id(pool, rpmid, archid, REL_ARCH, 1);
- }
-#else
- rpmrel = 0;
-#endif
-
- queue_init(&job);
- queue_init(&rids);
- queue_init(&cand);
- for (p = 1; p < (nocheck ? nocheck : pool->nsolvables); p++)
- {
- Solvable *s = pool->solvables + p;
- if (!s->repo)
- continue;
- if (withsrc && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC))
- {
- queue_push(&cand, p);
- continue;
- }
- if (!pool_installable(pool, s))
- continue;
- if (archid && s->arch != archid && s->arch != noarchid)
- {
- /* check if we will conflict with a infarch rule, if yes,
- * don't bother checking the package */
- Id rp, rpp;
- FOR_PROVIDES(rp, rpp, s->name)
- {
- if (pool->solvables[rp].name != s->name)
- continue;
- if (pool->solvables[rp].arch == archid)
- break;
- }
- if (rp)
- continue;
- }
- queue_push(&cand, p);
- }
- if (obsoletepkgcheck)
- {
- int obsoleteusesprovides = pool_get_flag(pool, POOL_FLAG_OBSOLETEUSESPROVIDES);
- int obsoleteusescolors = pool_get_flag(pool, POOL_FLAG_OBSOLETEUSESCOLORS);
-
- for (i = 0; i < cand.count; i++)
- {
- Solvable *s;
- s = pool->solvables + cand.elements[i];
-
- if (s->obsoletes)
- {
- Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
-
- while ((obs = *obsp++) != 0)
- {
- Id op, opp;
- FOR_PROVIDES(op, opp, obs)
- {
- Solvable *os = pool->solvables + op;
- if (nocheck && op >= nocheck)
- continue;
- if (solvable_identical(s, os))
- continue;
- if (!obsoleteusesprovides && !pool_match_nevr(pool, os, obs))
- continue;
- if (obsoleteusescolors && !pool_colormatch(pool, s, os))
- continue;
- status = 2;
- printf("can't install %s:\n", pool_solvid2str(pool, op));
- printf(" package is obsoleted by %s\n", pool_solvable2str(pool, s));
- }
- }
- }
- }
- }
-
- solv = solver_create(pool);
-
- /* prune cand by doing weak installs */
- while (cand.count)
- {
- queue_empty(&job);
- for (i = 0; i < cand.count; i++)
- {
- p = cand.elements[i];
- queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK, p);
- }
- if (rpmrel)
- queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_NAME, rpmrel);
- solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1);
- solver_solve(solv, &job);
- /* prune... */
- for (i = j = 0; i < cand.count; i++)
- {
- p = cand.elements[i];
- if (solver_get_decisionlevel(solv, p) <= 0)
- {
- cand.elements[j++] = p;
- continue;
- }
- }
- cand.count = j;
- if (i == j)
- break;
- }
-
- /* now check every candidate */
- for (i = 0; i < cand.count; i++)
- {
- Solvable *s;
- int problemcount;
-
- p = cand.elements[i];
- if (exclude_pat)
- {
- char *ptr, *save = 0, *pattern;
- int match = 0;
- pattern = solv_strdup(exclude_pat);
-
- for (ptr = strtok_r(pattern, " ", &save);
- ptr;
- ptr = strtok_r(NULL, " ", &save))
- {
- if (*ptr && strstr(pool_solvid2str(pool, p), ptr))
- {
- match = 1;
- break;
- }
- }
- solv_free(pattern);
- if (match)
- continue;
- }
- s = pool->solvables + p;
- queue_empty(&job);
- queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE, p);
- if (rpmrel)
- queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_NAME, rpmrel);
- solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1);
- problemcount = solver_solve(solv, &job);
- if (problemcount)
- {
- Id problem = 0;
- Solvable *s2;
-
- status = 1;
- printf("can't install %s:\n", pool_solvable2str(pool, s));
- while ((problem = solver_next_problem(solv, problem)) != 0)
- {
- solver_findallproblemrules(solv, problem, &rids);
- for (j = 0; j < rids.count; j++)
- {
- Id probr = rids.elements[j];
- int k;
- Queue rinfo;
- queue_init(&rinfo);
-
- solver_allruleinfos(solv, probr, &rinfo);
- for (k = 0; k < rinfo.count; k += 4)
- {
- Id dep, source, target;
- source = rinfo.elements[k + 1];
- target = rinfo.elements[k + 2];
- dep = rinfo.elements[k + 3];
- switch (rinfo.elements[k])
- {
- case SOLVER_RULE_DISTUPGRADE:
- break;
- case SOLVER_RULE_INFARCH:
- s = pool_id2solvable(pool, source);
- printf(" %s has inferior architecture\n", pool_solvable2str(pool, s));
- break;
- case SOLVER_RULE_UPDATE:
- s = pool_id2solvable(pool, source);
- printf(" %s can not be updated\n", pool_solvable2str(pool, s));
- break;
- case SOLVER_RULE_JOB:
- case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
- case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
- case SOLVER_RULE_JOB_UNSUPPORTED:
- break;
- case SOLVER_RULE_RPM:
- printf(" some dependency problem\n");
- break;
- case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
- printf(" nothing provides requested %s\n", pool_dep2str(pool, dep));
- break;
- case SOLVER_RULE_RPM_NOT_INSTALLABLE:
- s = pool_id2solvable(pool, source);
- printf(" package %s is not installable\n", pool_solvable2str(pool, s));
- break;
- case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
- s = pool_id2solvable(pool, source);
- printf(" nothing provides %s needed by %s\n", pool_dep2str(pool, dep), pool_solvable2str(pool, s));
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (!ISRELDEP(rd->name))
- {
- Id rp, rpp;
- FOR_PROVIDES(rp, rpp, rd->name)
- printf(" (we have %s)\n", pool_solvable2str(pool, pool->solvables + rp));
- }
- }
- break;
- case SOLVER_RULE_RPM_SAME_NAME:
- s = pool_id2solvable(pool, source);
- s2 = pool_id2solvable(pool, target);
- printf(" cannot install both %s and %s\n", pool_solvable2str(pool, s), pool_solvable2str(pool, s2));
- break;
- case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
- s = pool_id2solvable(pool, source);
- s2 = pool_id2solvable(pool, target);
- printf(" package %s conflicts with %s provided by %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep), pool_solvable2str(pool, s2));
- break;
- case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
- s = pool_id2solvable(pool, source);
- s2 = pool_id2solvable(pool, target);
- printf(" package %s obsoletes %s provided by %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep), pool_solvable2str(pool, s2));
- break;
- case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
- s = pool_id2solvable(pool, source);
- printf(" package %s requires %s, but none of the providers can be installed\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep));
- break;
- case SOLVER_RULE_RPM_SELF_CONFLICT:
- s = pool_id2solvable(pool, source);
- printf(" package %s conflicts with %s provided by itself\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep));
- break;
- }
- }
- }
- }
- }
- }
- solver_free(solv);
- exit(status);
-}
+++ /dev/null
-/*
- * Copyright (c) 2012, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * mdk2solv.c
- *
- * parse Mandriva/Mageie synthesis file
- *
- * reads from stdin
- * writes to stdout
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_mdk.h"
-#include "solv_xfopen.h"
-#include "common_write.h"
-
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "mdk2solv [-i <infoxml>]\n"
- " reads a 'synthesis' repository from <stdin> and writes a .solv file to <stdout>\n"
- " -i : info.xml file for extra attributes\n"
- " -f : files.xml file for extra attributes\n"
- " -h : print help & exit\n"
- );
- exit(status);
-}
-
-int
-main(int argc, char **argv)
-{
- Pool *pool;
- Repo *repo;
- char *infofile = 0, *filesfile = 0;
- int c;
-
- while ((c = getopt(argc, argv, "hi:f:")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- case 'i':
- infofile = optarg;
- break;
- case 'f':
- filesfile = optarg;
- break;
- default:
- usage(1);
- break;
- }
- }
- pool = pool_create();
- repo = repo_create(pool, "<stdin>");
- if (repo_add_mdk(repo, stdin, REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "mdk2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- if (infofile)
- {
- FILE *fp = solv_xfopen(infofile, "r");
- if (!fp)
- {
- perror(infofile);
- exit(1);
- }
- if (repo_add_mdk_info(repo, fp, REPO_EXTEND_SOLVABLES | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "mdk2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
- if (filesfile)
- {
- FILE *fp = solv_xfopen(filesfile, "r");
- if (!fp)
- {
- perror(filesfile);
- exit(1);
- }
- if (repo_add_mdk_info(repo, fp, REPO_EXTEND_SOLVABLES | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "mdk2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
- repo_internalize(repo);
- tool_write(repo, 0, 0);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * mergesolv
- *
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include "pool.h"
-#include "repo_solv.h"
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-#include "common_write.h"
-
-static void
-usage()
-{
- fprintf(stderr, "\nUsage:\n"
- "mergesolv [file] [file] [...]\n"
- " merges multiple solv files into one and writes it to stdout\n"
- );
- exit(0);
-}
-
-static int
-loadcallback (Pool *pool, Repodata *data, void *vdata)
-{
- FILE *fp;
- const char *location = repodata_lookup_str(data, SOLVID_META, REPOSITORY_LOCATION);
- int r;
-
- if (!location)
- return 0;
- fprintf(stderr, "Loading SOLV file %s\n", location);
- fp = fopen (location, "r");
- if (!fp)
- {
- perror(location);
- return 0;
- }
- r = repo_add_solv(data->repo, fp, REPO_USE_LOADING|REPO_LOCALPOOL);
- fclose(fp);
- return r ? 0 : 1;
-}
-
-int
-main(int argc, char **argv)
-{
- Pool *pool;
- Repo *repo;
- const char *basefile = 0;
- int with_attr = 0;
-#ifdef SUSE
- int add_auto = 0;
-#endif
- int c;
-
- pool = pool_create();
- repo = repo_create(pool, "<mergesolv>");
-
- while ((c = getopt(argc, argv, "ahb:X")) >= 0)
- {
- switch (c)
- {
- case 'h':
- usage();
- break;
- case 'a':
- with_attr = 1;
- break;
- case 'b':
- basefile = optarg;
- break;
- case 'X':
-#ifdef SUSE
- add_auto = 1;
-#endif
- break;
- default:
- usage();
- exit(1);
- }
- }
- if (with_attr)
- pool_setloadcallback(pool, loadcallback, 0);
-
- for (; optind < argc; optind++)
- {
- FILE *fp;
- if ((fp = fopen(argv[optind], "r")) == NULL)
- {
- perror(argv[optind]);
- exit(1);
- }
- if (repo_add_solv(repo, fp, 0))
- {
- fprintf(stderr, "repo %s: %s\n", argv[optind], pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
-#ifdef SUSE
- if (add_auto)
- repo_add_autopattern(repo, 0);
-#endif
- tool_write(repo, basefile, 0);
- pool_free(pool);
- return 0;
-}
+++ /dev/null
-/* vim: sw=2 et
- */
-
-/*
- * Copyright (c) 2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <zlib.h>
-
-#include "pool.h"
-#include "evr.h"
-#include "poolarch.h"
-#include "repo_solv.h"
-#ifdef ENABLE_SUSEREPO
-#include "repo_susetags.h"
-#endif
-#ifdef ENABLE_RPMMD
-#include "repo_updateinfoxml.h"
-#include "repo_rpmmd.h"
-#endif
-#include "solver.h"
-#include "solverdebug.h"
-
-#include "solv_xfopen.h"
-
-void
-showproblems(Solver *solv, Solvable *s, Queue *cand, Queue *badguys)
-{
- Pool *pool = solv->pool;
- Queue rids, rinfo;
- Id problem = 0;
- int jj;
- int rerun = 0;
-
- queue_init(&rids);
- queue_init(&rinfo);
- printf("can't install %s:\n", pool_solvable2str(pool, s));
- while ((problem = solver_next_problem(solv, problem)) != 0)
- {
- solver_findallproblemrules(solv, problem, &rids);
- for (jj = 0; jj < rids.count; jj++)
- {
- Id probr = rids.elements[jj];
- int k, l;
-
- queue_empty(&rinfo);
- solver_allruleinfos(solv, probr, &rinfo);
- for (k = 0; k < rinfo.count; k += 4)
- {
- Id dep, source, target;
- source = rinfo.elements[k + 1];
- target = rinfo.elements[k + 2];
- dep = rinfo.elements[k + 3];
- switch (rinfo.elements[k])
- {
- case SOLVER_RULE_DISTUPGRADE:
- break;
- case SOLVER_RULE_INFARCH:
- printf(" %s has inferior architecture\n", pool_solvid2str(pool, source));
- break;
- case SOLVER_RULE_UPDATE:
- printf(" update rule for %s\n", pool_solvid2str(pool, source));
- if (badguys)
- queue_pushunique(badguys, source);
- if (!cand)
- break;
- /* only drop update problem packages from cand so that we see all problems of this patch */
- for (l = 0; l < cand->count; l++)
- if (cand->elements[l] == source || cand->elements[l] == -source)
- break;
- if (l == cand->count)
- break;
- if (!rerun)
- {
- for (l = 0; l < cand->count; l++)
- if (cand->elements[l] < 0)
- cand->elements[l] = -cand->elements[l];
- rerun = 1;
- }
- for (l = 0; l < cand->count; l++)
- if (cand->elements[l] == source)
- {
- cand->elements[l] = -source;
- }
- break;
- case SOLVER_RULE_JOB:
- case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
- case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
- case SOLVER_RULE_JOB_UNSUPPORTED:
- break;
- case SOLVER_RULE_RPM:
- printf(" some dependency problem\n");
- break;
- case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
- printf(" nothing provides requested %s\n", pool_dep2str(pool, dep));
- break;
- case SOLVER_RULE_RPM_NOT_INSTALLABLE:
- printf(" package %s is not installable\n", pool_solvid2str(pool, source));
- break;
- case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
- printf(" nothing provides %s needed by %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, source));
- if (ISRELDEP(dep))
- {
- Reldep *rd = GETRELDEP(pool, dep);
- if (!ISRELDEP(rd->name))
- {
- Id rp, rpp;
- FOR_PROVIDES(rp, rpp, rd->name)
- printf(" (we have %s)\n", pool_solvid2str(pool, rp));
- }
- }
- break;
- case SOLVER_RULE_RPM_SAME_NAME:
- printf(" cannot install both %s and %s\n", pool_solvid2str(pool, source), pool_solvid2str(pool, target));
- break;
- case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
- printf(" package %s conflicts with %s provided by %s\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep), pool_solvid2str(pool, target));
- break;
- case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
- printf(" package %s obsoletes %s provided by %s\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep), pool_solvid2str(pool, target));
- break;
- case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
- printf(" package %s requires %s, but none of the providers can be installed\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep));
- break;
- case SOLVER_RULE_RPM_SELF_CONFLICT:
- printf(" package %s conflicts with %s provided by itself\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep));
- break;
- }
- }
- }
- }
- queue_free(&rids);
- queue_free(&rinfo);
-}
-
-void
-toinst(Solver *solv, Repo *repo, Repo *instrepo)
-{
- Pool *pool = solv->pool;
- Queue q;
- int k;
- Id p;
-
- queue_init(&q);
- solver_get_decisionqueue(solv, &q);
- for (k = 0; k < q.count; k++)
- {
- p = q.elements[k];
- if (p < 0 || p == SYSTEMSOLVABLE)
- continue;
-
- /* printf(" toinstall %s\n", pool_solvid2str(pool, p));*/
- /* oh my! */
- pool->solvables[p].repo = instrepo;
- }
- queue_free(&q);
-}
-
-void
-dump_instrepo(Repo *instrepo, Pool *pool)
-{
- Solvable *s;
- Id p;
-
- printf("instrepo..\n");
- FOR_REPO_SOLVABLES(instrepo, p, s)
- printf(" %s\n", pool_solvable2str(pool, s));
- printf("done.\n");
-}
-
-void
-frominst(Solver *solv, Repo *repo, Repo *instrepo)
-{
- Pool *pool = solv->pool;
- int k;
-
- for (k = 1; k < pool->nsolvables; k++)
- if (pool->solvables[k].repo == instrepo)
- pool->solvables[k].repo = repo;
-}
-
-void
-usage(char** argv)
-{
-
- printf("%s: <arch> <patchnameprefix> [--install-available] [repos] [--updaterepos] [repos]...\n"
- "\t --install-available: installation repository is available during update\n"
- "\t repos: repository ending in\n"
- "\t\tpackages, packages.gz, primary.xml.gz, updateinfo.xml.gz or .solv\n",
- argv[0]);
-
- exit(1);
-}
-
-typedef struct {
- int updatestart;
- int shown;
- int status;
- int install_available;
- Repo *repo;
- Repo *instrepo;
-} context_t;
-
-#define SHOW_PATCH(c) if (!(c)->shown++) printf("%s:\n", pool_solvable2str(pool, s));
-#define PERF_DEBUGGING 0
-
-static Pool *pool;
-
-void
-test_all_old_patches_included(context_t *c, Id pid)
-{
- Id p, pp;
- Id con, *conp;
- Solvable *s = pool->solvables + pid;
- /* Test 1: are all old patches included */
- FOR_PROVIDES(p, pp, s->name)
- {
- Solvable *s2 = pool->solvables + p;
- Id con2, *conp2;
-
- if (!s2->conflicts)
- continue;
- if (pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) <= 0)
- continue;
- conp2 = s2->repo->idarraydata + s2->conflicts;
- while ((con2 = *conp2++) != 0)
- {
- Reldep *rd2, *rd;
- if (!ISRELDEP(con2))
- continue;
- rd2 = GETRELDEP(pool, con2);
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- if (!ISRELDEP(con))
- continue;
- rd = GETRELDEP(pool, con);
- if (rd->name == rd2->name)
- break;
- }
- if (!con)
- {
- SHOW_PATCH(c);
- printf(" %s contained %s\n", pool_solvable2str(pool, s2), pool_dep2str(pool, rd2->name));
- }
- else
- {
- if (pool_evrcmp(pool, rd->evr, rd2->evr, EVRCMP_COMPARE) < 0)
- {
- SHOW_PATCH(c);
- printf(" %s required newer version %s-%s of %s-%s\n",
- pool_solvable2str(pool, s2), pool_dep2str(pool, rd2->name), pool_dep2str(pool, rd2->evr),
- pool_dep2str(pool, rd->name), pool_dep2str(pool, rd->evr));
- }
- }
-
- }
- }
-}
-
-void
-test_all_packages_installable(context_t *c, Id pid)
-{
- Solver *solv;
- Queue job;
- Id p, pp;
- Id con, *conp;
- unsigned int now, solver_runs;
- int i;
- Solvable *s = pool->solvables + pid;
-
- queue_init(&job);
-
- now = solv_timems(0);
- solver_runs = 0;
-
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- FOR_PROVIDES(p, pp, con)
- {
- queue_empty(&job);
- queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
- queue_push(&job, p);
-
- /* also set up some minimal system */
- queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
- queue_push(&job, pool_str2id(pool, "rpm", 1));
- queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
- queue_push(&job, pool_str2id(pool, "aaa_base", 1));
-
- solv = solver_create(pool);
- /* solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1); */
- ++solver_runs;
- if (solver_solve(solv, &job))
- {
- c->status = 1;
- printf("error installing original package\n");
- showproblems(solv, s, 0, 0);
- }
- toinst(solv, c->repo, c->instrepo);
- solver_free(solv);
-
-#if 0
- dump_instrepo(instrepo, pool);
-
-#endif
- if (!c->install_available)
- {
- queue_empty(&job);
- for (i = 1; i < c->updatestart; i++)
- {
- if (pool->solvables[i].repo != c->repo || i == pid)
- continue;
- queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
- queue_push(&job, i);
- }
- }
- queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE);
- queue_push(&job, pid);
- solv = solver_create(pool);
- /* solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1); */
- ++solver_runs;
- if (solver_solve(solv, &job))
- {
- c->status = 1;
- showproblems(solv, s, 0, 0);
- }
- frominst(solv, c->repo, c->instrepo);
- solver_free(solv);
- }
- }
-
- if (PERF_DEBUGGING)
- printf(" test_all_packages_installable took %d ms in %d runs\n", solv_timems(now), solver_runs);
-}
-
-void
-test_can_upgrade_all_packages(context_t *c, Id pid)
-{
- Solver *solv;
- Id p;
- Id con, *conp;
- Queue job;
- Queue cand;
- Queue badguys;
- int i, j;
- unsigned int now, solver_runs;
- Solvable *s = pool->solvables + pid;
-
- queue_init(&job);
- queue_init(&cand);
- queue_init(&badguys);
-
- now = solv_timems(0);
- solver_runs = 0;
-
- /* Test 3: can we upgrade all packages? */
- for (p = 1; p < pool->nsolvables; p++)
- {
- Solvable *s = pool->solvables + p;
- if (!s->repo)
- continue;
- if (strchr(pool_id2str(pool, s->name), ':'))
- continue; /* only packages, please */
- if (!pool_installable(pool, s))
- continue;
- queue_push(&cand, p);
- }
- while (cand.count)
- {
- solv = solver_create(pool);
- queue_empty(&job);
- for (i = 0; i < badguys.count; i++)
- {
- queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE|SOLVER_WEAK);
- queue_push(&job, badguys.elements[i]);
- }
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
- queue_push(&job, con);
- }
- for (i = 0; i < cand.count; i++)
- {
- p = cand.elements[i];
- queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
- queue_push(&job, p);
- }
- ++solver_runs;
- solver_solve(solv, &job);
-#if 0
- solver_printdecisions(solv);
-#endif
- /* put packages into installed repo and prune them from cand */
- toinst(solv, c->repo, c->instrepo);
- for (i = 0; i < cand.count; i++)
- {
- p = cand.elements[i];
- if (p > 0 && solver_get_decisionlevel(solv, p) > 0)
- cand.elements[i] = -p; /* drop candidate */
- }
- solver_free(solv);
-
- /* now the interesting part: test patch */
- queue_empty(&job);
- if (!c->install_available)
- {
- for (i = 1; i < c->updatestart; i++)
- {
- if (pool->solvables[i].repo != c->repo || i == pid)
- continue;
- queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
- queue_push(&job, i);
- }
- }
- queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE);
- queue_push(&job, pid);
- solv = solver_create(pool);
- solver_set_flag(solv, SOLVER_FLAG_IGNORE_RECOMMENDED, 1);
- ++solver_runs;
- if (solver_solve(solv, &job))
- {
- c->status = 1;
- showproblems(solv, s, &cand, &badguys);
- }
- frominst(solv, c->repo, c->instrepo);
- solver_free(solv);
- /* now drop all negative elements from cand */
- for (i = j = 0; i < cand.count; i++)
- {
- if (cand.elements[i] < 0)
- continue;
- cand.elements[j++] = cand.elements[i];
- }
- if (i == j)
- break; /* no progress */
- cand.count = j;
- }
- if (PERF_DEBUGGING)
- printf(" test_can_upgrade_all_packages took %d ms in %d runs\n", solv_timems(now), solver_runs);
-}
-
-void
-test_no_ga_package_fulfills_dependency(context_t *c, Id pid)
-{
- Id con, *conp;
- Solvable *s = pool->solvables + pid;
-
- /* Test 4: no GA package fulfills patch dependency */
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- Reldep *rd;
- Id rp, rpp;
-
- if (!ISRELDEP(con))
- continue;
- rd = GETRELDEP(pool, con);
- FOR_PROVIDES(rp, rpp, rd->name)
- {
- Solvable *s2 = pool_id2solvable(pool, rp);
- if (rp < c->updatestart
- && pool_evrcmp(pool, rd->evr, s2->evr, EVRCMP_COMPARE) < 0
- && pool_match_nevr_rel(pool, s2, rd->name)
- )
- {
- SHOW_PATCH(c);
- printf(" conflict %s < %s satisfied by non-updated package %s\n",
- pool_dep2str(pool, rd->name), pool_dep2str(pool, rd->evr), pool_solvable2str(pool, s2));
- break;
- }
- }
- }
-}
-
-int
-main(int argc, char **argv)
-{
- char *arch, *mypatch;
- const char *pname;
- int l, r;
- FILE *fp;
- int i;
- Id pid, p, pp;
- int tests = 0;
- context_t c;
- static const char* langs[] = {"en"};
-
- c.install_available = 0;
- c.updatestart = 0;
- c.status = 0;
-
- if (argc <= 3)
- usage(argv);
-
- arch = argv[1];
- pool = pool_create();
- pool_setarch(pool, arch);
- pool_set_languages(pool, langs, 1);
-
-#if 0
- pool_setdebuglevel(pool, 2);
-#endif
-
- mypatch = argv[2];
-
- c.repo = repo_create(pool, 0);
- c.instrepo = repo_create(pool, 0);
- for (i = 3; i < argc; i++)
- {
- if (!strcmp(argv[i], "--updaterepos"))
- {
- c.updatestart = pool->nsolvables;
- continue;
- }
-
- if (!strcmp(argv[i], "--install-available"))
- {
- c.install_available = 1;
- continue;
- }
-
- l = strlen(argv[i]);
- if (!strcmp(argv[i], "-"))
- fp = stdin;
- else if ((fp = solv_xfopen(argv[i], 0)) == 0)
- {
- perror(argv[i]);
- exit(1);
- }
- r = 0;
- if (0)
- {
- }
-#ifdef ENABLE_SUSEREPO
- else if (l >= 8 && !strcmp(argv[i] + l - 8, "packages"))
- {
- r = repo_add_susetags(c.repo, fp, 0, 0, 0);
- }
- else if (l >= 11 && !strcmp(argv[i] + l - 11, "packages.gz"))
- {
- r = repo_add_susetags(c.repo, fp, 0, 0, 0);
- }
-#endif
-#ifdef ENABLE_RPMMD
- else if (l >= 14 && !strcmp(argv[i] + l - 14, "primary.xml.gz"))
- {
- r = repo_add_rpmmd(c.repo, fp, 0, 0);
- }
- else if (l >= 17 && !strcmp(argv[i] + l - 17, "updateinfo.xml.gz"))
- {
- r = repo_add_updateinfoxml(c.repo, fp, 0);
- }
-#endif
- else
- r = repo_add_solv(c.repo, fp, 0);
- if (r)
- {
- fprintf(stderr, "could not add repo %s: %s\n", argv[i], pool_errstr(pool));
- exit(1);
- }
- if (fp != stdin)
- fclose(fp);
- }
-
- pool_addfileprovides(pool);
-
- /* bad hack ahead: clone repo */
- c.instrepo->idarraydata = c.repo->idarraydata;
- c.instrepo->idarraysize = c.repo->idarraysize;
- c.instrepo->start = c.repo->start;
- c.instrepo->end = c.repo->end;
- c.instrepo->nsolvables = c.repo->nsolvables; /* sic! */
- pool_set_installed(pool, c.instrepo);
- pool_createwhatprovides(pool);
-
- for (pid = 1; pid < pool->nsolvables; pid++)
- {
- Solvable *s;
- c.shown = 0;
- s = pool->solvables + pid;
- if (!s->repo)
- continue;
- if (!pool_installable(pool, s))
- continue;
- pname = pool_id2str(pool, s->name);
- if (strncmp(pname, "patch:", 6) != 0)
- continue;
-
- if (*mypatch)
- {
- if (strncmp(mypatch, pname + 6, strlen(pname + 6)) != 0)
- continue;
- if (strcmp(mypatch, pname + 6) != 0)
- {
- l = strlen(pname + 6);
- if (mypatch[l] != '-')
- continue;
- if (strcmp(mypatch + l + 1, pool_id2str(pool, s->evr)) != 0)
- continue;
- }
- }
- else
- {
- FOR_PROVIDES(p, pp, s->name)
- {
- Solvable *s2 = pool->solvables + p;
- if (pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) < 0)
- break;
- }
- if (p) {
- /* printf("found a newer one for %s\n", pname+6); */
- continue; /* found a newer one */
- }
- }
- tests++;
- if (!s->conflicts)
- continue;
-
-#if 0
- printf("testing patch %s-%s\n", pname + 6, pool_id2str(pool, s->evr));
-#endif
-
- test_all_old_patches_included(&c, pid);
- test_all_packages_installable(&c, pid);
- test_can_upgrade_all_packages(&c, pid);
- test_no_ga_package_fulfills_dependency(&c, pid);
- }
-
- exit(c.status);
-}
+++ /dev/null
-#! /bin/sh
-# repo2solv
-#
-# give it a directory of a local mirror of a repo and this
-# tries to detect the repo type and generate one SOLV file on stdout
-
-get_DESCRDIR () {
- local d=$(grep '^DESCRDIR' content | sed 's/^DESCRDIR[[:space:]]\+\(.*[^[:space:]]\)[[:space:]]*$/\1/')
- if test -z "$d"; then
- echo suse/setup/descr
- else
- echo ${d}
- fi
-}
-
-test_susetags() {
- if test -s content; then
- DESCR=$(get_DESCRDIR)
- test -d $DESCR
- return $?
- else
- return 1
- fi
-}
-
-repomd_findfile() {
- local t=$1
- local p=$2
- local f
- if test -n "$t" -a -s repomd.xml ; then
- f=`repomdxml2solv -q $t:location < repomd.xml 2>/dev/null`
- f=${f##*/}
- if test -f "$f" ; then
- echo "$f"
- return
- fi
- fi
- if test -f "$p.bz2" ; then
- echo "$p.bz2"
- elif test -f "$p.gz" ; then
- echo "$p.gz"
- elif test -f "$p" ; then
- echo "$p"
- fi
-}
-
-repomd_decompress() {
- case $1 in
- *.gz) gzip -dc "$1" ;;
- *.bz2) bzip2 -dc "$1" ;;
- *.lzma) lzma -dc "$1" ;;
- *.xz) xz -dc "$1" ;;
- *) cat "$1" ;;
- esac
-}
-
-susetags_findfile() {
- if test -s "$1.xz" ; then
- echo "$1.xz"
- elif test -s "$1.lzma" ; then
- echo "$1.lzma"
- elif test -s "$1.bz2" ; then
- echo "$1.bz2"
- elif test -s "$1.gz" ; then
- echo "$1.gz"
- fi
-}
-
-susetags_findfile_cat() {
- if test -s "$1.xz" ; then
- xz -dc "$1.xz"
- elif test -s "$1.lzma" ; then
- lzma -dc "$1.lzma"
- elif test -s "$1.bz2" ; then
- bzip2 -dc "$1.bz2"
- elif test -s "$1.gz" ; then
- gzip -dc "$1.gz"
- elif test -s "$1" ; then
- cat "$1"
- fi
-}
-
-# signal an error if there is a problem
-set -e
-
-LANG=C
-unset CDPATH
-parser_options=${PARSER_OPTIONS:-}
-
-findopt="-prune"
-repotype=
-addautooption=
-
-while true ; do
- if test "$1" = "-o" ; then
- exec > "$2"
- shift
- shift
- elif test "$1" = "-R" ; then
- # recursive
- findopt=
- repotype=plaindir
- shift
- elif test "$1" = "-X" ; then
- addautooption=-X
- shift
- elif test "$1" = "-A" ; then
- shift
- else
- break
- fi
-done
-
-dir="$1"
-cd "$dir" || exit 1
-
-if test -z "$repotype" ; then
- # autodetect repository type
- if test -d repodata -o -f repomd.xml; then
- repotype=rpmmd
- elif test_susetags ; then
- repotype=susetags
- else
- repotype=plaindir
- fi
-fi
-
-if test "$repotype" = rpmmd ; then
- test -d repodata && {
- cd repodata || exit 2
- }
-
- primfile=
- primxml=`repomd_findfile primary primary.xml`
- if test -n "$primxml" -a -s "$primxml" ; then
- primfile=`mktemp` || exit 3
- (
- # fake tag to combine primary.xml and extensions
- # like susedata.xml, other.xml, filelists.xml
- echo '<rpmmd>'
- if test -f $primxml ; then
- repomd_decompress $primxml
- # add a newline
- echo
- fi
- susedataxml=`repomd_findfile susedata susedata.xml`
- if test -f "$susedataxml" ; then
- repomd_decompress "$susedataxml"
- fi
- echo '</rpmmd>'
- ) | sed 's/<?xml[^>]*>//g' | sed '1i\<?xml version="1.0" encoding="UTF-8"?>' | rpmmd2solv $parser_options > $primfile || exit 4
- fi
-
- prodfile=
- prodxml=`repomd_findfile products products.xml`
- if test -z "$prodxml" ; then
- prodxml=`repomd_findfile product product.xml`
- fi
- if test -n "$prodxml" -a -s "$prodxml" ; then
- prodfile=`mktemp` || exit 3
- repomd_decompress "$prodxml" | rpmmd2solv $parser_options > $prodfile || exit 4
- fi
-
- patternfile=
- patternxml=`repomd_findfile 'patterns' patterns.xml`
- if test -n "$patternxml" -a -s "$patternxml" ; then
- patternfile=`mktemp` || exit 3
- repomd_decompress "$patternxml" | rpmmd2solv $parser_options > $patternfile || exit 4
- fi
-
- # This contains repomd.xml
- # for now we only read some keys like timestamp
- repomdfile=
- repomdxml=`repomd_findfile '' repomd.xml`
- if test -n "$repomdxml" -a -s "$repomdxml" ; then
- repomdfile=`mktemp` || exit 3
- repomd_decompress "$repomdxml" | repomdxml2solv $parser_options > $repomdfile || exit 4
- fi
-
- # This contains suseinfo.xml, which is an extension to repomd.xml
- # for now we only read some keys like expiration and products
- suseinfofile=
- suseinfoxml=`repomd_findfile suseinfo suseinfo.xml`
- if test -n "$suseinfoxml" -a -s "$suseinfoxml" ; then
- suseinfofile=`mktemp` || exit 3
- repomd_decompress "$suseinfoxml" | repomdxml2solv $parser_options > $suseinfofile || exit 4
- fi
-
- # This contains a updateinfo.xml* and maybe patches
- updateinfofile=
- updateinfoxml=`repomd_findfile updateinfo updateinfo.xml`
- if test -n "$updateinfoxml" -a -s "$updateinfoxml" ; then
- updateinfofile=`mktemp` || exit 3
- repomd_decompress "$updateinfoxml" | updateinfoxml2solv $parser_options > $updateinfofile || exit 4
- fi
-
- # This contains a deltainfo.xml*
- deltainfofile=
- deltainfoxml=`repomd_findfile deltainfo deltainfo.xml`
- if test -z "$deltainfoxml"; then
- deltainfoxml=`repomd_findfile prestodelta prestodelta.xml`
- fi
- if test -n "$deltainfoxml" -a -s "$deltainfoxml" ; then
- deltainfofile=`mktemp` || exit 3
- repomd_decompress "$deltainfoxml" | deltainfoxml2solv $parser_options > $deltainfofile || exit 4
- fi
-
- # This contains appdata
- appdataxml=
- appdatafile=
- if test -x /usr/bin/appdata2solv ; then
- appdataxml=`repomd_findfile appdata appdata.xml`
- fi
- if test -n "$appdataxml" -a -s "$appdataxml" ; then
- appdatafile=`mktemp` || exit 3
- repomd_decompress "$appdataxml" | appdata2solv $parser_options > $appdatafile || exit 4
- fi
-
- # Now merge primary, patches, updateinfo, and deltainfo
- mergesolv $addautooption $repomdfile $suseinfofile $primfile $prodfile $patternfile $updateinfofile $deltainfofile $appdatafile
- rm -f $repomdfile $suseinfofile $primfile $patternfile $prodfile $updateinfofile $deltainfofile $appdatafile
-
-elif test "$repotype" = susetags ; then
- olddir=`pwd`
- DESCR=$(get_DESCRDIR)
- cd ${DESCR} || exit 2
- appdataxml=
- appdatafile=
- if test -x /usr/bin/appdata2solv ; then
- appdataxml=`susetags_findfile appdata.xml`
- fi
- if test -n "$appdataxml" ; then
- appdatafile=`mktemp` || exit 3
- repomd_decompress "$appdataxml" | appdata2solv $parser_options > $appdatafile || exit 4
- parser_options="-M $appdatafile $parser_options"
- fi
- (
- # First packages
- susetags_findfile_cat packages
-
- # DU
- susetags_findfile_cat packages.DU
-
- # Now default language
- susetags_findfile_cat packages.en
-
- # Now patterns. Not simply those files matching *.pat{,.gz,bz2},
- # but only those mentioned in the file 'patterns'
- if test -f patterns ; then
- for i in `cat patterns`; do
- if test -s "$i" ; then
- repomd_decompress "$i"
- fi
- done
- fi
-
- # Now all other packages.{lang}. Needs to come last as it switches
- # languages for all following susetags files
- for i in packages.* ; do
- case $i in
- *.gz|*.bz2|*.xz|*.lzma) name="${i%.*}" ;;
- *) name="$i" ;;
- esac
- case $name in
- # ignore files we handled already
- *.DU | *.en | *.FL | packages ) continue ;;
- *)
- suff=${name#packages.}
- echo "=Lan: $suff"
- repomd_decompress "$i"
- esac
- done
-
- ) | susetags2solv $addautooption -c "${olddir}/content" $parser_options || exit 4
- test -n "$appdatafile" && rm -f "$appdatafile"
- cd "$olddir"
-elif test "$repotype" = plaindir ; then
- find * -name .\* -prune -o $findopt -name \*.delta.rpm -o -name \*.patch.rpm -o -name \*.rpm -a -type f -print0 | rpms2solv $addautooption -0 -m -
-else
- echo "unknown repository type '$repotype'" >&2
- exit 1
-fi
+++ /dev/null
-/*
- * Copyright (c) 2007-2009, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "chksum.h"
-#include "repo_repomdxml.h"
-#include "common_write.h"
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "repomdxml2solv [-q query]\n"
- " reads a 'repomd.xml' file from <stdin> and writes a .solv file to <stdout>\n"
- " -q : query a repomd data entry\n"
- " -h : print help & exit\n"
- );
- exit(status);
-}
-
-static void
-doquery(Pool *pool, Repo *repo, const char *query)
-{
- Id id, type = 0;
- char qbuf[256];
- const char *qp;
- Dataiterator di;
-
- qp = strchr(query, ':');
- if (qp)
- {
- type = pool_strn2id(pool, query, qp - query, 0);
- if (!type)
- exit(0);
- qp++;
- }
- else
- qp = query;
- snprintf(qbuf, sizeof(qbuf), "repository:repomd:%s", qp);
- id = pool_str2id(pool, qbuf, 0);
- if (!id)
- exit(0);
- dataiterator_init(&di, pool, repo, SOLVID_META, id, 0, 0);
- dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
- while (dataiterator_step(&di))
- {
- if (type)
- {
- dataiterator_setpos_parent(&di);
- if (pool_lookup_id(pool, SOLVID_POS, REPOSITORY_REPOMD_TYPE) != type)
- continue;
- }
- switch (di.key->type)
- {
- case REPOKEY_TYPE_ID:
- case REPOKEY_TYPE_CONSTANTID:
- printf("%s\n", pool_id2str(pool, di.kv.id));
- break;
- case REPOKEY_TYPE_STR:
- printf("%s\n", di.kv.str);
- break;
- case REPOKEY_TYPE_NUM:
- case REPOKEY_TYPE_CONSTANT:
- printf("%llu\n", SOLV_KV_NUM64(&di.kv));
- break;
- default:
- if (solv_chksum_len(di.key->type))
- printf("%s:%s\n", solv_chksum_type2str(di.key->type), repodata_chk2str(di.data, di.key->type, (unsigned char *)di.kv.str));
- break;
- }
- }
- dataiterator_free(&di);
-}
-
-int
-main(int argc, char **argv)
-{
- int c, flags = 0;
- const char *query = 0;
-
- Pool *pool = pool_create();
- Repo *repo = repo_create(pool, "<stdin>");
-
- while ((c = getopt (argc, argv, "hq:")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- case 'q':
- query = optarg;
- break;
- default:
- usage(1);
- break;
- }
- }
- if (repo_add_repomdxml(repo, stdin, flags))
- {
- fprintf(stderr, "repomdxml2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- if (query)
- doquery(pool, repo, query);
- else
- tool_write(repo, 0, 0);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * rpmdb2solv
- *
- * Reads rpm database (and evtl. more, like product metadata) to build
- * a .solv file of 'installed' solvables.
- * Writes .solv to stdout
- *
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_rpmdb.h"
-#ifdef ENABLE_PUBKEY
-#include "repo_pubkey.h"
-#endif
-#include "repo_products.h"
-#include "repo_solv.h"
-#include "common_write.h"
-#ifdef ENABLE_APPDATA
-#include "repo_appdata.h"
-#endif
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "rpmdb2solv [-n] [-b <basefile>] [-p <productsdir>] [-r <root>]\n"
- " -n : No packages, do not read rpmdb, useful to only parse products\n"
- " -b <basefile> : Write .solv to <basefile>.solv instead of stdout\n"
- " -p <productsdir> : Scan <productsdir> for .prod files, representing installed products\n"
- " -r <root> : Prefix rpmdb path and <productsdir> with <root>\n"
- " -o <solv> : Write .solv to file instead of stdout\n"
- );
- exit(status);
-}
-
-
-int
-main(int argc, char **argv)
-{
- FILE *reffp = 0;
- Pool *pool = pool_create();
- Repo *repo;
- Repodata *data;
- int c, percent = 0;
- int nopacks = 0;
- const char *root = 0;
- const char *basefile = 0;
- const char *refname = 0;
-#ifdef ENABLE_SUSEREPO
- char *proddir = 0;
-#endif
- char *outfile = 0;
-#ifdef ENABLE_PUBKEY
- int pubkeys = 0;
-#endif
-#ifdef ENABLE_APPDATA
- int add_appdata = 0;
-#endif
-#ifdef SUSE
- int add_auto = 0;
-#endif
-
- /*
- * parse arguments
- */
-
- while ((c = getopt(argc, argv, "APhnkxXb:r:p:o:")) >= 0)
- switch (c)
- {
- case 'h':
- usage(0);
- break;
- case 'r':
- root = optarg;
- break;
- case 'b':
- basefile = optarg;
- break;
- case 'n':
- nopacks = 1;
- break;
- case 'P':
- percent = 1;
- break;
- case 'p':
-#ifdef ENABLE_SUSEREPO
- proddir = optarg;
-#endif
- break;
- case 'x':
- break; /* extrapool no longer supported */
- case 'X':
-#ifdef SUSE
- add_auto = 1;
-#endif
- break;
- case 'A':
-#ifdef ENABLE_APPDATA
- add_appdata = 1;
-#endif
- break;
- case 'o':
- outfile = optarg;
- break;
-#ifdef ENABLE_PUBKEY
- case 'k':
- nopacks = 1;
- pubkeys = 1;
- break;
-#endif
- default:
- usage(1);
- }
-
- if (outfile && !freopen(outfile, "w", stdout))
- {
- perror(outfile);
- exit(1);
- }
-
- /*
- * optional arg is old version of rpmdb solv file
- * should make this a real option instead
- */
-
- if (optind < argc)
- refname = argv[optind];
-
- if (refname && !nopacks)
- {
- if ((reffp = fopen(refname, "r")) == NULL)
- perror(refname);
- }
-
- /*
- * create 'installed' repository
- * add products
- * add rpmdb
- * write .solv
- */
-
- if (root && *root)
- pool_set_rootdir(pool, root);
-
- repo = repo_create(pool, "installed");
- data = repo_add_repodata(repo, 0);
-
- if (!nopacks)
- {
- if (repo_add_rpmdb_reffp(repo, reffp, REPO_USE_ROOTDIR | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | (percent ? RPMDB_REPORT_PROGRESS : 0)))
- {
- fprintf(stderr, "rpmdb2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
-#ifdef ENABLE_PUBKEY
- if (pubkeys)
- {
- if (repo_add_rpmdb_pubkeys(repo, REPO_USE_ROOTDIR | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | ADD_WITH_KEYSIGNATURES))
- {
- fprintf(stderr, "rpmdb2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
-#endif
-
-#ifdef ENABLE_SUSEREPO
- if (proddir && *proddir)
- {
- if (root && *root)
- {
- int rootlen = strlen(root);
- if (!strncmp(root, proddir, rootlen))
- {
- proddir += rootlen;
- if (*proddir != '/' && proddir[-1] == '/')
- proddir--;
- }
- }
- if (repo_add_products(repo, proddir, REPO_USE_ROOTDIR | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "rpmdb2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
-#endif
-
-#ifdef ENABLE_APPDATA
- if (add_appdata)
- repo_add_appdata_dir(repo, "/usr/share/appdata", REPO_USE_ROOTDIR | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | APPDATA_SEARCH_UNINTERNALIZED_FILELIST);
-#endif
- repodata_internalize(data);
-
- if (reffp)
- fclose(reffp);
-
-#ifdef SUSE
- if (add_auto)
- repo_add_autopattern(repo, ADD_NO_AUTOPRODUCTS);
-#endif
-
- tool_write(repo, basefile, 0);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define _GNU_SOURCE
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <zlib.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_rpmmd.h"
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-#include "common_write.h"
-#include "solv_xfopen.h"
-
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "rpmmd2solv [-a][-h][-n <attrname>][-l <locale>]\n"
- " reads 'primary' from a 'rpmmd' repository from <stdin> and writes a .solv file to <stdout>\n"
- " -h : print help & exit\n"
- " -n <name>: save attributes as <name>.attr\n"
- " -l <locale>: parse localization data for <locale>\n"
- );
- exit(status);
-}
-
-int
-main(int argc, char **argv)
-{
- int c, flags = 0;
- const char *attrname = 0;
- const char *basefile = 0;
- const char *dir = 0;
- const char *locale = 0;
-#ifdef SUSE
- int add_auto = 0;
-#endif
-
- Pool *pool = pool_create();
- Repo *repo = repo_create(pool, "<stdin>");
-
- while ((c = getopt (argc, argv, "hn:b:d:l:X")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- case 'n':
- attrname = optarg;
- break;
- case 'b':
- basefile = optarg;
- break;
- case 'd':
- dir = optarg;
- break;
- case 'l':
- locale = optarg;
- break;
- case 'X':
-#ifdef SUSE
- add_auto = 1;
-#endif
- break;
- default:
- usage(1);
- break;
- }
- }
- if (dir)
- {
- FILE *fp;
- int l;
- char *fnp;
- l = strlen(dir) + 128;
- fnp = solv_malloc(l+1);
- snprintf(fnp, l, "%s/primary.xml.gz", dir);
- if (!(fp = solv_xfopen(fnp, 0)))
- {
- perror(fnp);
- exit(1);
- }
- if (repo_add_rpmmd(repo, fp, 0, flags))
- {
- fprintf(stderr, "rpmmd2solv: %s: %s\n", fnp, pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- snprintf(fnp, l, "%s/diskusagedata.xml.gz", dir);
- if ((fp = solv_xfopen(fnp, 0)))
- {
- if (repo_add_rpmmd(repo, fp, 0, flags))
- {
- fprintf(stderr, "rpmmd2solv: %s: %s\n", fnp, pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
- if (locale)
- {
- if (snprintf(fnp, l, "%s/translation-%s.xml.gz", dir, locale) >= l)
- {
- fprintf(stderr, "-l parameter too long\n");
- exit(1);
- }
- while (!(fp = solv_xfopen(fnp, 0)))
- {
- fprintf(stderr, "not opened %s\n", fnp);
- if (strlen(locale) > 2)
- {
- if (snprintf(fnp, l, "%s/translation-%.2s.xml.gz", dir, locale) >= l)
- {
- fprintf(stderr, "-l parameter too long\n");
- exit(1);
- }
- if ((fp = solv_xfopen(fnp, 0)))
- break;
- }
- perror(fnp);
- exit(1);
- }
- fprintf(stderr, "opened %s\n", fnp);
- if (repo_add_rpmmd(repo, fp, 0, flags))
- {
- fprintf(stderr, "rpmmd2solv: %s: %s\n", fnp, pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
- solv_free(fnp);
- }
- else
- {
- if (repo_add_rpmmd(repo, stdin, 0, flags))
- {
- fprintf(stderr, "rpmmd2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
-#ifdef SUSE
- if (add_auto)
- repo_add_autopattern(repo, 0);
-#endif
- tool_write(repo, basefile, attrname);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/*
- * rpms2solv - create a solv file from multiple rpms
- *
- */
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "util.h"
-#include "pool.h"
-#include "repo.h"
-#include "repo_rpmdb.h"
-#ifdef ENABLE_PUBKEY
-#include "repo_pubkey.h"
-#include "solv_xfopen.h"
-#endif
-#include "repo_solv.h"
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-#include "common_write.h"
-
-static char *
-fgets0(char *s, int size, FILE *stream)
-{
- char *p = s;
- int c;
-
- while (--size > 0)
- {
- c = getc(stream);
- if (c == EOF)
- {
- if (p == s)
- return 0;
- c = 0;
- }
- *p++ = c;
- if (!c)
- return s;
- }
- *p = 0;
- return s;
-}
-
-int
-main(int argc, char **argv)
-{
- const char **rpms = 0;
- char *manifest = 0;
- int manifest0 = 0;
- int c, i, res, nrpms = 0;
- Pool *pool = pool_create();
- Repo *repo;
- FILE *fp;
- char buf[4096], *p;
- const char *basefile = 0;
-#ifdef ENABLE_PUBKEY
- int pubkeys = 0;
-#endif
-#ifdef SUSE
- int add_auto = 0;
-#endif
- int filtered_filelist = 0;
-
- while ((c = getopt(argc, argv, "0XkKb:m:F")) >= 0)
- {
- switch(c)
- {
- case 'b':
- basefile = optarg;
- break;
- case 'm':
- manifest = optarg;
- break;
- case '0':
- manifest0 = 1;
- break;
- case 'F':
- filtered_filelist = 1;
- break;
-#ifdef ENABLE_PUBKEY
- case 'k':
- pubkeys = 1;
- break;
- case 'K':
- pubkeys = 2;
- break;
-#endif
- case 'X':
-#ifdef SUSE
- add_auto = 1;
-#endif
- break;
- default:
- exit(1);
- }
- }
- if (manifest)
- {
- if (!strcmp(manifest, "-"))
- fp = stdin;
- else if ((fp = fopen(manifest, "r")) == 0)
- {
- perror(manifest);
- exit(1);
- }
- for (;;)
- {
- if (manifest0)
- {
- if (!fgets0(buf, sizeof(buf), fp))
- break;
- }
- else
- {
- if (!fgets(buf, sizeof(buf), fp))
- break;
- if ((p = strchr(buf, '\n')) != 0)
- *p = 0;
- }
- rpms = solv_extend(rpms, nrpms, 1, sizeof(char *), 15);
- rpms[nrpms++] = strdup(buf);
- }
- if (fp != stdin)
- fclose(fp);
- }
- while (optind < argc)
- {
- rpms = solv_extend(rpms, nrpms, 1, sizeof(char *), 15);
- rpms[nrpms++] = strdup(argv[optind++]);
- }
- repo = repo_create(pool, "rpms2solv");
- repo_add_repodata(repo, 0);
- res = 0;
- for (i = 0; i < nrpms; i++)
- {
-#ifdef ENABLE_PUBKEY
- if (pubkeys == 2)
- {
- FILE *fp = solv_xfopen(rpms[i], "r");
- if (!fp)
- {
- perror(rpms[i]);
- res = 1;
- continue;
- }
- if (repo_add_keyring(repo, fp, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|ADD_WITH_KEYSIGNATURES))
- {
- fprintf(stderr, "rpms2solv: %s\n", pool_errstr(pool));
- res = 1;
- }
- fclose(fp);
- continue;
- }
- if (pubkeys)
- {
- if (repo_add_pubkey(repo, rpms[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|ADD_WITH_KEYSIGNATURES) == 0)
- {
- fprintf(stderr, "rpms2solv: %s\n", pool_errstr(pool));
- res = 1;
- }
- continue;
- }
-#endif
- if (repo_add_rpm(repo, rpms[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|(filtered_filelist ? RPM_ADD_FILTERED_FILELIST : 0)) == 0)
- {
- fprintf(stderr, "rpms2solv: %s\n", pool_errstr(pool));
- res = 1;
- }
- }
- repo_internalize(repo);
-#ifdef SUSE
- if (add_auto)
- repo_add_autopattern(repo, 0);
-#endif
- tool_write(repo, basefile, 0);
- pool_free(pool);
- for (c = 0; c < nrpms; c++)
- free((char *)rpms[c]);
- solv_free(rpms);
- exit(res);
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#define _GNU_SOURCE
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <zlib.h>
-#include <getopt.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_solv.h"
-#include "repo_susetags.h"
-#include "repo_content.h"
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-#include "common_write.h"
-#include "solv_xfopen.h"
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "susetags2solv [-b <base>][-c <content>][-d <descrdir>][-h][-n <name>]\n"
- " reads a 'susetags' repository from <stdin> and writes a .solv file to <stdout>\n"
- " -b <base>: save as multiple files starting with <base>\n"
- " -c <contentfile> : parse given contentfile (for product information)\n"
- " -d <descrdir> : do not read from stdin, but use data in descrdir\n"
- " -h : print help & exit\n"
- " -n <name>: save attributes as <name>.attr\n"
- );
- exit(status);
-}
-
-/* content file query */
-static void
-doquery(Pool *pool, Repo *repo, const char *arg)
-{
- char qbuf[256];
- const char *str;
- Id id;
-
- snprintf(qbuf, sizeof(qbuf), "susetags:%s", arg);
- id = pool_str2id(pool, qbuf, 0);
- if (!id)
- return;
- str = repo_lookup_str(repo, SOLVID_META, id);
- if (str)
- printf("%s\n", str);
-}
-
-int
-main(int argc, char **argv)
-{
- const char *contentfile = 0;
- const char *attrname = 0;
- const char *descrdir = 0;
- const char *basefile = 0;
- const char *query = 0;
- const char *mergefile = 0;
- Id defvendor = 0;
- int flags = 0;
-#ifdef SUSE
- int add_auto = 0;
-#endif
- int c;
- Pool *pool;
- Repo *repo;
-
- while ((c = getopt(argc, argv, "hn:c:d:b:q:M:X")) >= 0)
- {
- switch (c)
- {
- case 'h':
- usage(0);
- break;
- case 'n':
- attrname = optarg;
- break;
- case 'c':
- contentfile = optarg;
- break;
- case 'd':
- descrdir = optarg;
- break;
- case 'b':
- basefile = optarg;
- break;
- case 'q':
- query = optarg;
- break;
- case 'M':
- mergefile = optarg;
- break;
- case 'X':
-#ifdef SUSE
- add_auto = 1;
-#endif
- break;
- default:
- usage(1);
- break;
- }
- }
- pool = pool_create();
- repo = repo_create(pool, "<susetags>");
-
- repo_add_repodata(repo, 0);
-
- if (contentfile)
- {
- FILE *fp = fopen(contentfile, "r");
- if (!fp)
- {
- perror(contentfile);
- exit(1);
- }
- if (repo_add_content(repo, fp, REPO_REUSE_REPODATA))
- {
- fprintf(stderr, "susetags2solv: %s: %s\n", contentfile, pool_errstr(pool));
- exit(1);
- }
- defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
- fclose(fp);
- }
-
- if (attrname)
- {
- /* ensure '.attr' suffix */
- const char *dot = strrchr(attrname, '.');
- if (!dot || strcmp(dot, ".attr"))
- {
- int len = strlen (attrname);
- char *newname = (char *)malloc(len + 6); /* alloc for <attrname>+'.attr'+'\0' */
- strcpy (newname, attrname);
- strcpy (newname+len, ".attr");
- attrname = newname;
- }
- }
-
- /*
- * descrdir path given, open files and read from there
- */
-
- if (descrdir)
- {
- char *fnp;
- int ndirs, i;
- struct dirent **files;
-
- ndirs = scandir(descrdir, &files, 0, alphasort);
- if (ndirs < 0)
- {
- perror(descrdir);
- exit(1);
- }
-
- /* bring packages to front */
- for (i = 0; i < ndirs; i++)
- {
- char *fn = files[i]->d_name;
- if (!strcmp(fn, "packages") || !strcmp(fn, "packages.gz"))
- break;
- }
- if (i == ndirs)
- {
- fprintf(stderr, "found no packages file\n");
- exit(1);
- }
- if (i)
- {
- struct dirent *de = files[i];
- memmove(files + 1, files, i * sizeof(de));
- files[0] = de;
- }
-
- fnp = solv_malloc(strlen(descrdir) + 128);
- for (i = 0; i < ndirs; i++)
- {
- char *fn = files[i]->d_name;
-
- if (!strcmp(fn, "packages") || !strcmp(fn, "packages.gz"))
- {
- FILE *fp;
- sprintf(fnp, "%s/%s", descrdir, fn);
- fp = solv_xfopen(fnp, 0);
- if (!fp)
- {
- perror(fn);
- exit(1);
- }
- if (repo_add_susetags(repo, fp, defvendor, 0, flags | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "susetags2solv: %s: %s\n", fnp, pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
- else if (!strcmp(fn, "packages.DU") || !strcmp(fn, "packages.DU.gz"))
- {
- FILE *fp;
- sprintf(fnp, "%s/%s", descrdir, fn);
- fp = solv_xfopen(fnp, 0);
- if (!fp)
- {
- perror(fn);
- exit(1);
- }
- if (repo_add_susetags(repo, fp, defvendor, 0, flags | SUSETAGS_EXTEND | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "susetags2solv: %s: %s\n", fnp, pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
- else if (!strcmp(fn, "packages.FL") || !strcmp(fn, "packages.FL.gz"))
- {
-#if 0
- sprintf(fnp, "%s/%s", descrdir, fn);
- FILE *fp = solv_xfopen(fnp, 0);
- if (!fp)
- {
- perror(fn);
- exit(1);
- }
- if (repo_add_susetags(repo, fp, defvendor, 0, flags | SUSETAGS_EXTEND | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "susetags2solv: %s: %s\n", fnp, pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
-#else
- /* ignore for now. reactivate when filters work */
- continue;
-#endif
- }
- else if (!strncmp(fn, "packages.", 9))
- {
- char lang[6];
- char *p;
- FILE *fp;
- sprintf(fnp, "%s/%s", descrdir, fn);
- p = strrchr(fnp, '.');
- if (p && !strcmp(p, ".gz"))
- {
- *p = 0;
- p = strrchr(fnp, '.');
- }
- if (!p || !p[1] || strlen(p + 1) > 5)
- continue;
- strcpy(lang, p + 1);
- sprintf(fnp, "%s/%s", descrdir, fn);
- fp = solv_xfopen(fnp, 0);
- if (!fp)
- {
- perror(fn);
- exit(1);
- }
- if (repo_add_susetags(repo, fp, defvendor, lang, flags | SUSETAGS_EXTEND | REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "susetags2solv: %s: %s\n", fnp, pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
- }
- for (i = 0; i < ndirs; i++)
- free(files[i]);
- free(files);
- free(fnp);
- repo_internalize(repo);
- }
- else
- {
- /* read data from stdin */
- if (repo_add_susetags(repo, stdin, defvendor, 0, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE))
- {
- fprintf(stderr, "susetags2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- }
- repo_internalize(repo);
- if (mergefile)
- {
- FILE *fp = fopen(mergefile, "r");
- if (!fp)
- {
- perror(mergefile);
- exit(1);
- }
- if (repo_add_solv(repo, fp, 0))
- {
- fprintf(stderr, "susetags2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- fclose(fp);
- }
-#ifdef SUSE
- if (add_auto)
- repo_add_autopattern(repo, 0);
-#endif
-
- if (query)
- doquery(pool, repo, query);
- else
- tool_write(repo, basefile, attrname);
- pool_free(pool);
- exit(0);
-}
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "solver.h"
-#include "selection.h"
-#include "solverdebug.h"
-#include "testcase.h"
-
-static struct resultflags2str {
- Id flag;
- const char *str;
-} resultflags2str[] = {
- { TESTCASE_RESULT_TRANSACTION, "transaction" },
- { TESTCASE_RESULT_PROBLEMS, "problems" },
- { TESTCASE_RESULT_ORPHANED, "orphaned" },
- { TESTCASE_RESULT_RECOMMENDED, "recommended" },
- { TESTCASE_RESULT_UNNEEDED, "unneeded" },
- { TESTCASE_RESULT_ALTERNATIVES, "alternatives" },
- { TESTCASE_RESULT_RULES, "rules" },
- { TESTCASE_RESULT_GENID, "genid" },
- { 0, 0 }
-};
-
-static void
-usage(int ex)
-{
- fprintf(ex ? stderr : stdout, "Usage: testsolv <testcase>\n");
- exit(ex);
-}
-
-struct reportsolutiondata {
- int count;
- char *result;
-};
-
-static int
-reportsolutioncb(Solver *solv, void *cbdata)
-{
- struct reportsolutiondata *sd = cbdata;
- char *res;
-
- sd->count++;
- res = testcase_solverresult(solv, TESTCASE_RESULT_TRANSACTION);
- if (*res)
- {
- char prefix[64];
- char *p2, *p = res;
- sprintf(prefix, "callback%d:", sd->count);
- while ((p2 = strchr(p, '\n')) != 0)
- {
- char c = p2[1];
- p2[1] = 0;
- sd->result = solv_dupappend(sd->result, prefix, p);
- p2[1] = c;
- p = p2 + 1;
- }
- }
- solv_free(res);
- return 0;
-}
-
-int
-main(int argc, char **argv)
-{
- Pool *pool;
- Queue job;
- Queue solq;
- Solver *solv;
- char *result = 0;
- int resultflags = 0;
- int debuglevel = 0;
- int writeresult = 0;
- char *writetestcase = 0;
- int multijob = 0;
- int rescallback = 0;
- int c;
- int ex = 0;
- const char *list = 0;
- FILE *fp;
- const char *p;
-
- queue_init(&solq);
- while ((c = getopt(argc, argv, "vmrhl:s:T:")) >= 0)
- {
- switch (c)
- {
- case 'v':
- debuglevel++;
- break;
- case 'r':
- writeresult++;
- break;
- case 'm':
- rescallback = 1;
- break;
- case 'h':
- usage(0);
- break;
- case 'l':
- list = optarg;
- break;
- case 's':
- if ((p = strchr(optarg, ':')))
- queue_push2(&solq, atoi(optarg), atoi(p + 1));
- else
- queue_push2(&solq, 1, atoi(optarg));
- break;
- case 'T':
- writetestcase = optarg;
- break;
- default:
- usage(1);
- break;
- }
- }
- if (optind == argc)
- usage(1);
- for (; optind < argc; optind++)
- {
- pool = pool_create();
- pool_setdebuglevel(pool, debuglevel);
-
- fp = fopen(argv[optind], "r");
- if (!fp)
- {
- perror(argv[optind]);
- exit(0);
- }
- while (!feof(fp))
- {
- queue_init(&job);
- result = 0;
- resultflags = 0;
- solv = testcase_read(pool, fp, argv[optind], &job, &result, &resultflags);
- if (!solv)
- {
- pool_free(pool);
- exit(resultflags == 77 ? 77 : 1);
- }
-
- if (!multijob && !feof(fp))
- multijob = 1;
-
- if (multijob)
- printf("test %d:\n", multijob++);
- if (list)
- {
- int selflags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL|SELECTION_GLOB|SELECTION_FLAT;
- if (*list == '/')
- selflags |= SELECTION_FILELIST;
- queue_empty(&job);
- selection_make(pool, &job, list, selflags);
- if (!job.elements)
- printf("No match\n");
- else
- {
- Queue q;
- int i;
- queue_init(&q);
- selection_solvables(pool, &job, &q);
- for (i = 0; i < q.count; i++)
- printf(" - %s\n", testcase_solvid2str(pool, q.elements[i]));
- queue_free(&q);
- }
- }
- else if (result || writeresult)
- {
- char *myresult, *resultdiff;
- struct reportsolutiondata reportsolutiondata;
- memset(&reportsolutiondata, 0, sizeof(reportsolutiondata));
- if (rescallback)
- {
- solv->solution_callback = reportsolutioncb;
- solv->solution_callback_data = &reportsolutiondata;
- }
- solver_solve(solv, &job);
- solv->solution_callback = 0;
- solv->solution_callback_data = 0;
- if (!resultflags)
- resultflags = TESTCASE_RESULT_TRANSACTION | TESTCASE_RESULT_PROBLEMS;
- myresult = testcase_solverresult(solv, resultflags);
- if (rescallback && reportsolutiondata.result)
- {
- reportsolutiondata.result = solv_dupjoin(reportsolutiondata.result, myresult, 0);
- solv_free(myresult);
- myresult = reportsolutiondata.result;
- }
- if (writeresult)
- {
- if (*myresult)
- {
- if (writeresult > 1)
- {
- const char *p;
- int i;
-
- printf("result ");
- p = "%s";
- for (i = 0; resultflags2str[i].str; i++)
- if ((resultflags & resultflags2str[i].flag) != 0)
- {
- printf(p, resultflags2str[i].str);
- p = ",%s";
- }
- printf(" <inline>\n");
- p = myresult;
- while (*p)
- {
- const char *p2 = strchr(p, '\n');
- p2 = p2 ? p2 + 1 : p + strlen(p);
- printf("#>%.*s", (int)(p2 - p), p);
- p = p2;
- }
- }
- else
- printf("%s", myresult);
- }
- }
- else
- {
- resultdiff = testcase_resultdiff(result, myresult);
- if (resultdiff)
- {
- printf("Results differ:\n%s", resultdiff);
- ex = 1;
- solv_free(resultdiff);
- }
- }
- solv_free(result);
- solv_free(myresult);
- }
- else
- {
- int pcnt = solver_solve(solv, &job);
- if (writetestcase)
- testcase_write(solv, writetestcase, resultflags, 0, 0);
- if (pcnt && solq.count)
- {
- int i, taken = 0;
- for (i = 0; i < solq.count; i += 2)
- {
- if (solq.elements[i] > 0 && solq.elements[i] <= pcnt)
- if (solq.elements[i + 1] > 0 && solq.elements[i + 1] <= solver_solution_count(solv, solq.elements[i]))
- {
- printf("problem %d: taking solution %d\n", solq.elements[i], solq.elements[i + 1]);
- solver_take_solution(solv, solq.elements[i], solq.elements[i + 1], &job);
- taken = 1;
- }
- }
- if (taken)
- pcnt = solver_solve(solv, &job);
- }
- if (pcnt)
- {
- int problem, solution, scnt;
- printf("Found %d problems:\n", pcnt);
- for (problem = 1; problem <= pcnt; problem++)
- {
- printf("Problem %d:\n", problem);
-#if 1
- solver_printprobleminfo(solv, problem);
-#else
- {
- Queue pq;
- int j;
- queue_init(&pq);
- solver_findallproblemrules(solv, problem, &pq);
- for (j = 0; j < pq.count; j++)
- solver_printproblemruleinfo(solv, pq.elements[j]);
- queue_free(&pq);
- }
-#endif
- printf("\n");
- scnt = solver_solution_count(solv, problem);
- for (solution = 1; solution <= scnt; solution++)
- {
- printf("Solution %d:\n", solution);
- solver_printsolution(solv, problem, solution);
- printf("\n");
- }
- }
- }
- else
- {
- Transaction *trans = solver_create_transaction(solv);
- printf("Transaction summary:\n\n");
- transaction_print(trans);
- transaction_free(trans);
- }
- }
- queue_free(&job);
- solver_free(solv);
- }
- pool_free(pool);
- fclose(fp);
- }
- queue_free(&solq);
- exit(ex);
-}
+++ /dev/null
-/*
- * Copyright (c) 2007, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-#include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "pool.h"
-#include "repo.h"
-#include "repo_updateinfoxml.h"
-#include "common_write.h"
-
-static void
-usage(int status)
-{
- fprintf(stderr, "\nUsage:\n"
- "updateinfoxml2solv [-h][-n <attrname>]\n"
- " reads a 'updateinfo.xml' file from <stdin> and writes a .solv file to <stdout>\n"
- " -h : print help & exit\n"
- " -n <name>: save attributes as <name>.attr\n"
- );
- exit(status);
-}
-
-int
-main(int argc, char **argv)
-{
- int c, flags = 0;
- char *attrname = 0;
-
- Pool *pool = pool_create();
- Repo *repo = repo_create(pool, "<stdin>");
-
- while ((c = getopt(argc, argv, "hn:")) >= 0)
- {
- switch(c)
- {
- case 'h':
- usage(0);
- break;
- case 'n':
- attrname = optarg;
- break;
- default:
- usage(1);
- break;
- }
- }
- if (repo_add_updateinfoxml(repo, stdin, flags))
- {
- fprintf(stderr, "updateinfoxml2solv: %s\n", pool_errstr(pool));
- exit(1);
- }
- tool_write(repo, 0, attrname);
- pool_free(pool);
- exit(0);
-}
--- /dev/null
+BSSolv::pool T_PTROBJ
+BSSolv::repo T_PTROBJ
+BSSolv::expander T_PTROBJ