From 39aae7129a209182995fb5abffb323c76cb041ff Mon Sep 17 00:00:00 2001 From: SoonKyu Park Date: Mon, 20 Feb 2017 10:03:39 +0900 Subject: [PATCH] Imported vendor release 0.28.0-2.1 --- packaging/BSSolv.pm | 17 + packaging/BSSolv.xs | 5489 ++++++++++++++++++++++++++++++++++++++++++++ packaging/Makefile.PL | 21 + packaging/perl-BSSolv.spec | 250 ++ packaging/typemap | 3 + 5 files changed, 5780 insertions(+) create mode 100644 packaging/BSSolv.pm create mode 100644 packaging/BSSolv.xs create mode 100644 packaging/Makefile.PL create mode 100644 packaging/perl-BSSolv.spec create mode 100644 packaging/typemap diff --git a/packaging/BSSolv.pm b/packaging/BSSolv.pm new file mode 100644 index 0000000..82db1e1 --- /dev/null +++ b/packaging/BSSolv.pm @@ -0,0 +1,17 @@ +package BSSolv; + +use strict; + +require Exporter; + +our @ISA = qw(Exporter); + +our $VERSION = '0.08'; + +require XSLoader; + +XSLoader::load('BSSolv', $VERSION); + +package BSSolv::repo; + +1; diff --git a/packaging/BSSolv.xs b/packaging/BSSolv.xs new file mode 100644 index 0000000..3f9423c --- /dev/null +++ b/packaging/BSSolv.xs @@ -0,0 +1,5489 @@ +#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 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); diff --git a/packaging/Makefile.PL b/packaging/Makefile.PL new file mode 100644 index 0000000..ea70fa2 --- /dev/null +++ b/packaging/Makefile.PL @@ -0,0 +1,21 @@ +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 ], +) diff --git a/packaging/perl-BSSolv.spec b/packaging/perl-BSSolv.spec new file mode 100644 index 0000000..6f80a25 --- /dev/null +++ b/packaging/perl-BSSolv.spec @@ -0,0 +1,250 @@ +# +# spec file for package perl-BSSolv +# +# Copyright (c) 2016 SUSE LINUX 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: perl-BSSolv +Version: 0.28.0 +Release: 2.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 +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%if 0%{?mandriva_version} +# force this version on mandriva +BuildRequires: libneon0.26-devel +%endif +%if 0%{?fedora_version} +BuildRequires: db4-devel +BuildRequires: perl-devel +%endif +%if 0%{?suse_version} +Requires: perl = %perl_version +%if 0%{?suse_version} < 1030 +BuildRequires: expat +%else +BuildRequires: libexpat-devel +%endif +%else +BuildRequires: expat-devel +%endif +%if 0%{?rhel_version} || 0%{?centos_version} +BuildRequires: db4-devel +%endif +BuildRequires: cmake +BuildRequires: gcc-c++ +BuildRequires: perl +BuildRequires: rpm-devel +BuildRequires: xz-devel +BuildRequires: zlib-devel +#RHEL6 moved ExtUtils::MakeMaker outside the main perl package +BuildRequires: perl(ExtUtils::MakeMaker) +# the testsuite uses the check framework +BuildRequires: check-devel +Summary: A new approach to package dependency solving +License: BSD-3-Clause +Group: Development/Libraries/C and C++ +# probably needed for rhel/centos on x86_64 +%if 0%{!?perl_vendorarch} +%define perl_vendorarch %(eval "`%{__perl} -V:installvendorarch`"; echo $installvendorarch) +%endif + +%description +Using a Satisfyability Solver to compute package dependencies. + +%prep +%setup -c +ln -s libsolv-* libsolv +cp %{SOURCE1} %{SOURCE2} %{SOURCE3} %{SOURCE4} . +pushd libsolv +popd + +%build +export CFLAGS="$RPM_OPT_FLAGS" +export CXXFLAGS="$CFLAGS" + +CMAKE_FLAGS= +%if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version} +CMAKE_FLAGS="-DFEDORA=1" +%endif + +%if 0%{?rhel_version} || 0%{?centos_version} +CFLAGS="$CFLAGS -DUSE_OWN_QSORT" +%endif + +pushd libsolv +cmake $CMAKE_FLAGS \ + -DDISABLE_SHARED=1 \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_SKIP_RPATH=1 \ + -DENABLE_RPMDB=1 \ + -DENABLE_DEBIAN=1 \ + -DENABLE_ARCHREPO=1 \ + -DENABLE_LZMA_COMPRESSION=1 \ + -DMULTI_SEMANTICS=1 +pushd src ; make ; popd +pushd ext ; make ; popd +popd +perl Makefile.PL --bundled-libsolv +make + +%install +make DESTDIR=$RPM_BUILD_ROOT install_vendor +%if 0%{?suse_version} +%perl_process_packlist +%else +find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} \; +find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} \; +find $RPM_BUILD_ROOT -type f -name '*.bs' -a -size 0 -exec rm -f {} ';' +find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null \; +%{_fixperms} $RPM_BUILD_ROOT/* +%endif + +%files +%defattr(-,root,root) +%{perl_vendorarch}/BSSolv.pm +%{perl_vendorarch}/auto/BSSolv +%if 0%{?suse_version} +%if 0%{?suse_version} < 1140 +/var/adm/perl-modules/* +%endif +%endif + +%changelog +* Fri Apr 1 2016 mls@suse.de +- update to version 0.28.0 (perl module 0.08 version) + * fix typo, rename obscpiostats to obscpiostorestats +* Fri Apr 1 2016 mls@suse.de +- update to version 0.27.0 (perl module 0.07 version) + * add delta storage support +* Tue Mar 15 2016 mls@suse.de +- update to version 0.26.0 (perl module 0.06 version) + * add preparehashes pool method to speed up the scheduler +* Tue Aug 18 2015 mls@suse.de +- update to version 0.25.0 (perl module 0.05 version) + * make checksums pass our writefilter +* Tue Aug 18 2015 mls@suse.de +- update to version 0.24.0 (perl module 0.04 version) + * allow setting of the dodcookie without a dodurl +* Thu Jul 30 2015 mls@suse.de +- update to version 0.23.0 (perl module 0.03 version) + * implement download on demand data updating +* Thu Jun 11 2015 adrian@suse.de +- update to version 0.22.0 (perl module 0.02 version) + * implement handling of conflicts (aka '!') deps in expander + * treat ARCH_ANY like ARCH_ALL + * improve conflictsinfo handling + * support --ignoreignore-- flag + * improved debug output +* Thu Sep 18 2014 ro@suse.de +- update to BSSolv.xs from GIT +* Fri Oct 18 2013 mls@suse.de +- update to libsolv-0.4.0 to get REL_MULTIARCH support +* Wed Feb 6 2013 mls@suse.de +- update to libsolv-0.2.4 +* Wed Nov 14 2012 mls@suse.de +- update BSSolv.xs to get debugstr support +* Tue Nov 13 2012 mls@suse.de +- support 'ignoreconflicts' expandflag +* Tue Nov 13 2012 mls@suse.de +- use REPO_NO_LOCATION +* Mon Nov 12 2012 mls@suse.de +- update libsolv to current version to fix deb parsing +- bump repocookie +* Mon Oct 15 2012 mls@suse.de +- update libsolv and BSSolv.xs to current version + * export all dependencies + * obey package conflicts when expanding +* Fri Apr 27 2012 mls@suse.de +- update BSSolv.xs so that the worker caches binaries again +* Wed Apr 25 2012 mls@suse.de +- bump version to 0.18.2 +- update to libsolv-0.1.0 to fix opensuse 11.4 builds +* Wed Apr 4 2012 mls@suse.de +- bump version to 0.18.1 +- update to current libsolv +- enable arch linux support +* Wed Nov 30 2011 adrian@suse.de +- bump version to 0.18.0 +- base package on libsolv tar ball +* Wed Nov 30 2011 mls@suse.de +- update BSSolv.xs: + * fix prefer order handling bug + * add repo->tofile_fd +* Wed Nov 16 2011 mls@suse.de +- fix debian control file extraction, thanks djszapi! +* Thu Aug 25 2011 mls@suse.de +- fix debian dependency compare function +* Tue Jun 28 2011 coolo@novell.com +- add requires to correct perl version +* Tue Feb 15 2011 mls@suse.de +- define USE_OWN_QSORT instead of using glib +* Mon Feb 14 2011 adrian@suse.de +- update to 0.16.4 +* Thu Dec 9 2010 adrian@suse.de +- update to 0.16.2 +- fix build for Factory +* Fri Oct 29 2010 adrian@suse.de +- follow the version of satsolver for this package +* Sat Oct 23 2010 mrdocs@opensuse.org +- fix the build with the correct source version +* Tue Oct 19 2010 adrian@suse.de +- adapt spec to version 0.16.0 +* Fri Jul 23 2010 adrian@suse.de +- update BSSolv.xs from current build-service git repo +* Fri May 14 2010 adrian@suse.de +- adapt spec to version 0.15.0 +* Wed Apr 7 2010 mls@suse.de +- adapt spec to version 0.14.17 +* Mon Mar 15 2010 chris@computersalat.de +- fix spec + o new version 0.14.16 (zypp:Head) +* Tue Mar 2 2010 chris@computersalat.de +- hmm, cause of undocumented SOURCE changes + (satsolver-0.14.15 from zypp:Head), + util patch (SLES 10 fix) now obsolete + o hence removed +* Sun Feb 28 2010 adrian@suse.de +- fixed SLES 10 fix +* Tue Feb 23 2010 chris@computersalat.de +- fix spec + o new version 0.14.15 (zypp:Head) +- reworked patch for SLE_10 +* Fri Feb 12 2010 chris@computersalat.de +- spec mods + o added header + o fixed source version +- fix build for SLE_10 + o added satsolver-0.14.14-util.patch +- BuildReq ruby-devel > 1.8.4 + o /usr/bin/ruby: no such file to + load -- vendor-specific (LoadError) +* Mon Dec 21 2009 mls@suse.de +- add dod changes +- fix bug in debian dep parsing +- add support for whatprovides/whatrequires +* Fri Oct 23 2009 mls@suse.de +- strip trailing spaces in dep2id +* Wed Oct 21 2009 mls@suse.de +- add pkg2sizek +* Wed Oct 7 2009 mls@suse.de +- update BSSolv.xs +* Tue Oct 6 2009 mls@suse.de +- initial version diff --git a/packaging/typemap b/packaging/typemap new file mode 100644 index 0000000..5a437ed --- /dev/null +++ b/packaging/typemap @@ -0,0 +1,3 @@ +BSSolv::pool T_PTROBJ +BSSolv::repo T_PTROBJ +BSSolv::expander T_PTROBJ -- 2.7.4