File _service:obs_scm:BSSolv.xs of Package perl-BSSolv
/*
* Copyright (c) 2009 - 2017 SUSE Linux Products GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the same terms as Perl itself.
*
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#define MULTI_SEMANTICS
#include "solvversion.h"
#if LIBSOLV_VERSION < 623
#define LIBSOLVEXT_FEATURE_DEBIAN
#define LIBSOLVEXT_FEATURE_ARCHREPO
#endif
#include "pool.h"
#include "repo.h"
#include "util.h"
#include "evr.h"
#include "hash.h"
#include "chksum.h"
#include "testcase.h"
#include "repo_solv.h"
#include "repo_write.h"
#include "repo_rpmdb.h"
#if defined(LIBSOLVEXT_FEATURE_DEBIAN)
#include "repo_deb.h"
#endif
#if defined(LIBSOLVEXT_FEATURE_ARCHREPO)
#include "repo_arch.h"
#endif
#if defined(LIBSOLV_FEATURE_COMPLEX_DEPS)
#include "pool_parserpmrichdep.h"
#endif
#ifndef REL_ERROR
# define REL_ERROR 27 /* for old libsolv versions */
#endif
#ifndef REL_UNLESS
# define REL_UNLESS 29 /* for old libsolv versions */
#endif
#define EXPANDER_DEBUG_ALL (1 << 0)
#define EXPANDER_DEBUG_STDOUT (1 << 1)
#define EXPANDER_DEBUG_STR (1 << 2)
#define EXPANDER_OPTION_IGNOREIGNORE (1 << 0)
#define EXPANDER_OPTION_IGNORECONFLICTS (1 << 1)
#define EXPANDER_OPTION_DORECOMMENDS (1 << 2)
#define EXPANDER_OPTION_DOSUPPLEMENTS (1 << 3)
#define EXPANDER_OPTION_USERECOMMENDSFORCHOICES (1 << 4)
#define EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES (1 << 5)
typedef struct _Expander {
Pool *pool;
Map ignored;
Map ignoredx;
Queue preferposq;
Map preferpos;
Map preferposx;
Map preferneg;
Map prefernegx;
Queue conflictsq;
Map conflicts;
int havefileprovides;
/* debug support */
int debug;
char *debugstr;
int debugstrl;
int debugstrf;
/* options */
int ignoreconflicts;
int ignoreignore;
int userecommendsforchoices;
int usesupplementsforchoices;
int dorecommends;
int dosupplements;
} Expander;
typedef struct _ExpanderCtx {
Pool *pool;
Expander *xp;
Queue *out; /* the result */
Map installed; /* installed packages */
Map conflicts; /* conflicts from installed packages */
Queue conflictsinfo; /* source info for the above */
int cidone; /* conflictsinfo done position */
Queue todo; /* requires todo list */
Queue errors; /* expansion errors */
Queue cplxq; /* complex dep work queue */
Queue cplxblks; /* complex dep block data, add only */
Queue todo_cond; /* delayed requires/conflicts */
Queue pruneq; /* multi purpose queue for pruning packages */
Map todo_condmap; /* all neg packages in todo_cond blocks */
Map recommended; /* recommended packages */
int recdone; /* recommended done position */
/* options */
int ignoreconflicts;
int ignoreignore;
int userecommendsforchoices;
int usesupplementsforchoices;
int dorecommends;
int dosupplements;
/* hacks */
Solvable *ignore_s; /* small hack: ignore requires of this solvable */
} ExpanderCtx;
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 buildservice_dodcookie;
static Id buildservice_dodresources;
static Id buildservice_annotation;
static Id buildservice_modules;
static Id expander_directdepsend;
static int genmetaalgo;
static int depsortsccs;
/* 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_rec(Pool *pool, char *s)
{
char *n;
Id id;
int flags;
if ((n = strchr(s, '|')) != 0)
{
id = dep2id_rec(pool, n + 1);
*n = 0;
id = pool_rel2id(pool, dep2id_rec(pool, s), id, REL_OR, 1);
*n = '|';
return id;
}
while (*s == ' ' || *s == '\t')
s++;
n = s;
if (pool->disttype == DISTTYPE_RPM)
{
/* rpm delimits the name by whitespace only */
while (*s && *s != ' ' && *s != '\t')
s++;
}
else
{
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 Id
parsedep_error(Pool *pool, const char *s)
{
Id id;
id = pool_str2id(pool, s, 1);
return pool_rel2id(pool, pool_str2id(pool, "dependency parse error", 1), id, REL_ERROR, 1);
}
static Id
dep2id(Pool *pool, char *s)
{
Id id;
if (pool->disttype == DISTTYPE_RPM && *s == '(')
{
#if defined(LIBSOLV_FEATURE_COMPLEX_DEPS)
id = pool_parserpmrichdep(pool, s);
#else
id = 0;
#endif
}
else
id = dep2id_rec(pool, s);
if (!id)
id = parsedep_error(pool, s);
return id;
}
static Offset
importdeps(HV *hv, const char *key, int keyl, Repo *repo)
{
Pool *pool = repo->pool;
SSize_t 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)
{
Id id = testcase_str2dep(pool, str);
if (!id)
id = parsedep_error(pool, str);
off = repo_addid_dep(repo, off, id, 0);
}
}
}
return off;
}
static 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 = testcase_dep2str(pool, id);
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);
}
static int
data2pkg(Repo *repo, Repodata *data, HV *hv, int isdod)
{
Pool *pool = repo->pool;
char *str;
Id p;
Solvable *s;
AV *av;
str = hvlookupstr(hv, "name", 4);
if (!str)
return 0; /* 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);
}
if (isdod)
repodata_set_str(data, p, buildservice_id, "dod");
else
{
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);
if (isdod)
{
static unsigned char dod_pkgid[16] = { 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0 };
repodata_set_bin_checksum(data, p, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, dod_pkgid);
}
else
{
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->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
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);
}
}
str = hvlookupstr(hv, "annotation", 10);
if (str && strlen(str) < 100000)
repodata_set_str(data, p, buildservice_annotation, str);
av = hvlookupav(hv, "modules", 7);
if (av)
{
SSize_t i;
for (i = 0; i <= av_len(av); i++)
{
char *str = avlookupstr(av, i);
repodata_add_idarray(data, p, buildservice_modules, pool_str2id(pool, str, 1));
}
}
return p;
}
static void
data2solvables(Repo *repo, Repodata *data, SV *rsv, int isdod)
{
AV *rav = 0;
SSize_t ravi = 0;
HV *rhv = 0;
SV *sv;
char *key;
I32 keyl;
if (SvTYPE(rsv) == SVt_PVAV)
rav = (AV *)rsv;
else
rhv = (HV *)rsv;
if (rhv)
hv_iterinit(rhv);
for (;;)
{
if (rhv)
{
sv = hv_iternextsv(rhv, &key, &keyl);
if (!sv)
break;
}
else
{
SV **svp;
if (ravi > av_len(rav))
break;
svp = av_fetch(rav, ravi++, 0);
if (!svp || !*svp)
continue;
sv = *svp;
}
if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV)
continue;
data2pkg(repo, data, (HV *)SvRV(sv), isdod);
}
/* set meta information */
repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
if (rhv)
{
char *str;
AV *av;
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);
av = hvlookupav(rhv, "/dodresources", 13);
if (av)
{
SSize_t i;
for (i = 0; i <= av_len(av); i++)
{
Id id = pool_str2id(repo->pool, avlookupstr(av, i), 1);
repodata_add_idarray(data, SOLVID_META, buildservice_dodresources, id);
}
}
}
}
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;
}
#define CPLXDEPS_TODNF (1 << 0)
static int
invert_depblocks(ExpanderCtx *xpctx, Queue *bq, int start, int r)
{
int i, j, end;
if (r == 0 || r == 1)
return r ? 0 : 1;
end = bq->count;
for (i = j = start; i < end; i++)
{
if (bq->elements[i])
{
bq->elements[i] = -bq->elements[i];
continue;
}
/* end of block reached, reverse */
if (i - 1 > j)
{
int k;
for (k = i - 1; j < k; j++, k--)
{
Id t = bq->elements[j];
bq->elements[j] = bq->elements[k];
bq->elements[k] = t;
}
}
j = i + 1;
}
return -1;
}
static int
distribute_depblocks(ExpanderCtx *xpctx, Queue *bq, int start, int start2, int flags)
{
int i, j, end2 = bq->count;
for (i = start; i < start2; i++)
{
for (j = start2; j < end2; j++)
{
int a, b;
int bqcnt4 = bq->count;
int k = i;
/* distribute i block with j block, both blocks are sorted */
while (bq->elements[k] && bq->elements[j])
{
if (bq->elements[k] < bq->elements[j])
queue_push(bq, bq->elements[k++]);
else
{
if (bq->elements[k] == bq->elements[j])
k++;
queue_push(bq, bq->elements[j++]);
}
}
while (bq->elements[j])
queue_push(bq, bq->elements[j++]);
while (bq->elements[k])
queue_push(bq, bq->elements[k++]);
/* block is finished, check for A + -A */
for (a = bqcnt4, b = bq->count - 1; a < b; )
{
if (-bq->elements[a] == bq->elements[b])
break;
if (-bq->elements[a] > bq->elements[b])
a++;
else
b--;
}
if (a < b)
queue_truncate(bq, bqcnt4); /* ignore this block */
else
queue_push(bq, 0); /* finish block */
}
/* advance to next block */
while (bq->elements[i])
i++;
}
queue_deleten(bq, start, end2 - start);
if (start == bq->count)
return flags & CPLXDEPS_TODNF ? 0 : 1;
return -1;
}
#if 0
static void
print_depblocks(ExpanderCtx *xpctx, Queue *bq, int start, int r)
{
Pool *pool = xpctx->pool;
int i;
if (r == 0)
{
printf("[NONE]\n");
return;
}
if (r == 1)
{
printf("[ALL]\n");
return;
}
for (i = start; i < bq->count; i++)
{
if (bq->elements[i] > 0)
printf(" %s", pool_solvid2str(pool, bq->elements[i]));
else if (bq->elements[i] < 0)
printf(" -%s", pool_solvid2str(pool, -bq->elements[i]));
else
printf(" ||");
}
printf("\n");
}
#endif
static int
pool_is_complex_dep_rd(Pool *pool, Reldep *rd)
{
for (;;)
{
if (rd->flags == REL_AND || rd->flags == REL_COND || rd->flags == REL_UNLESS) /* those are the complex ones */
return 1;
if (rd->flags != REL_OR)
return 0;
if (ISRELDEP(rd->name) && pool_is_complex_dep_rd(pool, GETRELDEP(pool, rd->name)))
return 1;
if (!ISRELDEP(rd->evr))
return 0;
rd = GETRELDEP(pool, rd->evr);
}
}
static inline int
pool_is_complex_dep(Pool *pool, Id dep)
{
if (ISRELDEP(dep))
{
Reldep *rd = GETRELDEP(pool, dep);
if (rd->flags >= 8 && pool_is_complex_dep_rd(pool, rd))
return 1;
}
return 0;
}
static int normalize_dep(ExpanderCtx *xpctx, Id dep, Queue *bq, int flags);
static int
normalize_dep_or(ExpanderCtx *xpctx, Id dep1, Id dep2, Queue *bq, int flags, int invflags)
{
int r1, r2, bqcnt2, bqcnt = bq->count;
r1 = normalize_dep(xpctx, dep1, bq, flags);
if (r1 == 1)
return 1; /* early exit */
bqcnt2 = bq->count;
r2 = normalize_dep(xpctx, dep2, bq, flags ^ invflags);
if (invflags)
r2 = invert_depblocks(xpctx, bq, bqcnt2, r2);
if (r1 == 1 || r2 == 1)
{
queue_truncate(bq, bqcnt);
return 1;
}
if (r1 == 0)
return r2;
if (r2 == 0)
return r1;
if ((flags & CPLXDEPS_TODNF) == 0)
return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
return -1;
}
static int
normalize_dep_and(ExpanderCtx *xpctx, Id dep1, Id dep2, Queue *bq, int flags, int invflags)
{
int r1, r2, bqcnt2, bqcnt = bq->count;
r1 = normalize_dep(xpctx, dep1, bq, flags);
if (r1 == 0)
return 0; /* early exit */
bqcnt2 = bq->count;
r2 = normalize_dep(xpctx, dep2, bq, flags ^ invflags);
if (invflags)
r2 = invert_depblocks(xpctx, bq, bqcnt2, r2);
if (r1 == 0 || r2 == 0)
{
queue_truncate(bq, bqcnt);
return 0;
}
if (r1 == 1)
return r2;
if (r2 == 1)
return r1;
if ((flags & CPLXDEPS_TODNF) != 0)
return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
return -1;
}
static int
normalize_dep_if_else(ExpanderCtx *xpctx, Id dep1, Id dep2, Id dep3, Queue *bq, int flags)
{
/* A IF (B ELSE C) -> (A OR ~B) AND (C OR B) */
int r1, r2, bqcnt2, bqcnt = bq->count;
r1 = normalize_dep_or(xpctx, dep1, dep2, bq, flags, CPLXDEPS_TODNF);
if (r1 == 0)
return 0; /* early exit */
bqcnt2 = bq->count;
r2 = normalize_dep_or(xpctx, dep2, dep3, bq, flags, 0);
if (r1 == 0 || r2 == 0)
{
queue_truncate(bq, bqcnt);
return 0;
}
if (r1 == 1)
return r2;
if (r2 == 1)
return r1;
if ((flags & CPLXDEPS_TODNF) != 0)
return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
return -1;
}
static int
normalize_dep_unless_else(ExpanderCtx *xpctx, Id dep1, Id dep2, Id dep3, Queue *bq, int flags)
{
/* A UNLESS (B ELSE C) -> (A AND ~B) OR (C AND B) */
int r1, r2, bqcnt2, bqcnt = bq->count;
r1 = normalize_dep_and(xpctx, dep1, dep2, bq, flags, CPLXDEPS_TODNF);
if (r1 == 1)
return 1; /* early exit */
bqcnt2 = bq->count;
r2 = normalize_dep_and(xpctx, dep2, dep3, bq, flags, 0);
if (r1 == 1 || r2 == 1)
{
queue_truncate(bq, bqcnt);
return 1;
}
if (r1 == 0)
return r2;
if (r2 == 0)
return r1;
if ((flags & CPLXDEPS_TODNF) == 0)
return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
return -1;
}
static int expander_isignored(Expander *xp, Solvable *s, Id req);
static int
normalize_dep(ExpanderCtx *xpctx, Id dep, Queue *bq, int flags)
{
Pool *pool = xpctx->pool;
Id p, dp;
if (pool_is_complex_dep(pool, dep))
{
Reldep *rd = GETRELDEP(pool, dep);
if (rd->flags == REL_COND)
{
Id evr = rd->evr;
if (ISRELDEP(evr))
{
Reldep *rd2 = GETRELDEP(pool, evr);
if (rd2->flags == REL_ELSE)
return normalize_dep_if_else(xpctx, rd->name, rd2->name, rd2->evr, bq, flags);
}
return normalize_dep_or(xpctx, rd->name, rd->evr, bq, flags, CPLXDEPS_TODNF);
}
if (rd->flags == REL_UNLESS)
{
Id evr = rd->evr;
if (ISRELDEP(evr))
{
Reldep *rd2 = GETRELDEP(pool, evr);
if (rd2->flags == REL_ELSE)
return normalize_dep_unless_else(xpctx, rd->name, rd2->name, rd2->evr, bq, flags);
}
return normalize_dep_and(xpctx, rd->name, rd->evr, bq, flags, CPLXDEPS_TODNF);
}
if (rd->flags == REL_OR)
return normalize_dep_or(xpctx, rd->name, rd->evr, bq, flags, 0);
if (rd->flags == REL_AND)
return normalize_dep_and(xpctx, rd->name, rd->evr, bq, flags, 0);
}
if (xpctx->ignore_s && (flags & CPLXDEPS_TODNF) == 0)
{
if (expander_isignored(xpctx->xp, xpctx->ignore_s, dep))
return 1;
}
dp = pool_whatprovides(pool, dep);
if (dp == 2)
return 1;
if (dp < 2 || !pool->whatprovidesdata[dp])
return 0;
if (pool->whatprovidesdata[dp] == SYSTEMSOLVABLE)
return 1;
if ((flags & CPLXDEPS_TODNF) != 0)
{
while ((p = pool->whatprovidesdata[dp++]) != 0)
queue_push2(bq, p, 0);
}
else
{
while ((p = pool->whatprovidesdata[dp++]) != 0)
queue_push(bq, p);
queue_push(bq, 0);
}
return -1;
}
#define ISCPLX(pool, d) (ISRELDEP(d) && GETRELID(d) >= pool->nrels)
#define GETCPLX(pool, d) (GETRELID(d) - pool->nrels)
#define MAKECPLX(pool, d) (MAKERELDEP(pool->nrels + d))
#define DEPTYPE_REQUIRES 0
#define DEPTYPE_CONFLICTS 1
#define DEPTYPE_OBSOLETES 2
#define DEPTYPE_RECOMMENDS 3
#define DEPTYPE_PROVIDES 4
#define ERROR_NOPROVIDER 1
#define ERROR_CHOICE 2
#define ERROR_CONFLICTINGPROVIDERS 3
#define ERROR_PROVIDERINFO 4
#define ERROR_PROVIDERINFO2 5
#define ERROR_BADDEPENDENCY 6
#define ERROR_CONFLICT 7
#define ERROR_CONFLICT2 8
#define ERROR_ALLCONFLICT 9
#define ERROR_NOPROVIDERINFO 10
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);
l = strlen(buf);
if ((xp->debug & (EXPANDER_DEBUG_ALL | EXPANDER_DEBUG_STDOUT)) != 0)
{
printf("%s", buf);
if (buf[0] != ' ' || (l && buf[l - 1] == '\n'))
fflush(stdout);
}
if ((xp->debug & (EXPANDER_DEBUG_ALL | EXPANDER_DEBUG_STR)) != 0)
{
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 void
expander_clrdbg(Expander *xp)
{
if (xp->debugstr)
free(xp->debugstr);
xp->debugstr = 0;
xp->debugstrl = xp->debugstrf = 0;
}
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 const char *
expander_solvid2str(Expander *xp, Id p)
{
const char *n = pool_solvid2str(xp->pool, p);
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 int
pkgname_sort_cmp(const void *ap, const void *bp, void *dp)
{
Pool *pool = (Pool *)dp;
Id a = *(Id *)ap;
Id b = *(Id *)bp;
return strcmp(pool_id2str(pool, pool->solvables[a].name), pool_id2str(pool, pool->solvables[b].name));
}
static int
expander_isignored(Expander *xp, Solvable *s, Id req)
{
Pool *pool = xp->pool;
Id id = id2name(pool, req);
const char *n;
if (!xp->ignoreignore)
{
if (MAPTST(&xp->ignored, id))
return 1;
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))
return 1;
}
}
n = pool_id2str(pool, id);
if (!strncmp(n, "rpmlib(", 7))
{
MAPEXP(&xp->ignored, id);
MAPSET(&xp->ignored, id);
return 1;
}
if (*n == '/')
{
if (!xp->havefileprovides || pool->whatprovides[id] <= 1)
{
MAPEXP(&xp->ignored, id);
MAPSET(&xp->ignored, id);
return 1;
}
}
return 0;
}
static int
expander_to_cplxblks(ExpanderCtx *xpctx, Id p, Id dep, int deptype, Id *ptr)
{
int blkoff = xpctx->cplxblks.count;
queue_push(&xpctx->cplxblks, p);
queue_push(&xpctx->cplxblks, dep);
queue_push(&xpctx->cplxblks, deptype);
for (;;)
{
Id pp = *ptr++;
queue_push(&xpctx->cplxblks, pp);
if (!pp)
break;
}
return blkoff;
}
static int
expander_check_cplxblock(ExpanderCtx *xpctx, Id p, Id dep, int deptype, Id *ptr, int blkoff)
{
Pool *pool = xpctx->pool;
int posn = 0, posi = 0, negn = 0, negi = 0;
Id pp, *ptr2 = ptr;
Id lastcon = 0;
while ((pp = *ptr2++) != 0)
{
if (pp > 0)
{
posn++;
if (MAPTST(&xpctx->installed, pp))
posi++;
}
else
{
if (p == -pp)
continue; /* ignore redundant self-entry */
negn++;
if (MAPTST(&xpctx->installed, -pp))
negi++;
else
lastcon = -pp;
}
}
#if 0
printf("expander_check_cplxblock pos: %d,%d neg: %d,%d\n", posn, posi, negn, negi);
#endif
if (posi)
return -1;
if (!posn && deptype == DEPTYPE_RECOMMENDS)
return -1;
if (negi == negn)
{
/* all neg installed */
if (posn)
{
/* posn > 0 and all neg installed, add to todo */
if (blkoff < 0)
blkoff = expander_to_cplxblks(xpctx, p, dep, deptype, ptr);
#if 0
printf("put on todo, blkoff = %d\n", blkoff);
#endif
queue_push2(&xpctx->todo, MAKECPLX(pool, blkoff), p);
}
else
{
/* no posn, conflict */
for (ptr2 = ptr; (pp = *ptr2++) != 0; )
{
if (p == -pp)
continue; /* ignore redundant self-entry */
if (deptype == DEPTYPE_REQUIRES)
{
/* do not report a requires as conflicts */
queue_push(&xpctx->errors, ERROR_NOPROVIDER);
queue_push2(&xpctx->errors, dep, p);
break;
}
queue_push(&xpctx->errors, ERROR_CONFLICT);
queue_push2(&xpctx->errors, p, -pp);
}
}
return -1;
}
else if (!posn && negn && negi == negn - 1)
{
/* add conflict */
#if 0
printf("add conflict %d %d\n", lastcon, p);
#endif
MAPEXP(&xpctx->conflicts, pool->nsolvables);
MAPSET(&xpctx->conflicts, lastcon);
if (p)
queue_push2(&xpctx->conflictsinfo, lastcon, p); /* always do this for rich deps */
return -1;
}
else
{
#ifdef DEBUG_COND
printf("put/stay on cond queue, blkoff = %d\n", blkoff);
#endif
/* either posn > 0 and 1 neg not installed or more than 1 neg not installed */
if (blkoff < 0)
blkoff = expander_to_cplxblks(xpctx, p, dep, deptype, ptr);
return blkoff;
}
}
static void
expander_installed_complexdep(ExpanderCtx *xpctx, Id p, Id dep, int deptype)
{
Queue *cplxq = &xpctx->cplxq;
int r, i, start = cplxq->count, blkoff;
#if 0
printf("expander_installed_complexdep %s type %d\n", pool_dep2str(xpctx->pool, dep), deptype);
#endif
if (deptype == DEPTYPE_CONFLICTS)
{
r = normalize_dep(xpctx, dep, cplxq, CPLXDEPS_TODNF);
r = invert_depblocks(xpctx, cplxq, start, r);
}
else
r = normalize_dep(xpctx, dep, cplxq, 0);
#if 0
print_depblocks(xpctx, cplxq, start, r);
#endif
if (r == 1)
return;
if (r == 0)
{
if (deptype == DEPTYPE_CONFLICTS)
{
queue_push(&xpctx->errors, ERROR_ALLCONFLICT);
queue_push2(&xpctx->errors, dep, p);
}
else if (deptype != DEPTYPE_RECOMMENDS)
{
queue_push(&xpctx->errors, ERROR_NOPROVIDER);
queue_push2(&xpctx->errors, dep, p);
}
return;
}
while (start < cplxq->count)
{
/* find end */
for (i = start; cplxq->elements[i] != 0; i++)
;
blkoff = expander_check_cplxblock(xpctx, p, dep, deptype, cplxq->elements + start, -1);
if (blkoff >= 0)
{
Pool *pool = xpctx->pool;
Id p;
MAPEXP(&xpctx->todo_condmap, pool->nsolvables);
for (i = start; (p = cplxq->elements[i]) != 0; i++)
if (p < 0)
MAPSET(&xpctx->todo_condmap, -p);
queue_push(&xpctx->todo_cond, blkoff);
}
start = i + 1;
}
queue_truncate(cplxq, start);
}
static int
expander_checkconflicts_complexdep(ExpanderCtx *xpctx, Id p, Id dep, int deptype, int recorderrors)
{
Queue *cplxq = &xpctx->cplxq;
int r, i, start = cplxq->count;
Id pp;
int ret = 0;
#if 0
printf("expander_checkconflicts_complexdep %s type %d\n", pool_dep2str(xpctx->pool, dep), deptype);
#endif
if (deptype == DEPTYPE_CONFLICTS)
{
r = normalize_dep(xpctx, dep, cplxq, CPLXDEPS_TODNF);
r = invert_depblocks(xpctx, cplxq, start, r);
}
else
r = normalize_dep(xpctx, dep, cplxq, 0);
#if 0
print_depblocks(xpctx, cplxq, start, r);
#endif
/* r == 0: conflict with everything. Ignore here, pick error up when package gets installed */
if (r == 0 || r == 1)
return 0;
while (start < cplxq->count)
{
for (i = start; (pp = cplxq->elements[i]) != 0; i++)
if (pp > 0 || (pp < 0 && !MAPTST(&xpctx->installed, -pp)))
break;
if (pp == 0)
{
/* no pos and all neg installed -> conflict */
for (i = start; (pp = cplxq->elements[i]) != 0; i++)
{
pp = -cplxq->elements[i];
if (recorderrors)
{
queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT : ERROR_PROVIDERINFO);
queue_push2(&xpctx->errors, p, pp);
}
else if (xpctx->xp->debug)
{
Pool *pool = xpctx->pool;
expander_dbg(xpctx->xp, "ignoring provider %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_solvid2str(pool, pp));
}
ret = ret ? 1 : pp;
}
}
for (; cplxq->elements[i] != 0; i++)
;
start = i + 1;
}
queue_truncate(cplxq, start);
return ret;
}
static void
updateconflictsinfo(ExpanderCtx *xpctx)
{
int i;
Pool *pool = xpctx->pool;
Queue *out = xpctx->out;
Queue *conflictsinfo = &xpctx->conflictsinfo;
if (xpctx->ignoreconflicts)
return;
for (i = xpctx->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)
{
if (pool_is_complex_dep(pool, con))
continue; /* already pushed */
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);
}
}
}
}
xpctx->cidone = out->count;
}
static int
findconflictsinfo(ExpanderCtx *xpctx, Id p, int recorderrors)
{
Queue *conflictsinfo = &xpctx->conflictsinfo;
int i, ret = 0;
if (xpctx->cidone < xpctx->out->count)
updateconflictsinfo(xpctx);
for (i = 0; i < conflictsinfo->count; i++)
if (conflictsinfo->elements[i] == p)
{
ret = conflictsinfo->elements[i + 1];
if (recorderrors)
{
queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT2 : ERROR_PROVIDERINFO2);
queue_push2(&xpctx->errors, p, ret);
}
else if (xpctx->xp->debug)
{
Pool *pool = xpctx->pool;
expander_dbg(xpctx->xp, "ignoring provider %s because installed %s %s it\n", pool_solvid2str(pool, p), pool_solvid2str(pool, ret > 0 ? ret : -ret), ret > 0 ? "conflicts with" : "obsoletes");
}
}
if (!ret)
{
/* conflict from our job, i.e. a !xxx dep */
if (recorderrors)
{
queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT2 : ERROR_PROVIDERINFO2);
queue_push2(&xpctx->errors, p, 0);
}
else if (xpctx->xp->debug)
{
Pool *pool = xpctx->pool;
expander_dbg(xpctx->xp, "ignoring conflicted provider %s\n", pool_solvid2str(pool, p));
}
}
return ret;
}
static void
recheck_conddeps(ExpanderCtx *xpctx)
{
int i;
for (i = 0; i < xpctx->todo_cond.count; i++)
{
int blkoff = xpctx->todo_cond.elements[i];
#ifdef DEBUG_COND
printf("todo_cond %d\n", blkoff);
#endif
Id *ptr = xpctx->cplxblks.elements + blkoff;
if (expander_check_cplxblock(xpctx, ptr[0], ptr[1], ptr[2], ptr + 3, blkoff) < 0)
{
#ifdef DEBUG_COND
printf("remove no longer needed cond entry\n");
#endif
queue_delete(&xpctx->todo_cond, i);
i--;
}
}
}
/* install a single package */
static void
expander_installed(ExpanderCtx *xpctx, Id p)
{
Pool *pool = xpctx->pool;
Expander *xp = xpctx->xp;
Solvable *s = pool->solvables + p;
Id req, *reqp, con, *conp;
#if 0
printf("expander_installed %s\n", pool_solvid2str(pool, p));
#endif
MAPSET(&xpctx->installed, p);
queue_push(xpctx->out, p);
if (xpctx->conflicts.size && MAPTST(&xpctx->conflicts, p))
findconflictsinfo(xpctx, p, 2);
/* add synthetic conflicts from the project config */
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)
continue;
if (MAPTST(&xpctx->installed, p2))
{
queue_push(&xpctx->errors, ERROR_CONFLICT);
queue_push2(&xpctx->errors, p, p2);
continue;
}
MAPEXP(&xpctx->conflicts, pool->nsolvables);
MAPSET(&xpctx->conflicts, p2);
queue_push2(&xpctx->conflictsinfo, p2, p);
}
}
}
if (s->requires)
{
reqp = s->repo->idarraydata + s->requires;
while ((req = *reqp++) != 0)
{
if (req == SOLVABLE_PREREQMARKER)
continue;
if (ISRELDEP(req) && GETRELDEP(pool, req)->flags == REL_ERROR)
{
queue_push(&xpctx->errors, ERROR_BADDEPENDENCY);
queue_push2(&xpctx->errors, GETRELDEP(pool, req)->evr, p);
continue;
}
if (pool_is_complex_dep(pool, req))
{
xpctx->ignore_s = s;
expander_installed_complexdep(xpctx, p, req, DEPTYPE_REQUIRES);
xpctx->ignore_s = 0;
continue;
}
if (expander_isignored(xp, s, req))
continue;
queue_push2(&xpctx->todo, req, p);
}
}
if (!xpctx->ignoreconflicts)
{
if (s->conflicts)
{
conp = s->repo->idarraydata + s->conflicts;
while ((con = *conp++) != 0)
{
Id p2, pp2;
if (ISRELDEP(con) && GETRELDEP(pool, con)->flags == REL_ERROR)
{
queue_push(&xpctx->errors, ERROR_BADDEPENDENCY);
queue_push2(&xpctx->errors, GETRELDEP(pool, con)->evr, p);
continue;
}
if (pool_is_complex_dep(pool, con))
{
expander_installed_complexdep(xpctx, p, con, DEPTYPE_CONFLICTS);
continue;
}
FOR_PROVIDES(p2, pp2, con)
{
if (p2 == p)
continue;
if (MAPTST(&xpctx->installed, p2))
{
queue_push(&xpctx->errors, ERROR_CONFLICT);
queue_push2(&xpctx->errors, p, p2);
continue;
}
MAPEXP(&xpctx->conflicts, pool->nsolvables);
MAPSET(&xpctx->conflicts, p2);
if (xp->debug)
queue_push2(&xpctx->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;
if (MAPTST(&xpctx->installed, p2))
{
queue_push(&xpctx->errors, ERROR_CONFLICT);
queue_push2(&xpctx->errors, p, -p2);
continue;
}
MAPEXP(&xpctx->conflicts, pool->nsolvables);
MAPSET(&xpctx->conflicts, p2);
if (xp->debug)
queue_push2(&xpctx->conflictsinfo, p2, -p);
}
}
}
if (xp->debug)
xpctx->cidone = xpctx->out->count;
}
if (xpctx->todo_condmap.size && MAPTST(&xpctx->todo_condmap, p))
recheck_conddeps(xpctx);
}
/* same as expander_installed, but install multiple packages
* in one block */
static void
expander_installed_multiple(ExpanderCtx *xpctx, Queue *toinstall)
{
int i, j, havecond = 0;
/* unify */
for (i = j = 0; i < toinstall->count; i++)
{
Id p = toinstall->elements[i];
if (MAPTST(&xpctx->installed, p))
continue; /* already seen */
MAPSET(&xpctx->installed, p);
toinstall->elements[j++] = p;
if (xpctx->todo_condmap.size && MAPTST(&xpctx->todo_condmap, p))
{
havecond = 1;
MAPCLR(&xpctx->todo_condmap, p); /* no longer needed */
}
}
queue_truncate(toinstall, j);
/* run conditionals first */
if (havecond)
recheck_conddeps(xpctx);
if (!xpctx->errors.count)
for (i = 0; i < toinstall->count; i++)
expander_installed(xpctx, toinstall->elements[i]);
queue_empty(toinstall);
}
static int
expander_checkconflicts(ExpanderCtx *xpctx, Id p, Id *conflicts, int isobsoletes, int recorderrors)
{
Map *installed = &xpctx->installed;
Pool *pool = xpctx->pool;
Id con, p2, pp2;
int ret = 0;
if (xpctx->ignoreconflicts)
return 0;
while ((con = *conflicts++) != 0)
{
if (!isobsoletes && pool_is_complex_dep(pool, con))
{
p2 = expander_checkconflicts_complexdep(xpctx, p, con, DEPTYPE_CONFLICTS, recorderrors);
ret = ret ? 1 : p2;
continue;
}
FOR_PROVIDES(p2, pp2, con)
{
if (p == p2)
continue;
if (isobsoletes && !pool_match_nevr(pool, pool->solvables + p2, con))
continue;
if (MAPTST(installed, p2))
{
if (recorderrors)
{
queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT : ERROR_PROVIDERINFO);
queue_push2(&xpctx->errors, p, isobsoletes ? -p2 : p2);
}
else if (xpctx->xp->debug)
{
if (isobsoletes)
expander_dbg(xpctx->xp, "ignoring provider %s because it obsoletes installed %s\n", pool_solvid2str(pool, p), pool_solvid2str(pool, p2));
else
expander_dbg(xpctx->xp, "ignoring provider %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_solvid2str(pool, p2));
}
ret = ret ? 1 : p2;
}
}
}
return ret;
}
static void
expander_updaterecommendedmap(ExpanderCtx *xpctx)
{
Pool *pool = xpctx->pool;
Queue *out = xpctx->out;
Map *recommended = &xpctx->recommended;
int i;
Id p, pp, rec, *recp;
for (i = xpctx->recdone; i < out->count; i++)
{
Solvable *s;
s = pool->solvables + out->elements[i];
if (s->recommends)
{
MAPEXP(recommended, pool->nsolvables);
for (recp = s->repo->idarraydata + s->recommends; (rec = *recp++) != 0; )
FOR_PROVIDES(p, pp, rec)
MAPSET(recommended, p);
}
}
xpctx->recdone = out->count;
}
static int
expander_dep_fulfilled(ExpanderCtx *xpctx, Id dep)
{
Pool *pool = xpctx->pool;
Id p, pp;
if (ISRELDEP(dep))
{
Reldep *rd = GETRELDEP(pool, dep);
if (rd->flags == REL_COND)
{
if (ISRELDEP(rd->evr))
{
Reldep *rd2 = GETRELDEP(pool, rd->evr);
if (rd2->flags == REL_ELSE)
{
if (expander_dep_fulfilled(xpctx, rd2->name))
return expander_dep_fulfilled(xpctx, rd->name);
return expander_dep_fulfilled(xpctx, rd2->evr);
}
}
if (expander_dep_fulfilled(xpctx, rd->name)) /* A OR ~B */
return 1;
return !expander_dep_fulfilled(xpctx, rd->evr);
}
if (rd->flags == REL_UNLESS)
{
if (ISRELDEP(rd->evr))
{
Reldep *rd2 = GETRELDEP(pool, rd->evr);
if (rd2->flags == REL_ELSE)
{
if (!expander_dep_fulfilled(xpctx, rd2->name))
return expander_dep_fulfilled(xpctx, rd->name);
return expander_dep_fulfilled(xpctx, rd2->evr);
}
}
if (!expander_dep_fulfilled(xpctx, rd->name)) /* A AND ~B */
return 0;
return !expander_dep_fulfilled(xpctx, rd->evr);
}
if (rd->flags == REL_AND)
{
if (!expander_dep_fulfilled(xpctx, rd->name))
return 0;
return expander_dep_fulfilled(xpctx, rd->evr);
}
if (rd->flags == REL_OR)
{
if (expander_dep_fulfilled(xpctx, rd->name))
return 1;
return expander_dep_fulfilled(xpctx, rd->evr);
}
}
FOR_PROVIDES(p, pp, dep)
{
if (MAPTST(&xpctx->installed, p))
return 1;
}
return 0;
}
static int
prune_neg_prefers(ExpanderCtx *xpctx, Id who, Id *e, int n)
{
Expander *xp = xpctx->xp;
Pool *pool = xpctx->pool;
Id whon = who ? pool->solvables[who].name : 0;
int i, j;
for (i = j = 0; i < n; i++)
{
Id p = e[i];
Id 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;
}
e[j++] = p;
}
return j ? j : n;
}
static int
prune_pos_prefers(ExpanderCtx *xpctx, Id who, Id *e, int n, int domulti)
{
Expander *xp = xpctx->xp;
Queue *pruneq = &xpctx->pruneq;
Pool *pool = xpctx->pool;
Id whon = who ? pool->solvables[who].name : 0;
int i, j;
if (pruneq->count)
queue_empty(pruneq);
for (i = j = 0; i < n; i++)
{
Id p = e[i];
Id pn = pool->solvables[p].name;
if (MAPTST(&xp->preferpos, pn))
queue_push2(pruneq, pn, p);
else 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(pruneq, xid, p);
}
}
if (!pruneq->count)
return n;
if (pruneq->count > 2)
{
if (!domulti)
return n;
/* pos prefers are ordered, the first one wins */
for (i = 0; i < xp->preferposq.count; i++)
{
Id xid = xp->preferposq.elements[i];
for (j = 0; j < pruneq->count; j += 2)
if (pruneq->elements[j] == xid)
{
e[0] = pruneq->elements[j + 1];
return 1;
}
}
}
e[0] = pruneq->elements[1]; /* simple case, just one prefer */
return 1;
}
static int
prune_or_dep(ExpanderCtx *xpctx, Id dep, Id *e, int n)
{
Pool *pool = xpctx->pool;
int i, j;
Id p, pp;
for (;;)
{
Reldep *rd = 0;
if (ISRELDEP(dep))
{
rd = GETRELDEP(pool, dep);
if (rd->flags != REL_OR)
rd = 0;
}
if (rd)
dep = rd->name;
i = j = 0;
/* both sets are ordered */
FOR_PROVIDES(p, pp, dep)
{
if (p < e[i])
continue;
while (i < n && p > e[i])
i++;
if (i == n)
break;
if (p == e[i])
e[j++] = p;
}
if (j)
return j;
if (rd)
dep = rd->evr;
else
break;
}
return n;
}
static int
prune_supplemented(ExpanderCtx *xpctx, Id *e, int n)
{
Pool *pool = xpctx->pool;
int i, j;
Id sup, *supp;
for (i = j = 0; i < n; i++)
{
Id p = e[i];
Solvable *s = pool->solvables + p;
if (!s->supplements)
continue;
supp = s->repo->idarraydata + s->supplements;
while ((sup = *supp++) != 0)
if (expander_dep_fulfilled(xpctx, sup))
break;
if (sup)
e[j++] = p;
}
return j ? j : n;
}
static void
add_recommended_packages(ExpanderCtx *xpctx, Solvable *s)
{
Pool *pool = xpctx->pool;
Id p, pp, rec, *recp;
for (recp = s->repo->idarraydata + s->recommends; (rec = *recp++) != 0; )
{
int haveone = 0;
if (pool_is_complex_dep(pool, rec))
{
expander_installed_complexdep(xpctx, s - pool->solvables, rec, DEPTYPE_RECOMMENDS);
continue;
}
FOR_PROVIDES(p, pp, rec)
{
if (MAPTST(&xpctx->installed, p))
break;
if (haveone)
continue;
if (xpctx->conflicts.size && MAPTST(&xpctx->conflicts, p))
continue;
if (pool->solvables[p].conflicts && expander_checkconflicts(xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0, 0) != 0)
continue;
if (pool->solvables[p].obsoletes && expander_checkconflicts(xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1, 0) != 0)
continue;
haveone = 1;
}
if (p)
continue; /* already fulfilled */
if (haveone)
queue_push2(&xpctx->todo, rec, s - pool->solvables);
}
}
static void
expander_growmaps(Expander *xp)
{
Pool *pool = xp->pool;
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);
}
static Id
str2id_dup(Pool *pool, const char *str)
{
char buf[256];
size_t l = strlen(str);
if (l < 256) {
memcpy(buf, str, l + 1);
return pool_str2id(pool, buf, 1);
} else {
return pool_str2id(pool, pool_tmpjoin(pool, str, 0, 0), 1);
}
}
static void
add_noproviderinfo(ExpanderCtx *xpctx, Id dep, Id who)
{
Pool *pool = xpctx->pool;
Reldep *rd, *prd;
Id p, pp, prov, *provp;
int nprovinfo;
if (xpctx->xp->debug)
{
if (who)
expander_dbg(xpctx->xp, "nothing provides %s needed by %s\n", pool_dep2str(pool, dep), expander_solvid2str(xpctx->xp, who));
else
expander_dbg(xpctx->xp, "nothing provides %s\n", pool_dep2str(pool, dep));
}
if (!ISRELDEP(dep))
return;
rd = GETRELDEP(pool, dep);
if (rd->flags >= 8 || ISRELDEP(rd->name) || ISRELDEP(rd->evr))
return;
nprovinfo = 0;
FOR_PROVIDES(p, pp, rd->name)
{
Solvable *s = pool->solvables + p;
if (!s->repo || !s->provides)
continue;
for (provp = s->repo->idarraydata + s->provides; (prov = *provp++) != 0; )
{
if (!ISRELDEP(prov))
continue;
prd = GETRELDEP(pool, prov);
if (prd->name != rd->name || ISRELDEP(prd->evr))
continue;
queue_push(&xpctx->errors, ERROR_NOPROVIDERINFO);
if (prd->name == s->name && prd->evr == s->evr)
{
if (xpctx->xp->debug)
expander_dbg(xpctx->xp, "%s has version %s\n", expander_solvid2str(xpctx->xp, p), pool_id2str(pool, prd->evr));
queue_push2(&xpctx->errors, prd->evr, 0);
}
else
{
if (xpctx->xp->debug)
expander_dbg(xpctx->xp, "%s provides version %s\n", expander_solvid2str(xpctx->xp, p), pool_id2str(pool, prd->evr));
queue_push2(&xpctx->errors, prd->evr, p);
}
if (++nprovinfo >= 4)
return; /* only show the first 4 providers */
}
}
}
static int
expander_expand(Expander *xp, Queue *in, Queue *indep, Queue *out, Queue *ignoreq, int options)
{
ExpanderCtx xpctx;
Pool *pool = xp->pool;
Queue toinstall;
Queue qq, choices;
Solvable *s;
Id q, p, pp;
int i, j, nerrors;
int ti, tj, tc;
Id todoid, id, who, whon;
Id conflprovpc;
int pass;
Queue revertignore;
int oldignoreignore = xp->ignoreignore;
Map oldignored, oldignoredx;
int ignoremapssaved = 0;
int dorecstart = 0;
memset(&xpctx, 0, sizeof(xpctx));
xpctx.xp = xp;
xpctx.pool = pool;
xpctx.out = out;
xpctx.ignoreignore = options & EXPANDER_OPTION_IGNOREIGNORE ? 1 : xp->ignoreignore;
xpctx.ignoreconflicts = options & EXPANDER_OPTION_IGNORECONFLICTS ? 1 : xp->ignoreconflicts;
xpctx.userecommendsforchoices = options & EXPANDER_OPTION_USERECOMMENDSFORCHOICES ? 1 : xp->userecommendsforchoices;
xpctx.usesupplementsforchoices = options & EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES ? 1 : xp->usesupplementsforchoices;
xpctx.dorecommends = options & EXPANDER_OPTION_DORECOMMENDS ? 1 : xp->dorecommends;
xpctx.dosupplements = options & EXPANDER_OPTION_DOSUPPLEMENTS ? 1 : xp->dosupplements;
map_init(&xpctx.installed, pool->nsolvables);
map_init(&xpctx.conflicts, 0);
map_init(&xpctx.recommended, 0);
queue_init(&xpctx.conflictsinfo);
queue_init(&xpctx.todo);
queue_init(&xpctx.todo_cond);
map_init(&xpctx.todo_condmap, 0);
queue_init(&xpctx.errors);
queue_init(&xpctx.cplxq);
queue_init(&xpctx.cplxblks);
queue_init(&xpctx.pruneq);
queue_init(&toinstall);
queue_init(&qq);
queue_init(&choices);
queue_init(&revertignore);
queue_empty(out);
/* process ignored. hack: we mess with the ignore config in xp */
xp->ignoreignore = 0;
if (xpctx.ignoreignore && ignoreq->count)
{
/* bad: have direct ignores and we need to zero the 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 (ignoreq->count)
{
/* mix direct ignores with ignores from project config */
for (i = 0; i < ignoreq->count; i++)
{
const char *ss;
id = ignoreq->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 = str2id_dup(pool, ss + 1);
MAPEXP(&xp->ignoredx, id);
if (MAPTST(&xp->ignoredx, id))
continue;
MAPSET(&xp->ignoredx, id);
queue_push(&revertignore, -id);
}
}
}
else if (xpctx.ignoreignore)
{
/* no direct ignores, ignore project config ignores.
* easy: just disable ignore processing */
xp->ignoreignore = 1;
}
/* grow maps to make bit tests cheaper */
expander_growmaps(xp);
/* process standard dependencies */
if (indep)
{
for (i = 0; i < indep->count; i += 2)
{
int deptype = indep->elements[i];
Id dep = indep->elements[i + 1];
if (ISRELDEP(dep) && GETRELDEP(pool, dep)->flags == REL_ERROR)
{
queue_push(&xpctx.errors, ERROR_BADDEPENDENCY);
queue_push2(&xpctx.errors, GETRELDEP(pool, dep)->evr, 0);
continue;
}
if ((deptype == DEPTYPE_REQUIRES || deptype == DEPTYPE_CONFLICTS) && pool_is_complex_dep(pool, dep))
{
expander_installed_complexdep(&xpctx, 0, dep, deptype);
continue;
}
if (deptype == DEPTYPE_REQUIRES)
{
queue_push2(&xpctx.todo, dep, 0);
}
else if (deptype == DEPTYPE_CONFLICTS || deptype == DEPTYPE_OBSOLETES)
{
FOR_PROVIDES(p, pp, dep)
{
if (deptype == DEPTYPE_OBSOLETES && !pool_match_nevr(pool, pool->solvables + p, dep))
continue;
MAPEXP(&xpctx.conflicts, pool->nsolvables);
MAPSET(&xpctx.conflicts, p);
}
}
}
}
/* process direct dependencies */
for (i = 0; i < in->count; i++)
{
id = in->elements[i];
if (ISRELDEP(id) && GETRELDEP(pool, id)->flags == REL_ERROR)
{
queue_push(&xpctx.errors, ERROR_BADDEPENDENCY);
queue_push2(&xpctx.errors, GETRELDEP(pool, id)->evr, 0);
continue;
}
if (pool_is_complex_dep(pool, id))
{
expander_installed_complexdep(&xpctx, 0, id, DEPTYPE_REQUIRES);
continue;
}
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)
{
queue_push2(&xpctx.todo, id, 0); /* unclear, resolve later */
continue;
}
if (xp->debug)
expander_dbg(xp, "added %s because of %s (direct dep)\n", expander_solvid2name(xp, q), pool_dep2str(pool, id));
queue_push(&toinstall, q);
}
/* unify toinstall, check against conflicts */
for (i = 0; i < toinstall.count; i++)
{
p = toinstall.elements[i];
MAPSET(&xpctx.installed, p);
}
for (i = j = 0; i < toinstall.count; i++)
{
p = toinstall.elements[i];
if (!MAPTST(&xpctx.installed, p))
continue;
MAPCLR(&xpctx.installed, p);
toinstall.elements[j++] = p;
}
queue_truncate(&toinstall, j);
if (xpctx.conflicts.size)
{
for (i = 0; i < toinstall.count; i++)
{
p = toinstall.elements[i];
if (MAPTST(&xpctx.conflicts, p))
findconflictsinfo(&xpctx, p, 2);
}
}
/* here is the big expansion loop */
pass = 0;
while (!xpctx.errors.count)
{
if (toinstall.count)
{
expander_installed_multiple(&xpctx, &toinstall);
pass = 0;
continue;
}
if (!xpctx.todo.count)
{
/* almost finished. now do weak deps if requested */
pass = 0;
if (xpctx.dorecommends)
{
expander_dbg(xp, "--- now doing recommended packages\n");
for (; dorecstart < out->count; dorecstart++)
{
s = pool->solvables + out->elements[dorecstart];
if (s->recommends)
add_recommended_packages(&xpctx, s);
}
if (xpctx.todo.count)
continue;
}
if (xpctx.dosupplements)
{
Id sup, *supp;
expander_dbg(xp, "--- now doing supplemented packages\n");
for (p = 1; p < pool->nsolvables; p++)
{
s = pool->solvables + p;
if (!s->supplements || !s->repo)
continue;
if (MAPTST(&xpctx.installed, p))
continue;
if (!pool_installable(pool, s))
continue;
if (xpctx.conflicts.size && MAPTST(&xpctx.conflicts, p))
continue;
if (s->conflicts && expander_checkconflicts(&xpctx, p, s->repo->idarraydata + s->conflicts, 0, 0) != 0)
continue;
if (s->obsoletes && expander_checkconflicts(&xpctx, p, s->repo->idarraydata + s->obsoletes, 1, 0) != 0)
continue;
supp = s->repo->idarraydata + s->supplements;
while ((sup = *supp++) != 0)
if (expander_dep_fulfilled(&xpctx, sup))
break;
if (!sup)
continue;
expander_dbg(xp, "added %s because it supplements %s\n", expander_solvid2name(xp, p), pool_dep2str(pool, sup));
queue_push(&toinstall, p);
}
if (toinstall.count)
continue;
}
/* no new stuff to do, we're finished! */
break;
}
expander_dbg(xp, "--- now doing normal dependencies\n");
if (pass == 1)
queue_empty(&choices);
for (ti = tj = 0; ti < xpctx.todo.count; ti += 2)
{
int deptype = DEPTYPE_REQUIRES;
todoid = id = xpctx.todo.elements[ti];
who = xpctx.todo.elements[ti + 1];
if (!id) /* deleted entry? */
continue;
queue_empty(&qq);
if (ISCPLX(pool, id))
{
pp = GETCPLX(pool, id); /* p, dep, deptype, ids... */
id = xpctx.cplxblks.elements[pp + 1];
deptype = xpctx.cplxblks.elements[pp + 2];
pp += 3;
while ((p = xpctx.cplxblks.elements[pp++]))
if (p > 0)
queue_push(&qq, p);
}
else
{
FOR_PROVIDES(p, pp, id)
queue_push(&qq, p);
}
if (qq.count == 0)
{
if (deptype == DEPTYPE_RECOMMENDS)
continue;
queue_push(&xpctx.errors, ERROR_NOPROVIDER);
queue_push2(&xpctx.errors, id, who);
add_noproviderinfo(&xpctx, id, who);
continue;
}
/* check installed and ignores */
whon = who ? pool->solvables[who].name : 0;
for (i = 0; i < qq.count; i++)
{
p = qq.elements[i];
if (MAPTST(&xpctx.installed, p))
break;
if (who && deptype == DEPTYPE_REQUIRES && !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 (i < qq.count)
continue; /* ignored dependency or fulfilled */
if (pass == 0 && qq.count > 1)
{
xpctx.todo.elements[tj++] = todoid;
xpctx.todo.elements[tj++] = who;
continue;
}
/* do conflict pruning */
conflprovpc = 0;
for (i = j = 0; i < qq.count; i++)
{
Id pc;
p = qq.elements[i];
if (xpctx.conflicts.size && MAPTST(&xpctx.conflicts, p))
{
if (xp->debug)
findconflictsinfo(&xpctx, p, 0);
conflprovpc = 0;
continue;
}
if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(&xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0, 0)) != 0)
{
conflprovpc = pc;
continue;
}
if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(&xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1, 0)) != 0)
{
conflprovpc = -pc;
continue;
}
qq.elements[j++] = p;
}
if (j == 0)
{
if (deptype == DEPTYPE_RECOMMENDS)
continue;
queue_push(&xpctx.errors, ERROR_CONFLICTINGPROVIDERS);
queue_push2(&xpctx.errors, id, who);
if (qq.count == 1 && conflprovpc != 1 && conflprovpc != -1)
{
p = qq.elements[0];
if (conflprovpc)
{
queue_push(&xpctx.errors, ERROR_PROVIDERINFO);
queue_push2(&xpctx.errors, p, conflprovpc);
continue;
}
findconflictsinfo(&xpctx, p, 1);
continue;
}
/* even more work if all providers conflict */
for (j = 0; j < qq.count; j++)
{
p = qq.elements[j];
if (xpctx.conflicts.size && MAPTST(&xpctx.conflicts, p))
findconflictsinfo(&xpctx, p, 1);
if (pool->solvables[p].conflicts)
expander_checkconflicts(&xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0, 1);
if (pool->solvables[p].obsoletes)
expander_checkconflicts(&xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1, 1);
}
continue;
}
queue_truncate(&qq, j);
if (qq.count == 1)
{
p = qq.elements[0];
if (xp->debug)
expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
queue_push(&toinstall, p);
continue;
}
/* pass is == 1 and we have multiple choices */
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");
}
queue_push2(&choices, qq.count + 3, id);
queue_push(&choices, qq.count);
queue_insertn(&choices, choices.count, qq.count, qq.elements);
xpctx.todo.elements[tj++] = todoid;
xpctx.todo.elements[tj++] = who;
}
queue_truncate(&xpctx.todo, tj);
if (toinstall.count)
continue;
if (!xpctx.todo.count)
continue;
/* did not find a package to install, only choices left on todo list */
if (pass == 0)
{
pass = 1; /* now do conflict pruning */
continue;
}
expander_dbg(xp, "--- now doing undecided dependencies\n");
/* prune prefers */
for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
{
Id who = xpctx.todo.elements[ti + 1];
Id *qe = choices.elements + tc + 3;
Id id = choices.elements[tc + 1];
int qn = choices.elements[tc + 2];
whon = who ? pool->solvables[who].name : 0;
if (qn > 1)
qn = prune_neg_prefers(&xpctx, who, qe, qn);
if (qn > 1)
qn = prune_pos_prefers(&xpctx, who, qe, qn, 0);
if (qn == 1)
{
p = qe[0];
if (xp->debug)
expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
queue_push(&toinstall, p);
xpctx.todo.elements[ti] = 0; /* kill entry */
}
choices.elements[tc + 2] = qn;
tc += choices.elements[tc];
}
if (toinstall.count)
continue;
/* prune pos prefers with domulti and debian or */
for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
{
Id who = xpctx.todo.elements[ti + 1];
Id *qe = choices.elements + tc + 3;
Id id = choices.elements[tc + 1];
int qn = choices.elements[tc + 2];
whon = who ? pool->solvables[who].name : 0;
if (qn > 1)
qn = prune_pos_prefers(&xpctx, who, qe, qn, 1);
if (qn > 1 && pool->disttype != DISTTYPE_RPM)
{
if (ISRELDEP(id) && GETRELDEP(pool, id)->flags == REL_OR)
qn = prune_or_dep(&xpctx, id, qe, qn);
}
if (qn == 1)
{
p = qe[0];
if (xp->debug)
expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
queue_push(&toinstall, p);
xpctx.todo.elements[ti] = 0; /* kill entry */
}
choices.elements[tc + 2] = qn;
tc += choices.elements[tc];
}
if (toinstall.count)
continue;
/* prune recommended packages */
if (xpctx.userecommendsforchoices)
expander_updaterecommendedmap(&xpctx);
if (xpctx.recommended.size)
{
expander_dbg(xp, "now doing undecided dependencies with recommends\n");
for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
{
Id who = xpctx.todo.elements[ti + 1];
Id *qe = choices.elements + tc + 3;
Id id = choices.elements[tc + 1];
int qn = choices.elements[tc + 2];
whon = who ? pool->solvables[who].name : 0;
for (i = j = 0; i < qn; i++)
if (MAPTST(&xpctx.recommended, qe[i]))
qe[j++] = qe[i];
if (j)
qn = j;
if (qn == 1)
{
p = qe[0];
if (xp->debug)
expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
queue_push(&toinstall, p);
xpctx.todo.elements[ti] = 0; /* kill entry */
}
choices.elements[tc + 2] = qn;
tc += choices.elements[tc];
}
if (toinstall.count)
continue;
}
if (xpctx.usesupplementsforchoices)
{
expander_dbg(xp, "now doing undecided dependencies with supplements\n");
for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
{
Id who = xpctx.todo.elements[ti + 1];
Id *qe = choices.elements + tc + 3;
Id id = choices.elements[tc + 1];
int qn = choices.elements[tc + 2];
whon = who ? pool->solvables[who].name : 0;
qn = prune_supplemented(&xpctx, qe, qn);
if (qn == 1)
{
p = qe[0];
if (xp->debug)
expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
queue_push(&toinstall, p);
xpctx.todo.elements[ti] = 0; /* kill entry */
}
choices.elements[tc + 2] = qn;
tc += choices.elements[tc];
}
if (toinstall.count)
continue;
}
/* nothing more to prune. record errors. */
for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
{
Id who = xpctx.todo.elements[ti + 1];
Id *qe = choices.elements + tc + 3;
Id id = choices.elements[tc + 1];
int qn = choices.elements[tc + 2];
queue_push(&xpctx.errors, ERROR_CHOICE);
queue_push2(&xpctx.errors, id, who);
for (i = 0; i < qn; i++)
queue_push(&xpctx.errors, qe[i]);
queue_push(&xpctx.errors, 0);
tc += choices.elements[tc];
}
}
/* free data */
map_free(&xpctx.installed);
map_free(&xpctx.conflicts);
map_free(&xpctx.recommended);
map_free(&xpctx.todo_condmap);
queue_free(&xpctx.conflictsinfo);
queue_free(&xpctx.todo_cond);
queue_free(&xpctx.todo);
queue_free(&toinstall);
queue_free(&qq);
queue_free(&choices);
queue_free(&xpctx.pruneq);
queue_free(&xpctx.cplxq);
queue_free(&xpctx.cplxblks);
/* 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);
/* finish return queue, count errors */
nerrors = 0;
if (xpctx.errors.count)
{
queue_empty(out);
queue_insertn(out, 0, xpctx.errors.count, xpctx.errors.elements);
for (i = 0; i < out->count; i += 3)
{
nerrors++;
if (out->elements[i] == ERROR_CHOICE)
while (out->elements[i + 3])
i++;
}
}
queue_free(&xpctx.errors);
return nerrors;
}
static Expander *
expander_create(Pool *pool, Queue *preferpos, Queue *preferneg, Queue *ignore, Queue *conflict, Queue *fileprovides, int debug, int options)
{
Expander *xp;
int i, j;
Id id, id2;
const char *str;
Queue q;
xp = calloc(sizeof(Expander), 1);
xp->pool = pool;
xp->debug = debug;
xp->ignoreignore = options & EXPANDER_OPTION_IGNOREIGNORE ? 1 : 0;
xp->ignoreconflicts = options & EXPANDER_OPTION_IGNORECONFLICTS ? 1 : 0;
xp->userecommendsforchoices = options & EXPANDER_OPTION_USERECOMMENDSFORCHOICES ? 1 : 0;
xp->usesupplementsforchoices = options & EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES ? 1 : 0;
xp->dorecommends = options & EXPANDER_OPTION_DORECOMMENDS ? 1 : 0;
xp->dosupplements = options & EXPANDER_OPTION_DOSUPPLEMENTS ? 1 : 0;
queue_init(&xp->preferposq);
for (i = 0; i < preferpos->count; i++)
{
id = preferpos->elements[i];
queue_push(&xp->preferposq, id);
MAPEXP(&xp->preferpos, id);
MAPSET(&xp->preferpos, id);
if ((str = strchr(pool_id2str(pool, id), ':')) != 0)
{
id = str2id_dup(pool, str + 1);
MAPEXP(&xp->preferposx, id);
MAPSET(&xp->preferposx, id);
}
}
for (i = 0; i < preferneg->count; i++)
{
id = preferneg->elements[i];
MAPEXP(&xp->preferneg, id);
MAPSET(&xp->preferneg, id);
if ((str = strchr(pool_id2str(pool, id), ':')) != 0)
{
id = str2id_dup(pool, str + 1);
MAPEXP(&xp->prefernegx, id);
MAPSET(&xp->prefernegx, id);
}
}
for (i = 0; i < ignore->count; i++)
{
id = ignore->elements[i];
MAPEXP(&xp->ignored, id);
MAPSET(&xp->ignored, id);
if ((str = strchr(pool_id2str(pool, id), ':')) != 0)
{
id = str2id_dup(pool, str + 1);
MAPEXP(&xp->ignoredx, id);
MAPSET(&xp->ignoredx, id);
}
}
queue_init(&xp->conflictsq);
for (i = 0; i < conflict->count; i += 2)
{
id = conflict->elements[i];
id2 = conflict->elements[i + 1];
queue_push2(&xp->conflictsq, id, id2);
MAPEXP(&xp->conflicts, id);
MAPSET(&xp->conflicts, id);
MAPEXP(&xp->conflicts, id2);
MAPSET(&xp->conflicts, id2);
}
if (fileprovides->count)
xp->havefileprovides = 1;
queue_init(&q);
for (i = 0; i < fileprovides->count; i++)
{
Id p, pp;
id = fileprovides->elements[i];
int havenew = 0;
/* XXX: this modifies the pool, which is somewhat unclean! */
/* get old providers */
queue_empty(&q);
FOR_PROVIDES(p, pp, id)
queue_push(&q, p);
for (j = i + 1; j < fileprovides->count && (id2 = fileprovides->elements[j]) != 0; j++)
{
FOR_PROVIDES(p, pp, id2)
{
int k;
if (pool->solvables[p].name != id2)
continue; /* match name only */
/* insert sorted */
for (k = 0; ; k++)
{
if (k == q.count || q.elements[k] > p)
{
queue_insert(&q, k, p);
havenew = 1;
break;
}
if (q.elements[k] == p)
break;
}
}
}
if (havenew)
pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
i = j;
}
queue_free(&q);
return xp;
}
static void
expander_free(Expander *xp)
{
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);
}
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 >= 12 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz") || !strcmp(s + sl - 12, ".pkg.tar.zst")))
disttype = DISTTYPE_ARCH;
#endif
if (disttype >= 0 && pool->disttype != disttype)
set_disttype(pool, disttype);
}
static inline const char *
solvid2name(Pool *pool, Id p)
{
return pool_id2str(pool, pool->solvables[p].name);
}
#define ISNOARCH(arch) (arch == ARCH_NOARCH || arch == ARCH_ALL || arch == ARCH_ANY)
static int
has_keyname(Repo *repo, Id keyname)
{
Repodata *data;
int rdid;
FOR_REPODATAS(repo, rdid, data)
if (repodata_has_keyname(data, keyname))
return 1;
return 0;
}
static inline int
match_modules_req(Pool *pool, Id id)
{
const char *dep = pool_id2str(pool, id);
Id *modules;
if (strncmp(dep, "platform", 8) == 0 && (dep[8] == 0 || dep[8] == '-'))
return 1;
for (modules = pool->appdata; *modules; modules++)
{
const char *name, *rname;
if (*modules == id)
return 1;
name = pool_id2str(pool, *modules);
if ((rname = strrchr(name, '-')) == 0 || rname == name)
continue;
if (!strncmp(dep, rname, rname - name) && dep[rname - name] == 0)
return 1;
}
return 0;
}
static void
create_module_map(Repo *repo, Map *modulemap, Queue *modulemapq)
{
Pool *pool = repo->pool;
Id *modules = pool->appdata;
int i, have_moduleinfo = 0;
Id id, p, *pp;
Solvable *s;
if (!modulemap->size)
map_grow(modulemap, pool->ss.nstrings);
if (!modules)
return;
if (!*modules)
{
map_setall(modulemap);
return;
}
/* clear old bits */
if (modulemapq->count)
{
for (i = 0; i < modulemapq->count; i++)
MAPCLR(modulemap, modulemapq->elements[i]);
queue_empty(modulemapq);
}
for (modules = pool->appdata; *modules; modules++)
MAPSET(modulemap, *modules);
/* look for module information stored in "buildservice:modules" solvables */
FOR_REPO_SOLVABLES(repo, p, s)
{
if (s->name != buildservice_modules || s->arch != ARCH_SRC)
continue;
have_moduleinfo = 1;
if (s->evr >= 1 && s->evr < pool->ss.nstrings && MAPTST(modulemap, s->evr))
{
queue_push(modulemapq, s->evr); /* directly addressed */
continue;
}
id = s->repo->idarraydata[s->provides];
if (id < 1 || id >= pool->ss.nstrings || !MAPTST(modulemap, id))
continue; /* not what we're looking for */
for (pp = s->repo->idarraydata + s->requires; (id = *pp) != 0; pp++)
{
/* check if the dep is fulfilled by any module in the list */
if (id < 1 || id >= pool->ss.nstrings)
break; /* hey! */
if (!MAPTST(modulemap, id) && !match_modules_req(pool, id))
break; /* could not fulfil requires */
}
if (id)
continue; /* could not fulfil one of the requires, ignore module */
queue_push(modulemapq, s->evr);
}
if (!have_moduleinfo)
{
/* old style repo with no moduleinfo at all. simple use the unexpanded ids */
for (modules = pool->appdata; *modules; modules++)
queue_push(modulemapq, *modules);
return;
}
for (modules = pool->appdata; *modules; modules++)
MAPCLR(modulemap, *modules);
for (i = 0; i < modulemapq->count; i++)
MAPSET(modulemap, modulemapq->elements[i]);
}
static int
in_module_map(Pool *pool, Map *modulemap, Queue *modules)
{
int i;
for (i = 0; i < modules->count; i++)
{
Id id = modules->elements[i];
if (id > 1 && id < pool->ss.nstrings && MAPTST(modulemap, id))
return 1;
}
return 0;
}
static void
create_considered(Pool *pool, Repo *repoonly, Map *considered, int unorderedrepos)
{
Id p, pb,*best;
Solvable *s, *sb;
int ridx;
Repo *repo;
int olddisttype = -1;
int dodrepo;
int mayhave_modules;
Queue modules;
Map modulemap;
Queue modulemapq;
int modulemap_uptodate;
map_init(considered, pool->nsolvables);
best = solv_calloc(sizeof(Id), pool->ss.nstrings);
queue_init(&modules);
map_init(&modulemap, 0);
queue_init(&modulemapq);
FOR_REPOS(ridx, repo)
{
if (repoonly && repo != repoonly)
continue;
dodrepo = repo_lookup_str(repo, SOLVID_META, buildservice_dodurl) != 0;
mayhave_modules = has_keyname(repo, buildservice_modules) ? 1 : 0;
modulemap_uptodate = 0;
FOR_REPO_SOLVABLES(repo, p, s)
{
int inmodule = 0;
if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
continue;
pb = best[s->name];
sb = pb ? pool->solvables + pb : 0;
if (mayhave_modules)
{
solvable_lookup_idarray(s, buildservice_modules, &modules);
inmodule = modules.count ? 1 : 0;
if (inmodule)
{
if (!modulemap_uptodate)
{
create_module_map(repo, &modulemap, &modulemapq);
modulemap_uptodate = 1;
}
if (!in_module_map(pool, &modulemap, &modules))
continue; /* nope, ignore package */
}
}
if (unorderedrepos && sb && s->repo->priority != sb->repo->priority)
{
if (s->repo->priority < sb->repo->priority)
continue; /* lower prio, ignore */
}
else if (sb)
{
int sbinmodule = 0;
/* we already have that name. decide which one to take */
if (!unorderedrepos && s->repo != sb->repo)
continue; /* first repo wins */
if (s->repo == sb->repo && mayhave_modules)
sbinmodule = solvable_lookup_type(sb, buildservice_modules) ? 1 : 0;
if (inmodule != sbinmodule)
{
if (inmodule < sbinmodule)
continue;
}
else if (s->evr != sb->evr)
{
/* 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)
r = strcmp(pool_id2str(pool, sb->evr), pool_id2str(pool, s->evr));
if (r >= 0)
continue;
}
else if (s->arch != sb->arch)
{
/* same versions, check arch */
if (ISNOARCH(sb->arch) && !ISNOARCH(s->arch))
continue;
if (ISNOARCH(sb->arch) || !ISNOARCH(s->arch))
{
int r;
/* 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;
}
}
}
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 identical 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);
queue_free(&modules);
map_free(&modulemap);
queue_free(&modulemapq);
if (olddisttype >= 0 && pool->disttype != olddisttype)
set_disttype(pool, olddisttype);
}
struct metaline {
char *l; /* pointer to line */
int lastoff; /* line offset of last path element */
int nslash; /* number of slashes */
int killed; /* 1: line has been killed. 2: because of a cycle package */
};
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;
}
static char *
slurp(FILE *fp, int *lenp)
{
int l, ll;
char *buf = 0;
int bufl = 0;
for (l = 0; ; l += ll)
{
if (bufl - l < 4096)
{
bufl += 4096;
if (bufl < 0)
{
buf = solv_free(buf);
l = 0;
break;
}
buf = solv_realloc(buf, bufl);
}
ll = fread(buf + l, 1, bufl - l, fp);
if (ll < 0)
{
buf = solv_free(buf);
l = 0;
break;
}
if (ll == 0)
{
buf[l] = 0; /* always zero-terminate */
break;
}
}
if (lenp)
*lenp = l;
return buf;
}
Id
repo_add_obsbinlnk(Repo *repo, const char *path, int flags)
{
Repodata *data;
FILE *fp;
char *buf;
int len;
SV *sv;
unsigned char *src;
STRLEN srcl;
Id p;
if ((fp = fopen(path, "r")) == 0)
return 0;
buf = slurp(fp, &len);
fclose(fp);
if (!buf || len <= 0)
return 0;
src = (unsigned char *)buf;
srcl = len;
sv = 0;
if (srcl >= 7 && src[0] == 'p' && src[1] == 's' && src[2] == 't' && src[3] == '0' && (src[4] & 1) == 1 && src[4] >= 5) {
src += 6;
srcl -= 6;
sv = retrieve(&src, &srcl, 0);
}
free(buf);
if (!sv)
return 0;
if (SvTYPE(sv) != SVt_PVHV)
{
SvREFCNT_dec(sv);
return 0;
}
data = repo_add_repodata(repo, flags);
p = data2pkg(repo, data, (HV *)sv, 0);
SvREFCNT_dec(sv);
if (!(flags & REPO_NO_INTERNALIZE))
repodata_internalize(data);
return p;
}
#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);
#if defined(LIBSOLVEXT_FEATURE_DEBIAN)
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);
#endif
else if (sl >= 10 && !strcmp(s + sl - 10, ".obsbinlnk"))
{
p = repo_add_obsbinlnk(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION);
/* do not overwrite location from obsbinlnk file */
solv_free(path);
if (p)
repodata_set_str(data, p, buildservice_id, sid);
return p;
}
#if defined(LIBSOLVEXT_FEATURE_ARCHREPO) && defined(ARCH_ADD_WITH_PKGID)
else if (sl >= 12 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz") || !strcmp(s + sl - 12, ".pkg.tar.zst")))
p = repo_add_arch_pkg(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|ARCH_ADD_WITH_PKGID);
#endif
solv_free(path);
if (!p)
return 0;
#if REPO_NO_LOCATION != 0
repodata_set_location(data, p, 0, 0, s);
#else
if ((sp = strrchr(s, '/')) != 0)
{
*sp = 0;
repodata_set_str(data, p, SOLVABLE_MEDIADIR, s);
*sp = '/';
}
else
repodata_delete_uninternalized(data, p, SOLVABLE_MEDIADIR);
#endif
repodata_set_str(data, p, buildservice_id, sid);
return p;
}
static int
subpack_sort_cmp(const void *ap, const void *bp, void *dp)
{
Pool *pool = (Pool *)dp;
const Id *a = ap;
const Id *b = bp;
int r = a[1] - b[1];
if (r)
return r;
r = strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
return r ? r : a[0] - b[0];
}
/* This is an OpenSSL-compatible implementation of the RSA Data Security,
* Inc. MD5 Message-Digest Algorithm.
*
* Written by Solar Designer <solar@openwall.com> in 2001, and placed in
* the public domain. */
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define STEP(f, a, b, c, d, x, t, s) \
(a) += f((b), (c), (d)) + (x) + (t); \
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
(a) += (b);
#if defined(__i386__) || defined(__vax__)
#define SET(n) \
(*(MD5_u32plus *)&ptr[(n) * 4])
#define GET(n) \
SET(n)
#else
#define SET(n) \
(ctx->block[(n)] = \
(MD5_u32plus)ptr[(n) * 4] | \
((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
#define GET(n) \
(ctx->block[(n)])
#endif
typedef unsigned long MD5_u32plus;
typedef struct {
MD5_u32plus lo, hi;
MD5_u32plus a, b, c, d;
unsigned char buffer[64];
MD5_u32plus block[16];
} MD5_CTX;
/*
* This processes one or more 64-byte data blocks, but does NOT update
* the bit counters. There're no alignment requirements.
*/
static void *md5_body(MD5_CTX *ctx, void *data, unsigned long size)
{
unsigned char *ptr;
MD5_u32plus a, b, c, d;
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
ptr = data;
a = ctx->a;
b = ctx->b;
c = ctx->c;
d = ctx->d;
do {
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
/* Round 1 */
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
/* Round 2 */
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
/* Round 3 */
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
/* Round 4 */
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
ptr += 64;
} while (size -= 64);
ctx->a = a;
ctx->b = b;
ctx->c = c;
ctx->d = d;
return ptr;
}
static void md5_init(MD5_CTX *ctx)
{
ctx->a = 0x67452301;
ctx->b = 0xefcdab89;
ctx->c = 0x98badcfe;
ctx->d = 0x10325476;
ctx->lo = 0;
ctx->hi = 0;
}
static void md5_update(MD5_CTX *ctx, void *data, unsigned long size)
{
MD5_u32plus saved_lo;
unsigned long used, free;
saved_lo = ctx->lo;
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
ctx->hi++;
ctx->hi += size >> 29;
used = saved_lo & 0x3f;
if (used) {
free = 64 - used;
if (size < free) {
memcpy(&ctx->buffer[used], data, size);
return;
}
memcpy(&ctx->buffer[used], data, free);
data = (unsigned char *)data + free;
size -= free;
md5_body(ctx, ctx->buffer, 64);
}
if (size >= 64) {
data = md5_body(ctx, data, size & ~(unsigned long)0x3f);
size &= 0x3f;
}
memcpy(ctx->buffer, data, size);
}
static void md5_final(MD5_CTX *ctx, unsigned char *result)
{
unsigned long used, free;
used = ctx->lo & 0x3f;
ctx->buffer[used++] = 0x80;
free = 64 - used;
if (free < 8) {
memset(&ctx->buffer[used], 0, free);
md5_body(ctx, ctx->buffer, 64);
used = 0;
free = 64;
}
memset(&ctx->buffer[used], 0, free - 8);
ctx->lo <<= 3;
ctx->buffer[56] = ctx->lo;
ctx->buffer[57] = ctx->lo >> 8;
ctx->buffer[58] = ctx->lo >> 16;
ctx->buffer[59] = ctx->lo >> 24;
ctx->buffer[60] = ctx->hi;
ctx->buffer[61] = ctx->hi >> 8;
ctx->buffer[62] = ctx->hi >> 16;
ctx->buffer[63] = ctx->hi >> 24;
md5_body(ctx, ctx->buffer, 64);
result[0] = ctx->a;
result[1] = ctx->a >> 8;
result[2] = ctx->a >> 16;
result[3] = ctx->a >> 24;
result[4] = ctx->b;
result[5] = ctx->b >> 8;
result[6] = ctx->b >> 16;
result[7] = ctx->b >> 24;
result[8] = ctx->c;
result[9] = ctx->c >> 8;
result[10] = ctx->c >> 16;
result[11] = ctx->c >> 24;
result[12] = ctx->d;
result[13] = ctx->d >> 8;
result[14] = ctx->d >> 16;
result[15] = ctx->d >> 24;
memset(ctx, 0, sizeof(*ctx));
}
static unsigned int buz_noise[256] =
{
0x9be502a4U, 0xba7180eaU, 0x324e474fU, 0x0aab8451U, 0x0ced3810U,
0x2158a968U, 0x6bbd3771U, 0x75a02529U, 0x41f05c14U, 0xc2264b87U,
0x1f67b359U, 0xcd2d031dU, 0x49dc0c04U, 0xa04ae45cU, 0x6ade28a7U,
0x2d0254ffU, 0xdec60c7cU, 0xdef5c084U, 0x0f77ffc8U, 0x112021f6U,
0x5f6d581eU, 0xe35ea3dfU, 0x3216bfb4U, 0xd5a3083dU, 0x7e63e9cdU,
0xaa9208f6U, 0xda3f3978U, 0xfe0e2547U, 0x09dfb020U, 0xd97472c5U,
0xbbce2edeU, 0x121aebd2U, 0x0e9fdbebU, 0x7b6f5d9cU, 0x84938e43U,
0x30694f2dU, 0x86b7a7f8U, 0xefaf5876U, 0x263812e6U, 0xb6e48ddfU,
0xce8ed980U, 0x4df591e1U, 0x75257b35U, 0x2f88dcffU, 0xa461fe44U,
0xca613b4dU, 0xd9803f73U, 0xea056205U, 0xccca7a89U, 0x0f2dbb07U,
0xc53e359eU, 0xe80d0137U, 0x2b2d2a5dU, 0xcfc1391aU, 0x2bb3b6c5U,
0xb66aea3cU, 0x00ea419eU, 0xce5ada84U, 0xae1d6712U, 0x12f576baU,
0x117fcbc4U, 0xa9d4c775U, 0x25b3d616U, 0xefda65a8U, 0xaff3ef5bU,
0x00627e68U, 0x668d1e99U, 0x088d0eefU, 0xf8fac24dU, 0xe77457c7U,
0x68d3beb4U, 0x921d2acbU, 0x9410eac9U, 0xd7f24399U, 0xcbdec497U,
0x98c99ae1U, 0x65802b2cU, 0x81e1c3c4U, 0xa130bb09U, 0x17a87badU,
0xa70367d6U, 0x148658d4U, 0x02f33377U, 0x8620d8b6U, 0xbdac25bdU,
0xb0a6de51U, 0xd64c4571U, 0xa4185ba0U, 0xa342d70fU, 0x3f1dc4c1U,
0x042dc3ceU, 0x0de89f43U, 0xa69b1867U, 0x3c064e11U, 0xad1e2c3eU,
0x9660e8cdU, 0xd36b09caU, 0x4888f228U, 0x61a9ac3cU, 0xd9561118U,
0x3532797eU, 0x71a35c22U, 0xecc1376cU, 0xab31e656U, 0x88bd0d35U,
0x423b20ddU, 0x38e4651cU, 0x3c6397a4U, 0x4a7b12d9U, 0x08b1cf33U,
0xd0604137U, 0xb035fdb8U, 0x4916da23U, 0xa9349493U, 0xd83daa9bU,
0x145f7d95U, 0x868531d6U, 0xacb18f17U, 0x9cd33b6fU, 0x193e42b9U,
0x26dfdc42U, 0x5069d8faU, 0x5bee24eeU, 0x5475d4c6U, 0x315b2c0cU,
0xf764ef45U, 0x01b6f4ebU, 0x60ba3225U, 0x8a16777cU, 0x4c05cd28U,
0x53e8c1d2U, 0xc8a76ce5U, 0x8045c1e6U, 0x61328752U, 0x2ebad322U,
0x3444f3e2U, 0x91b8af11U, 0xb0cee675U, 0x55dbff5aU, 0xf7061ee0U,
0x27d7d639U, 0xa4aef8c9U, 0x42ff0e4fU, 0x62755468U, 0x1c6ca3f3U,
0xe4f522d1U, 0x2765fcb3U, 0xe20c8a95U, 0x3a69aea7U, 0x56ab2c4fU,
0x8551e688U, 0xe0bc14c2U, 0x278676bfU, 0x893b6102U, 0xb4f0ab3bU,
0xb55ddda9U, 0xa04c521fU, 0xc980088eU, 0x912aeac1U, 0x08519badU,
0x991302d3U, 0x5b91a25bU, 0x696d9854U, 0x9ad8b4bfU, 0x41cb7e21U,
0xa65d1e03U, 0x85791d29U, 0x89478aa7U, 0x4581e337U, 0x59bae0b1U,
0xe0fc9df3U, 0x45d9002cU, 0x7837464fU, 0xda22de3aU, 0x1dc544bdU,
0x601d8badU, 0x668b0abcU, 0x7a5ebfb1U, 0x3ac0b624U, 0x5ee16d7dU,
0x9bfac387U, 0xbe8ef20cU, 0x8d2ae384U, 0x819dc7d5U, 0x7c4951e7U,
0xe60da716U, 0x0c5b0073U, 0xb43b3d97U, 0xce9974edU, 0x0f691da9U,
0x4b616d60U, 0x8fa9e819U, 0x3f390333U, 0x6f62fad6U, 0x5a32b67cU,
0x3be6f1c3U, 0x05851103U, 0xff28828dU, 0xaa43a56aU, 0x075d7dd5U,
0x248c4b7eU, 0x52fde3ebU, 0xf72e2edaU, 0x5da6f75fU, 0x2f5148d9U,
0xcae2aeaeU, 0xfda6f3e5U, 0xff60d8ffU, 0x2adc02d2U, 0x1dbdbd4cU,
0xd410ad7cU, 0x8c284aaeU, 0x392ef8e0U, 0x37d48b3aU, 0x6792fe9dU,
0xad32ddfaU, 0x1545f24eU, 0x3a260f73U, 0xb724ca36U, 0xc510d751U,
0x4f8df992U, 0x000b8b37U, 0x292e9b3dU, 0xa32f250fU, 0x8263d144U,
0xfcae0516U, 0x1eae2183U, 0xd4af2027U, 0xc64afae3U, 0xe7b34fe4U,
0xdf864aeaU, 0x80cc71c5U, 0x0e814df3U, 0x66cc5f41U, 0x853a497aU,
0xa2886213U, 0x5e34a2eaU, 0x0f53ba47U, 0x718c484aU, 0xfa0f0b12U,
0x33cc59ffU, 0x72b48e07U, 0x8b6f57bcU, 0x29cf886dU, 0x1950955bU,
0xcd52910cU, 0x4cecef65U, 0x05c2cbfeU, 0x49df4f6aU, 0x1f4c3f34U,
0xfadc1a09U, 0xf2d65a24U, 0x117f5594U, 0xde3a84e6U, 0x48db3024U,
0xd10ca9b5U
};
/*
* our delta search blocksize
*
* smaller gives more hits, but increases the hash size
*
* must be a multiple of 256
* must be in range [256,32767]
*/
#define DELTA_BSIZE 1024
/* min store block len, smaller blocks are encoded as direct data */
#define MIN_BSIZE 32
/* min meta block len, must be >= 10 */
#define MIN_BSIZE_META 32
/* max meta block len, must be <= DELTA_BSIZE */
#define MAX_BSIZE_META DELTA_BSIZE
/* number of slots per data area */
#define SLOTS_PER_AREA 4095
/* buzhash by Robert C. Uzgalis */
/* General hash functions. Technical Report TR-92-01, The University
of Hong Kong, 1993 */
static unsigned int buzhash(unsigned char *buf)
{
unsigned int x = 0x83d31df4U;
int i;
for (i = DELTA_BSIZE ; i != 0; i--)
x = (x << 1) ^ (x & (1 << 31) ? 1 : 0) ^ buz_noise[*buf++];
return x;
}
static void md5block(unsigned char *buf, int len, unsigned char *out)
{
MD5_CTX ctx;
md5_init(&ctx);
md5_update(&ctx, buf, (unsigned long)len);
md5_final(&ctx, out);
}
#define HASHCHAIN_START 7
#define HASHCHAIN_NEXT(h, hh, mask) (((h) + (hh)++) & (mask))
struct deltastore {
int fd; /* file descriptor */
int rdonly; /* store is read only */
unsigned long long end; /* store file size */
unsigned long long *offsets; /* the data areas we know about */
int noffsets;
unsigned char *hash; /* our hash */
unsigned int hm; /* size of hash */
unsigned int hf; /* hash fill */
unsigned int hd; /* entries not in hash */
int freecnt; /* free slots in last slot area */
int usedcnt; /* used slots in last slot area */
unsigned long long slotsoffset; /* offset of last slot area */
};
struct deltaout {
FILE *fp;
struct deltastore *store;
/* for block coalescence */
unsigned long long oldoffset;
unsigned long long oldsize;
/* for offset encoding */
unsigned long long lastoffset;
/* for meta block creation */
int outbuf_do_meta; /* create meta blocks */
unsigned char outbuf[MAX_BSIZE_META + 16]; /* 16 extra bytes for one encoded block */
int outbuf_len;
/* offset patching */
unsigned long long outbuf_startoffset;
int outbuf_startoffset_set;
int outbuf_set_len1;
int outbuf_set_len2;
unsigned long long outbuf_set_offset; /* offset we need to patch in, already encoded */
};
static inline unsigned long long getu48(unsigned char *d)
{
unsigned long long x = d[0] << 8 | d[1];
return (x << 32) | (d[2] << 24 | d[3] << 16 | d[4] << 8 | d[5]);
}
static inline void putu48(unsigned char *d, unsigned long long x)
{
d[0] = x >> 40;
d[1] = x >> 32;
d[2] = x >> 24;
d[3] = x >> 16;
d[4] = x >> 8;
d[5] = x;
}
static inline unsigned int getu32(unsigned char *d)
{
return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
}
static inline void putu32(unsigned char *d, unsigned int x)
{
d[0] = x >> 24;
d[1] = x >> 16;
d[2] = x >> 8;
d[3] = x;
}
/**
** store handling
**/
static int
finddataarea(struct deltastore *store, unsigned long long offset)
{
int i;
for (i = 0; i < store->noffsets; i += 2)
if (offset >= store->offsets[i] && offset < store->offsets[i + 1])
return i;
return -1;
}
static void
adddataarea(struct deltastore *store, unsigned long long start, unsigned long long end)
{
unsigned long long *newoffsets;
if (store->noffsets && store->offsets[store->noffsets - 1] == start)
{
store->offsets[store->noffsets - 1] = end;
return;
}
if (store->offsets)
newoffsets = realloc(store->offsets, (store->noffsets + 2) * sizeof(unsigned long long));
else
newoffsets = malloc((store->noffsets + 2) * sizeof(unsigned long long));
if (!newoffsets)
return;
newoffsets[store->noffsets++] = start;
newoffsets[store->noffsets++] = end;
store->offsets = newoffsets;
}
static int
addslotarea(struct deltastore *store, int cnt)
{
unsigned char *slots;
if (!cnt || cnt > 65535)
return 0;
if (store->rdonly)
return 0;
if ((store->end & 4095) != 0) /* pad to multiple of 4096 */
{
char pad[4096];
int l = 4096 - (store->end & 4095);
memset(pad, 0, l);
if (pwrite(store->fd, pad, l, store->end) != l)
{
perror("pwrite pad next slotsarea");
return 0;
}
store->end += l;
}
if (store->end + (cnt + 1) * 16 >= (1LL << 48))
return 0; /* store too big! */
slots = calloc(cnt + 1, 16);
if (!slots)
return 0;
memcpy(slots, "OBSDELT", 8);
slots[8] = cnt >> 8;
slots[9] = cnt;
/* write pointer to next slot area */
if (store->end)
{
putu48(slots + 10, store->end);
if (pwrite(store->fd, slots, 16, store->slotsoffset) != 16)
{
perror("pwrite update next slotsarea");
free(slots);
return 0;
}
memset(slots + 10, 0, 6);
}
if (pwrite(store->fd, slots, (cnt + 1) * 16, store->end) != (cnt + 1) * 16)
{
perror("pwrite new slotarea");
free(slots);
return 0;
}
free(slots);
store->slotsoffset = store->end;
store->end += (cnt + 1) * 16;
store->freecnt = cnt;
store->usedcnt = 0;
return 1;
}
/*
* add a new block to the store.
* returns the store offset, 0 on error
*/
static unsigned long long
putinstore(struct deltastore *store, unsigned char *buf, int size, unsigned char *md5, unsigned int hx)
{
unsigned char md5buf[16];
unsigned char hp[16];
unsigned long long offset;
unsigned char *hash;
unsigned int h, hh, hm;
if (!size || size > DELTA_BSIZE)
return 0;
if (store->rdonly)
return 0;
if (store->freecnt == 0 && !addslotarea(store, SLOTS_PER_AREA))
return 0;
/* write data */
offset = store->end;
if (offset + size >= (1LL << 48))
return 0; /* store too big! */
if (pwrite(store->fd, buf, size, store->end) != size)
{
perror("pwrite data");
return 0;
}
adddataarea(store, store->end, store->end + size);
store->end += size;
/* write slot */
if (!md5)
{
md5block(buf, size, md5buf);
md5 = md5buf;
}
hp[0] = size >> 8;
hp[1] = size;
putu48(hp + 2, offset);
if (size == DELTA_BSIZE)
{
if (!hx)
hx = buzhash(buf);
putu32(hp + 8, hx);
memcpy(hp + 12, md5, 4);
}
else
{
hp[0] |= 0x80; /* small block marker */
memcpy(hp + 8, md5, 8);
hx = getu32(hp + 8); /* needed later */
}
#if 0
{
int j;
printf("NEW SLOT");
for (j = 0; j < 16; j++)
printf(" %02x", hp[j]);
printf("\n");
}
#endif
if (pwrite(store->fd, hp, 16, store->slotsoffset + (store->usedcnt + 1) * 16) != 16)
{
perror("pwrite slot");
return 0;
}
store->freecnt--;
store->usedcnt++;
/* update hash */
hm = store->hm;
hash = store->hash;
h = hx & hm;
hh = HASHCHAIN_START;
while (hash[16 * h] != 0)
h = HASHCHAIN_NEXT(h, hh, hm);
memcpy(hash + 16 * h, hp, 16);
store->hf++;
return offset;
}
/* make sure that we found the correct block */
static int
checkstore(struct deltastore *store, unsigned long long offset, unsigned char *buf, int size)
{
unsigned char buf2[4096];
while (size)
{
int l = size > 4096 ? 4096 : size;
if (pread(store->fd, buf2, l, offset) != l)
return 0;
if (memcmp(buf2, buf, l) != 0)
return 0;
size -= l;
buf += l;
offset += l;
}
return 1;
}
/*
* try to find a (non-rolling) block in the store. If not found, add it.
* returns the store offset, 0 on error
*/
static unsigned long long
reuse_or_add_block(struct deltastore *store, unsigned char *buf, int size)
{
unsigned char md5[16];
unsigned int h, hh, hm;
unsigned char *hash;
unsigned long long offset;
if (!size || size >= DELTA_BSIZE)
return 0; /* only small non-rolling blocks for now */
md5block(buf, size, md5);
hm = store->hm;
hash = store->hash;
h = (md5[0] << 24 | md5[1] << 16 | md5[2] << 8 | md5[3]) & hm;
hh = HASHCHAIN_START;
while (hash[16 * h] != 0)
{
unsigned char *hp = hash + 16 * h;
if (((hp[0] & 0x7f) << 8 | hp[1]) == size && !memcmp(hp + 8, md5, 8))
{
offset = getu48(hp + 2);
if (checkstore(store, offset, buf, size))
return offset;
}
h = HASHCHAIN_NEXT(h, hh, hm);
}
return putinstore(store, buf, size, md5, 0);
}
/**
** block encoding
**/
static int
encodelonglong(FILE *ofp, unsigned long long x)
{
unsigned long long z = 1;
int c;
do
{
z = z << 7 | (x & 127);
x >>= 7;
}
while (x);
for (;;)
{
c = (z & 127) | 128;
z >>= 7;
if (z == 1)
break;
if (putc(c, ofp) == EOF)
return 0;
}
if (putc(c ^ 128, ofp) == EOF)
return 0;
return 1;
}
static int
encodelonglong_mem(unsigned char *bp, unsigned long long x)
{
unsigned long long z = 1;
int c;
int l = 0;
do
{
z = z << 7 | (x & 127);
x >>= 7;
}
while (x);
for (;;)
{
c = (z & 127) | 128;
z >>= 7;
if (z == 1)
break;
*bp++ = c;
l++;
}
*bp = c ^ 128;;
return l + 1;
}
#if 1
/* fancy delta conversion, seems to work better than the simple xor */
static inline unsigned long long
encodeoffset(unsigned long long oldoffset, unsigned long long offset)
{
if (oldoffset & (1LL << 47))
{
offset ^= ((1LL << 48) - 1);
oldoffset ^= ((1LL << 48) - 1);
}
if (offset < oldoffset * 2)
{
if (offset < oldoffset)
offset = (oldoffset - offset - 1) << 1 | 1;
else
offset = (offset - oldoffset) << 1;
}
return offset;
}
static inline unsigned long long
decodeoffset(unsigned long long oldoffset, unsigned long long offset)
{
int neg = oldoffset & (1LL << 47) ? ((1LL << 48) - 1) : 0;
oldoffset ^= neg;
if (offset < oldoffset * 2)
{
if (offset & 1)
offset = oldoffset - ((offset >> 1) + 1);
else
offset = oldoffset + (offset >> 1);
}
return offset ^ neg;
}
#else
static inline unsigned long long
encodeoffset(unsigned long long oldoffset, unsigned long long offset)
{
return oldoffset ^ offset;
}
static inline unsigned long long
decodeoffset(unsigned long long oldoffset, unsigned long long offset)
{
return oldoffset ^ offset;
}
#endif
static int
flushoutbuf(struct deltaout *out)
{
if (!out->outbuf_len)
return 1;
if (out->outbuf_len >= MAX_BSIZE_META)
return 0;
if (out->outbuf_len >= MIN_BSIZE_META)
{
/* put as meta block into store! */
int size = out->outbuf_len;
unsigned long long offset;
#if 0
printf("META size %d\n", out->outbuf_len);
#endif
offset = reuse_or_add_block(out->store, out->outbuf, size);
if (!offset)
return 0;
/* encode meta block into outbuf */
if (out->outbuf_startoffset_set)
out->lastoffset = out->outbuf_startoffset;
out->outbuf[0] = 15; /* meta */
out->outbuf_len = 1;
out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, size);
out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, encodeoffset(out->lastoffset, offset));
out->lastoffset = offset + size;
out->outbuf_startoffset_set = 0;
}
if (out->outbuf_startoffset_set)
{
/* tricky: fix up first offset! */
unsigned char buf[9];
int l = encodelonglong_mem(buf, out->outbuf_set_offset);
if (fwrite(out->outbuf, out->outbuf_set_len1, 1, out->fp) != 1)
return 0;
if (fwrite(buf, l, 1, out->fp) != 1)
return 0;
if (out->outbuf_set_len2 < out->outbuf_len && fwrite(out->outbuf + out->outbuf_set_len2, out->outbuf_len - out->outbuf_set_len2, 1, out->fp) != 1)
return 0;
}
else if (fwrite(out->outbuf, out->outbuf_len, 1, out->fp) != 1)
return 0;
out->outbuf_len = 0;
out->outbuf_startoffset_set = 0;
return 1;
}
static int
encodestoreblock_real(struct deltaout *out, unsigned long long offset, unsigned long long size)
{
#if 0
printf("BLOCK %#llx %llu\n", offset, size);
#endif
if (out->outbuf_do_meta)
{
int lastlen = out->outbuf_len;
/* the following code is needed as we want to use a lastoffset of
* zero if this ends up in a meta instruction. So we encode with
* lastoffset zero but also store the real lastoffset and byte offsets,
* in order to fix up the offset in flushbuf */
int set = out->outbuf_startoffset_set;
if (!set)
{
out->outbuf_startoffset_set = 1;
out->outbuf_startoffset = out->lastoffset;
out->outbuf_set_offset = encodeoffset(out->lastoffset, offset);
out->lastoffset = 0;
}
out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, out->oldsize + 256);
if (!set)
out->outbuf_set_len1 = out->outbuf_len;
out->outbuf_len += encodelonglong_mem(out->outbuf + out->outbuf_len, encodeoffset(out->lastoffset, offset));
if (!set)
out->outbuf_set_len2 = out->outbuf_len;
if (out->outbuf_len >= DELTA_BSIZE)
{
/* buffer too full. revert changes. flush outbuf. retry */
out->outbuf_len = lastlen;
if (!set)
{
out->outbuf_startoffset_set = 0;
out->lastoffset = out->outbuf_startoffset;
}
if (!flushoutbuf(out))
return 0;
return encodestoreblock_real(out, offset, size);
}
}
else
{
if (!encodelonglong(out->fp, size + 256))
return 0;
if (!encodelonglong(out->fp, encodeoffset(out->lastoffset, offset)))
return 0;
}
out->lastoffset = offset + size;
return 1;
}
/* encode a store block instruction
* we delay the real encoding so that we can join adjacent blocks
*/
static int
encodestoreblock(struct deltaout *out, unsigned long long offset, unsigned long long size)
{
if (out->oldoffset)
{
if (out->oldoffset + out->oldsize == offset)
{
out->oldsize += size;
return 1;
}
if (!encodestoreblock_real(out, out->oldoffset, out->oldsize))
return 0;
}
out->oldoffset = offset; /* block not yet written */
out->oldsize = size;
return 1;
}
/* encode a direct data instruction
*/
static int
encodedirect(struct deltaout *out, unsigned char *buf, int size)
{
if (!size)
return 1;
if (size >= 256 - 16)
return 0;
if (out->oldoffset)
{
if (!encodestoreblock(out, 0, 0)) /* flush */
return 0;
}
#if 0
printf("DIRECT %u\n", size);
#endif
if (out->outbuf_do_meta)
{
if (out->outbuf_len + 1 + size >= DELTA_BSIZE)
{
/* buffer too full. flush outbuf */
if (!flushoutbuf(out))
return 0;
}
out->outbuf[out->outbuf_len++] = 16 + size;
memcpy(out->outbuf + out->outbuf_len, buf, size);
out->outbuf_len += size;
}
else
{
if (putc(16 + size, out->fp) == EOF)
return 0;
if (fwrite(buf, size, 1, out->fp) != 1)
return 0;
}
return 1;
}
/**
** the delta algorithm
**/
static unsigned long long
extendblock(struct deltastore *store, FILE *fp, unsigned long long offset, unsigned long long areaend, unsigned long long maxextend)
{
unsigned char buf[1024];
int c, i, bufl;
unsigned long long extend = 0;
if (offset >= areaend)
return 0;
if (areaend - offset < maxextend)
maxextend = areaend - offset;
if (!maxextend)
return 0;
i = bufl = 0;
for (;;)
{
if (i == bufl)
{
bufl = maxextend > 1024 ? 1024 : maxextend;
if (bufl == 0)
return extend;
if (pread(store->fd, buf, bufl, (off_t)offset) != bufl)
return extend;
offset += bufl;
maxextend -= bufl;
i = 0;
}
c = getc(fp);
if (c == EOF)
return extend;
if (buf[i++] != c)
{
ungetc(c, fp);
return extend;
}
extend++;
}
}
static unsigned long long
extendblock_back(struct deltastore *store, unsigned char *data, unsigned long long offset, unsigned long long areastart, unsigned long long maxextend)
{
unsigned char buf[1024];
unsigned long long extend = 0;
int bufl;
if (offset <= areastart)
return 0;
if (offset - areastart < maxextend)
maxextend = offset - areastart;
if (!maxextend)
return 0;
bufl = 0;
for (;;)
{
if (bufl == 0)
{
bufl = maxextend > 1024 ? 1024 : maxextend;
if (bufl == 0)
return extend;
offset -= bufl;
if (pread(store->fd, buf, bufl, (off_t)offset) != bufl)
return extend;
maxextend -= bufl;
}
if (*--data != buf[--bufl])
return extend;
extend++;
}
}
static int
dosimple(struct deltastore *store, struct deltaout *out, unsigned char *buf, int size)
{
unsigned long long offset;
while (size >= DELTA_BSIZE)
{
offset = putinstore(store, buf, DELTA_BSIZE, 0, 0);
if (!offset || !encodestoreblock(out, offset, DELTA_BSIZE))
return 0;
size -= DELTA_BSIZE;
buf += DELTA_BSIZE;
}
if (size < MIN_BSIZE)
return encodedirect(out, buf, size);
offset = reuse_or_add_block(store, buf, size);
if (!offset)
return 0;
return encodestoreblock(out, offset, size);
}
static int
dodelta(struct deltastore *store, FILE *fp, struct deltaout *out, unsigned long long size)
{
unsigned char buf[DELTA_BSIZE * 16];
unsigned char md5[16];
unsigned long long offset, extendf, extendb;
unsigned int h, hh, hm, hx;
unsigned char *hash;
int c, foundit, bi;
#if 0
printf("DODELTA\n");
#endif
hm = store->hm;
hash = store->hash;
while (size)
{
if (size < DELTA_BSIZE)
{
if (fread(buf, (int)size, 1, fp) != 1)
return 0;
if (!dosimple(store, out, buf, (int)size))
return 0;
break;
}
/* read a block */
bi = 0;
if (fread(buf, DELTA_BSIZE, 1, fp) != 1)
return 0;
size -= DELTA_BSIZE;
hx = buzhash(buf);
foundit = 0;
/* start rolling */
for (;;)
{
int md5set = 0;
/* check if the block at (buf + bi) is in the hash */
#if 0
if (hx != buzhash(buf + bi))
abort();
#endif
hh = HASHCHAIN_START;
for (h = hx & hm; hash[16 * h] != 0; h = HASHCHAIN_NEXT(h, hh, hm))
{
unsigned char *hp = hash + 16 * h;
/* first check block len */
if (hp[0] != (DELTA_BSIZE >> 8) || hp[1] != (DELTA_BSIZE & 0xff))
continue;
/* then check complete hash value */
if (hp[8] != (hx >> 24))
continue;
if ((hp[8] << 24 | hp[9] << 16 | hp[10] << 8 | hp[11]) != hx)
continue;
/* then check strong hash */
if (!md5set)
{
md5block(buf + bi, DELTA_BSIZE, md5);
md5set = 1;
}
if (memcmp(hp + 12, md5, 4) != 0)
continue;
/* looks good. check data */
offset = getu48(hp + 2);
if (!checkstore(store, offset, buf + bi, DELTA_BSIZE))
continue;
/* yes! found block in store! */
/* try to extend found block */
c = finddataarea(store, offset);
extendf = extendb = 0;
if (c >= 0)
{
extendf = extendblock(store, fp, offset + DELTA_BSIZE, store->offsets[c + 1], size);
size -= extendf; /* extended bytes */
extendb = extendblock_back(store, buf + bi, offset, store->offsets[c], bi);
offset -= extendb;
bi -= extendb;
}
/* encode data before block */
if (bi)
{
if (!dosimple(store, out, buf, bi))
return 0;
bi = 0;
}
/* encode */
if (!encodestoreblock(out, offset, DELTA_BSIZE + extendf + extendb))
return 0;
foundit = 1;
break;
}
if (foundit)
break;
/* not found. move block one byte */
if (!size)
{
if (!dosimple(store, out, buf, bi + DELTA_BSIZE))
return 0;
break;
}
c = fgetc(fp);
if (c == EOF)
return 0;
size--;
buf[DELTA_BSIZE + bi] = c;
hx = (hx << 1) ^ (hx & (1 << 31) ? 1 : 0) ^ buz_noise[c];
c = buf[bi++];
hx ^= buz_noise[c] ^ (0x83d31df4U ^ 0x07a63be9U);
if (bi == sizeof(buf) - DELTA_BSIZE)
{
/* trim down, but leave one block for backward extension */
if (!dosimple(store, out, buf, bi - DELTA_BSIZE))
return 0;
/* no overlap as the buffer is >= 4 * DELTA_BSIZE */
memcpy(buf, buf + bi - DELTA_BSIZE, 2 * DELTA_BSIZE);
bi = DELTA_BSIZE;
}
}
}
if (!encodestoreblock(out, 0, 0)) /* flush */
return 0;
if (!flushoutbuf(out))
return 0;
return 1;
}
static int
readdeltastore(struct deltastore *store, int fd, int rdonly, unsigned long long xsize)
{
unsigned char *slots;
unsigned char oneslot[16];
unsigned long long offset, nextoffset, lastgoodoffset;
struct stat st;
unsigned long long fsize;
unsigned int nslots = 0, hslots;
unsigned char *hash;
unsigned int hm, h, hh, hf, hd;
int isbad = 0;
int i, lasti, cnt, maxcnt = 0;
unsigned int drop = 0;
memset(store, 0, sizeof(*store));
store->fd = fd;
store->rdonly = rdonly;
if (fstat(fd, &st))
{
perror("fstat");
return 0;
}
fsize = (unsigned long long)st.st_size;
store->end = fsize;
/* first pass: find number of used entries */
offset = 0;
lastgoodoffset = -1;
for (;;)
{
if (offset == fsize)
break;
if (offset + 16 > fsize)
{
fprintf(stderr, "WARNING: slot area exceeds file size!\n");
isbad = 1;
break;
}
if (pread(fd, oneslot, 16, offset) != 16)
return 0;
if (memcmp(oneslot, "OBSDELT", 8) != 0)
{
fprintf(stderr, "WARNING: slot magic error!\n");
isbad = 1;
break;
}
cnt = oneslot[8] << 8 | oneslot[9];
nextoffset = getu48(oneslot + 10);
if (!nextoffset)
nextoffset = fsize;
offset += (cnt + 1) * 16;
if (offset > fsize)
{
fprintf(stderr, "WARNING: slot area exceeds file size!\n");
isbad = 1;
break;
}
nslots += cnt;
lastgoodoffset = offset - (cnt + 1) * 16;
if (cnt > maxcnt)
maxcnt = cnt;
if (offset > nextoffset)
{
fprintf(stderr, "WARNING: end of slots bigger than nextoffset!\n");
isbad = 1;
break;
}
offset = nextoffset;
}
if (isbad && !store->rdonly)
{
fprintf(stderr, "WARNING: fixing up bad slots!\n");
if (lastgoodoffset == -1)
{
/* worst case: first slots area is damaged */
memset(oneslot, 0, 16);
memcpy(oneslot, "OBSDELT", 8);
putu48(oneslot + 10, fsize);
if (pwrite(store->fd, oneslot, 16, 0) != 16)
{
perror("pwrite repair first slots area");
return 0;
}
}
else
{
putu48(oneslot + 10, fsize);
if (pwrite(store->fd, oneslot + 10, 6, lastgoodoffset + 10) != 6)
{
perror("pwrite repair bad slots area nextoffset");
return 0;
}
}
isbad = 0;
}
slots = calloc(maxcnt + 1, 16);
if (!slots)
return 0;
/* find good hash size and allocate hash */
hslots = nslots + xsize / 512;
while (hslots & (hslots - 1))
hslots = hslots & (hslots - 1);
if (hslots < 16384)
hslots = 16384; /* 1 MByte min mem */
while (hslots > 128 * 1024 * 1024) /* 8 GByte max mem */
{
/* oh no. max size reached. drop half of slots */
hslots >>= 1;
drop += (drop ? drop : nslots) / 2;
}
hslots *= 4;
store->hm = hm = hslots - 1;
store->hash = hash = calloc(hm + 1, 16);
if (!hash)
{
fprintf(stderr, "could not allocate hash (%u MB)\n", (hm + 1) / (1024 * 1024 / 16));
free(slots);
return 0;
}
/* second pass: fill the hash */
offset = 0;
hf = hd = 0;
for (;;)
{
int toread = 16 * (maxcnt + 1);
if (isbad && lastgoodoffset == -1)
break;
if (offset >= fsize)
break;
if (offset + toread > fsize)
toread = fsize - offset;
if (pread(fd, slots, toread, offset) != toread)
{
free(slots);
return 0;
}
if (memcmp(slots, "OBSDELT", 8) != 0)
break;
cnt = oneslot[8] << 8 | oneslot[9];
offset += 16 * (cnt + 1);
nextoffset = getu48(slots + 10);
if (!nextoffset)
nextoffset = fsize;
if (offset > nextoffset)
break;
if (offset != nextoffset)
adddataarea(store, offset, nextoffset);
lasti = 0;
for (i = 1; i < cnt + 1; i++)
if (slots[16 * i])
{
unsigned char *hp = slots + 16 * i;
int len = (hp[0] & 127) << 8 | hp[1];
unsigned long long o = getu48(hp + 2);
lasti = i;
if (drop)
{
drop--;
hd++;
}
else if (o >= offset && o + len <= nextoffset)
{
/* a good one. add to hash. */
h = getu32(hp + 8) & hm;
hh = HASHCHAIN_START;
while (hash[16 * h] != 0)
h = HASHCHAIN_NEXT(h, hh, hm);
memcpy(hash + 16 * h, hp, 16);
hf++;
}
}
store->slotsoffset = offset - 16 * (cnt + 1);
store->freecnt = cnt - lasti;
store->usedcnt = lasti;
if (isbad && lastgoodoffset == store->slotsoffset)
break;
offset = nextoffset;
}
store->hf = hf;
store->hd = hd;
#if 0
printf("readdeltastore: have %d entries, %d dropped, hash size %d\n", hf, hd, hm + 1);
#endif
free(slots);
return 1;
}
static void
printdeltastorestats(struct deltastore *store)
{
unsigned int buckets[2048];
unsigned int hm, hf, hd;
unsigned char *hp;
int i, j, bc = 16;
memset(buckets, 0, sizeof(buckets));
hm = store->hm;
hf = store->hf;
hd = store->hd;
printf("store size: %llu (%u MB)\n", store->end, (unsigned int)(store->end / (1024 * 1024)));
printf("hash mask: 0x%x (%u MB hash mem)\n", hm, (unsigned int)(hm / 65536) + 1);
printf("hash entries set: %u (%.2f %%)\n", hf, ((double)hf * 100) / ((double)hm + 1));
printf("hash entries dropped: %u (%.2f %%)\n", hd, hd ? ((double)hd * 100) / ((double)hf + (double)hd) : 0.);
for (hp = store->hash;; hp += 16)
{
if (hp[0])
buckets[((hp[0] & 0x7f) << 8 | hp[1]) / 16]++;
if (!hm--)
break;
}
for (i = 2047; i >= 1; i--)
if (buckets[i])
break;
i++;
while (i > 16)
{
for (j = 0; j < i; j += 2)
buckets[j / 2] = buckets[j] + buckets[j + 1];
i = (i + 1) / 2;
bc *= 2;
}
printf("block stats:\n");
for (j = 0; j < i; j++)
printf(" size %#6x - %#6x: %10u\n", j * bc, j * bc + bc - 1, buckets[j]);
printf("data areas: %d\n", store->noffsets / 2);
}
static void
freedeltastore(struct deltastore *store)
{
if (store->hash)
free(store->hash);
if (store->offsets)
free(store->offsets);
}
static void
settimes(int fd, struct stat *st)
{
struct timeval tv[2];
tv[0].tv_sec = st->st_atime;
tv[0].tv_usec = 0;
tv[1].tv_sec = st->st_mtime;
tv[1].tv_usec = 0;
futimes(fd, tv);
}
static int
checkhexcomp(unsigned char *buf)
{
int i, hexcomp = 0;
for (i = 0; i < 110; i++)
{
int c = *buf++;
if (c >= '0' && c <= '9')
continue;
else if (c >= 'A' && c <= 'F')
{
if (!hexcomp)
hexcomp = 1;
if (hexcomp != 1)
break;
}
else if (c >= 'a' && c <= 'f')
{
if (!hexcomp)
hexcomp = 2;
if (hexcomp != 2)
break;
}
else
break;
}
if (i < 110)
return 0;
return hexcomp ? hexcomp : 1;
}
static unsigned int fromhex(unsigned char *hex)
{
int i;
unsigned int x = 0;
for (i = 0; i < 8; i++, hex++)
{
if (*hex >= '0' && *hex <= '9')
x = x << 4 | (*hex - '0');
else if (*hex >= 'a' && *hex <= 'f')
x = x << 4 | (*hex - ('a' - 10));
else if (*hex >= 'A' && *hex <= 'F')
x = x << 4 | (*hex - ('A' - 10));
}
return x;
}
static void
magic_inode_increment(unsigned char *cpio)
{
unsigned int inode = getu32(cpio + 3);
if (inode)
putu32(cpio + 3, inode + 1);
}
static int
makedelta(struct deltastore *store, FILE *fp, FILE *ofp, unsigned long long fpsize)
{
unsigned char cpiohead[1024 + 16384];
unsigned char oldcpio[1024];
int trailerseen = 0;
int i, j;
struct deltaout out;
if (fpsize >= (1LL << 40))
return 0;
/* init deltaout struct */
memset(&out, 0, sizeof(out));
out.fp = ofp;
out.store = store;
out.outbuf_do_meta = 1; /* create meta blocks */
/* write our header */
memset(cpiohead, 0, 32);
memcpy(cpiohead, "OBScpio", 8);
putu48(cpiohead + 10, fpsize);
if (fwrite(cpiohead, 16, 1, ofp) != 1)
return 0;
memset(oldcpio, 0, 1024);
while (!trailerseen)
{
unsigned long long fsize;
unsigned int hsize, nsize;
int run;
int hexcomp;
int noff = 110;
int zero;
/* read the header */
if (fread(cpiohead, 110, 1, fp) != 1)
{
fprintf(stderr, "cpio header read error\n");
return 0;
}
if (memcmp(cpiohead, "070701", 6) != 0)
{
fprintf(stderr, "not a newc cpio archive\n");
return 0;
}
fsize = fromhex(cpiohead + 54);
nsize = fromhex(cpiohead + 94);
if (nsize > 16384)
{
fprintf(stderr, "filename size too big\n");
return 0;
}
hsize = noff + nsize;
if (hsize & 3)
hsize += 4 - (hsize & 3);
/* check if we can do hex compression */
hexcomp = checkhexcomp(cpiohead);
if (hexcomp)
{
/* do fancy hex compression */
cpiohead[0] = 0x07;
cpiohead[1] = 0x07;
cpiohead[2] = 0x01;
for (i = 3; i < 55; i += 4)
{
unsigned int x = fromhex(cpiohead + i * 2);
putu32(cpiohead + i, x);
}
noff -= 55;
hsize -= 55;
}
#if 0
printf("fsize = %d, nsize = %d, hsize = %d\n", fsize, nsize, hsize);
#endif
if (fread(cpiohead + noff, hsize - noff, 1, fp) != 1)
{
fprintf(stderr, "cpio header read error\n");
return 0;
}
if (fsize == 0 && nsize == 11 && !memcmp(cpiohead + noff, "TRAILER!!!", 11))
{
trailerseen = 1;
while (hsize < sizeof(cpiohead))
{
int c = getc(fp);
if (c == EOF)
break;
cpiohead[hsize++] = c;
}
if (hsize == sizeof(cpiohead))
{
fprintf(stderr, "excess trailer data\n");
return 0;
}
}
/* check trailing zero */
for (zero = 0; zero < 4; zero++)
if (cpiohead[hsize - 1 - zero] != 0)
break;
/* write the header */
if (putc(2 + hexcomp, ofp) == EOF)
return 0;
for (i = 0; i < 1024 && i < hsize; i++)
{
cpiohead[i] ^= oldcpio[i];
oldcpio[i] ^= cpiohead[i];
}
if (hexcomp)
magic_inode_increment(oldcpio);
run = 0;
hsize -= zero;
for (i = 0; i < hsize; i++)
{
if (cpiohead[i] == 0)
{
run++;
if (i + 1 < hsize)
continue;
}
while (run)
{
int z = run > 127 ? 127 : run;
if (putc(z, ofp) == EOF)
return 0;
run -= z;
}
if (cpiohead[i] == 0)
break; /* ended in zero */
for (j = i; j < hsize - 1; j++)
if (cpiohead[j] == 0 && cpiohead[j + 1] == 0)
break;
if (j == hsize - 1)
j = hsize;
j -= i;
if (j > 123)
j = 123;
if (putc(j + 128, ofp) == EOF)
return 0;
while (j-- > 0)
{
int z = cpiohead[i++];
if (putc(z, ofp) == EOF)
return 0;
}
i--;
}
if (putc(zero ? zero + 251 : 0, ofp) == EOF)
return 0;
if (fsize)
{
if (!dodelta(store, fp, &out, fsize))
return 0;
if ((fsize & 3) != 0)
{
i = 4 - (fsize & 3);
if (putc(4 + i, ofp) == EOF)
return 0;
while (i--)
{
if (getc(fp) != 0)
{
fprintf(stderr, "non-zero padding\n");
return 0;
}
}
}
}
}
if (putc(1, ofp) == EOF)
return 0;
if (fflush(ofp) != 0)
return 0;
return 1;
}
static unsigned long long
expandobscpio_next(FILE *fp)
{
unsigned long long x = 0;
int i;
for (;;)
{
i = getc(fp);
if (i == EOF)
return (unsigned long long)(-1);
if ((i & 128) == 0)
return x << 7 | i;
x = x << 7 | (i ^ 128);
}
}
static unsigned long long
expandobscpio_next_mem(unsigned char **bp, unsigned int *lp)
{
unsigned long long x = 0;
unsigned char *b = *bp;
unsigned int l = *lp;
int i;
for (;;)
{
if (l == 0)
return (unsigned long long)(-1);
i = *b++;
l--;
if ((i & 128) == 0)
{
*bp = b;
*lp = l;
return x << 7 | i;
}
x = x << 7 | (i ^ 128);
}
}
static int
expandobscpio_direct_mem(unsigned char **bp, unsigned int *lp, void *out, unsigned int outlen)
{
if (*lp < outlen)
return 0;
if (outlen)
memcpy(out, *bp, outlen);
*bp += outlen;
*lp -= outlen;
return 1;
}
static int
expandcpiohead(FILE *fp, FILE *ofp, unsigned char *cpio, int hexcomp)
{
int l = 0;
int zero;
for (;;)
{
int c = getc(fp);
if (c == EOF)
return 0;
if (c == 0)
break;
if (c < 128)
zero = 1;
else if (c >= 252)
{
zero = -1;
c -= 251;
}
else
{
zero = 0;
c -= 128;
}
while (c-- > 0)
{
int x = zero ? 0 : getc(fp);
if (x == EOF)
return 0;
if (l < 1024)
{
if (zero >= 0)
x ^= cpio[l];
cpio[l++] = x;
}
if (hexcomp && l <= 55)
{
int lettershift = (hexcomp == 1 ? 'A' : 'a') - ('0' + 10);
int x1 = (x >> 4) + '0';
int x2 = (x & 15) + '0';
if (x1 > '9')
x1 += lettershift;
if (x2 > '9')
x2 += lettershift;
if (putc(x1, ofp) == EOF || putc(x2, ofp) == EOF)
return 0;
}
else if (putc(x, ofp) == EOF)
return 0;
}
if (zero < 0)
break;
}
if (hexcomp)
magic_inode_increment(cpio);
return 1;
}
static int
expandobscpio(FILE *fp, int fdstore, FILE *ofp)
{
unsigned char magic[16];
unsigned char metabuf[16384];
unsigned char cpio[1024];
unsigned long long o, l;
struct stat st;
unsigned long long oldoffset = 0;
unsigned char *meta = 0;
unsigned int metal = 0;
unsigned long long oldoffset_meta = 0;
if (!fp || !ofp || fdstore == -1)
return 0;
if (fstat(fileno(fp), &st))
return 0;
if (fread(magic, 16, 1, fp) != 1 || memcmp(magic, "OBScpio", 7) != 0)
return 0;
memset(cpio, 0, sizeof(cpio));
for (;;)
{
if (meta && !metal)
{
meta = 0;
oldoffset = oldoffset_meta;
}
if (meta)
l = expandobscpio_next_mem(&meta, &metal);
else
l = expandobscpio_next(fp);
if (l == (unsigned long long)(-1))
return 0;
#if 0
printf("NEXT %d\n", l);
#endif
if (l < 16)
{
/* first 16 reserved as instructions */
if (meta)
return 0;
if (l == 1) /* EOF */
break;
if (l == 2 || l == 3 || l == 4) /* CPIO */
{
if (!expandcpiohead(fp, ofp, cpio, l - 2))
return 0;
}
else if (l == 5 || l == 6 || l == 7) /* ZERO */
{
l -= 4;
while (l--)
if (putc(0, ofp) == EOF)
return 0;
}
else if (l == 15) /* META */
{
l = expandobscpio_next(fp);
if (l == (unsigned long long)(-1))
return 0;
if (l < 16 || l > sizeof(metabuf))
return 0;
o = expandobscpio_next(fp);
if (o == (unsigned long long)(-1))
return 0;
o = decodeoffset(oldoffset, o);
oldoffset_meta = o + l;
oldoffset = 0;
if (pread(fdstore, metabuf, (size_t)l, (off_t)o) != (size_t)l)
return 0;
metal = (unsigned int)l;
meta = metabuf;
}
else
return 0;
}
else if (l < 256)
{
/* direct bytes */
l -= 16;
if (l)
{
char buf[256];
if (meta)
{
if (expandobscpio_direct_mem(&meta, &metal, buf, l) != 1)
return 0;
}
else if (fread(buf, (int)l, 1, fp) != 1)
return 0;
if (fwrite(buf, (int)l, 1, ofp) != 1)
return 0;
}
}
else
{
/* bytes from the store */
l -= 256;
if (meta)
o = expandobscpio_next_mem(&meta, &metal);
else
o = expandobscpio_next(fp);
if (o == (unsigned long long)(-1))
return 0;
o = decodeoffset(oldoffset, o);
oldoffset = o + l;
while (l)
{
char buf[8192];
size_t count = l > 8192 ? 8192 : l;
if (pread(fdstore, buf, count, (off_t)o) != count)
return 0;
if (fwrite(buf, count, 1, ofp) != 1)
return 0;
o += count;
l -= count;
}
}
}
if (fflush(ofp) != 0)
return 0;
settimes(fileno(ofp), &st);
return 1;
}
static void
printobscpioinstr(FILE *fp, int fdstore, int withmeta)
{
unsigned char magic[16];
unsigned long long oldoffset = 0, o, l;
unsigned char metabuf[16384];
unsigned char *meta = 0;
unsigned int metal = 0;
unsigned long long oldoffset_meta = 0;
unsigned int stats_cpio = 0;
unsigned long long stats_cpio_len = 0;
unsigned int stats_direct = 0;
unsigned long long stats_direct_len = 0;
unsigned int stats_store = 0;
unsigned long long stats_store_len = 0;
unsigned int stats_zero = 0;
unsigned long long stats_zero_len = 0;
unsigned int stats_meta = 0;
unsigned long long stats_meta_len = 0;
unsigned int stats_meta_store = 0;
unsigned long long stats_meta_store_len = 0;
unsigned int stats_meta_direct = 0;
unsigned long long stats_meta_direct_len = 0;
if (fread(magic, 16, 1, fp) != 1 || memcmp(magic, "OBScpio", 7) != 0)
return;
for (;;)
{
if (meta && !metal)
{
meta = 0;
oldoffset = oldoffset_meta;
}
if (meta)
l = expandobscpio_next_mem(&meta, &metal);
else
l = expandobscpio_next(fp);
if (l == (unsigned long long)(-1))
return;
if (l < 16)
{
if (meta)
return;
if (l == 1)
{
printf("end\n");
break;
}
if (l == 2 || l == 3 || l == 4) /* CPIO HEADER */
{
printf("cpio%d", (int)l);
stats_cpio++;
for (;;)
{
int c = getc(fp);
if (c == EOF)
return;
stats_cpio_len++;
if (c == 0)
{
printf(" (0)");
break;
}
if (c < 128)
printf(" [%d]", c);
else if (c >= 252)
{
printf(" (%d)", c - 251);
break;
}
else
{
c -= 128;
printf(" %d", c);
stats_cpio_len += c;
while (c--)
if (getc(fp) == EOF)
return;
}
}
printf("\n");
}
else if (l == 5 || l == 6 || l == 7) /* ZERO */
{
printf("zero %d\n", (int)l - 4);
stats_zero++;
stats_zero_len += (int)l - 4;
}
else if (l == 15) /* META */
{
l = expandobscpio_next(fp);
if (l == (unsigned long long)(-1))
return;
if (l < 16 || l > sizeof(metabuf))
return;
o = expandobscpio_next(fp);
if (o == (unsigned long long)(-1))
return;
o = decodeoffset(oldoffset, o);
oldoffset = o + l;
printf("meta %#llx %llu\n", o, l);
stats_meta++;
stats_meta_len += l;
if (withmeta)
{
oldoffset_meta = o + l;
oldoffset = 0;
if (pread(fdstore, metabuf, (size_t)l, (off_t)o) != (size_t)l)
return;
metal = (unsigned int)l;
meta = metabuf;
}
}
else
return;
continue;
}
if (meta)
printf(" ");
if (l < 256)
{
l -= 16;
printf("direct %d\n", (int)l);
if (meta)
{
stats_meta_direct++;
stats_meta_direct_len += l;
}
else
{
stats_direct++;
stats_direct_len += l;
}
if (meta)
{
if (l > metal)
return;
metal -= l;
meta += l;
}
else
{
while (l--)
if (getc(fp) == EOF)
return;
}
continue;
}
l -= 256;
if (meta)
o = expandobscpio_next_mem(&meta, &metal);
else
o = expandobscpio_next(fp);
if (o == (unsigned long long)(-1))
return;
o = decodeoffset(oldoffset, o);
oldoffset = o + l;
printf("store %#llx %llu\n", o, l);
if (meta)
{
stats_meta_store++;
stats_meta_store_len += l;
}
else
{
stats_store++;
stats_store_len += l;
}
}
printf("stats cpio %u len %llu\n", stats_cpio, stats_cpio_len);
printf("stats direct %u len %llu\n", stats_direct, stats_direct_len);
if (withmeta)
printf("stats meta_direct %u len %llu\n", stats_meta_direct, stats_meta_direct_len);
printf("stats store %u len %llu\n", stats_store, stats_store_len);
if (withmeta)
printf("stats meta_store %u len %llu\n", stats_meta_store, stats_meta_store_len);
printf("stats zero %u len %llu\n", stats_zero, stats_zero_len);
printf("stats meta %u len %llu\n", stats_meta, stats_meta_len);
if (withmeta)
printf("stats instr %u\n", stats_cpio + stats_direct + stats_store + stats_zero + stats_meta + 1 + stats_meta_direct + stats_meta_store);
printf("stats file_instr %u\n", stats_cpio + stats_direct + stats_store + stats_zero + stats_meta + 1);
printf("stats file_data %lld\n", stats_cpio_len + stats_direct_len);
printf("stats file_size %lld\n", (unsigned long long)ftell(fp));
}
static int
unifymodules_cmp(const void *ap, const void *bp, void *dp)
{
return *(Id *)ap - *(Id *)bp;
}
static int
missingmodules_cmp(const void *ap, const void *bp, void *dp)
{
const Id *a = ap;
const Id *b = bp;
if (a[0] != b[0])
return a[0] - b[0];
if (!a[1] && b[1])
return -1;
if (!b[1] && a[1])
return 1;
return a[1] - b[1];
}
static int
is_dod_package(Solvable *s)
{
const char *str = solvable_lookup_str(s, buildservice_id);
return str && !strcmp(str, "dod") ? 1 : 0;
}
static Solvable *
find_corresponding_dod(Solvable *s)
{
Repo *repo = s->repo;
Id p2;
Solvable *s2;
if (!repo)
return 0;
FOR_REPO_SOLVABLES(repo, p2, s2)
{
if (s->name == s2->name && s->evr == s2->evr && s->arch == s2->arch && s != s2 && is_dod_package(s2))
return s2;
}
return 0;
}
struct scc_data {
Id *edata;
Id *vedge;
Queue *sccs;
int *stack;
int nstack;
int *low;
int idx;
};
static void
scc_collect(struct scc_data *scc, int node)
{
int *low = scc->low;
Id *e;
queue_push(scc->sccs, node);
low[node] = -1;
for (e = scc->edata + scc->vedge[node]; *e; e++)
if (*e != -1 && low[*e] > 0)
scc_collect(scc, *e);
}
/* Tarjan's SCC algorithm */
static int
scc_visit(struct scc_data *scc, int node)
{
int l, myidx, *low = scc->low, nontrivial = 0;
Id *e;
low[node] = myidx = scc->idx++;
for (e = scc->edata + scc->vedge[node]; *e; e++)
{
if (*e == -1 || *e == node)
continue;
if (!(l = low[*e]))
l = scc_visit(scc, *e);
if (l > 0)
nontrivial = 1;
if (l > 0 && l < low[node])
low[node] = l;
}
if (low[node] != myidx)
return low[node];
low[node] = -1;
if (nontrivial)
{
scc_collect(scc, node);
queue_push(scc->sccs, 0);
}
return -1;
}
static void
find_sccs(Queue *edata, Queue *vedge, Queue *sccs)
{
struct scc_data scc;
int i;
scc.edata = edata->elements;
scc.vedge = vedge->elements;
scc.sccs = sccs;
scc.low = solv_calloc(vedge->count, sizeof(int));
scc.idx = 1;
for (i = 1; i < vedge->count; i++)
if (!scc.edata[vedge->elements[i]])
scc.low[i] = -1;
for (i = 1; i < vedge->count; i++)
if (!scc.low[i])
scc_visit(&scc, i);
solv_free(scc.low);
}
MODULE = BSSolv PACKAGE = BSSolv
void
depsort(HV *deps, SV *mapp, SV *cycp, ...)
ALIAS:
depsort2 = 1
PPCODE:
{
int i, j, k, cy, cycstart, nv;
int pkgstart = 3;
SV *sv, **svp;
SV *pkg2srcp = 0;
Id id, *e;
Id *mark;
char **names;
char **depnames;
Hashtable ht;
Hashval h, hh, hm;
HV *mhv = 0;
HV *pkg2srchv = 0;
Queue edata;
Queue vedge;
Queue todo;
Queue cycles;
Map edgeunifymap;
int didsccs = 0;
if (ix)
{
/* called as depsort2 */
if (items < 4)
XSRETURN_EMPTY; /* nothing to sort */
pkgstart = 4;
pkg2srcp = cycp;
cycp = ST(3);
}
if (items == pkgstart)
XSRETURN_EMPTY; /* nothing to sort */
if (items == pkgstart + 1)
{
/* only one item */
char *s = SvPV_nolen(ST(pkgstart));
EXTEND(SP, 1);
sv = newSVpv(s, 0);
PUSHs(sv_2mortal(sv));
XSRETURN(1); /* nothing to sort */
}
if (pkg2srcp && SvROK(pkg2srcp) && SvTYPE(SvRV(pkg2srcp)) == SVt_PVHV)
pkg2srchv = (HV *)SvRV(pkg2srcp);
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 = depnames = solv_calloc(items, sizeof(char *));
/* create pkgname -> edge hash, store edge -> pkgname data */
nv = 1;
for (i = pkgstart; 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;
}
if (pkg2srchv)
{
/* redo the hash with src names instead of pkg names */
depnames = solv_calloc(nv, sizeof(char *));
memset(ht, 0, (hm + 1) * sizeof(*ht));
for (i = 1; i < nv; i++)
{
char *s = names[i];
svp = hv_fetch(pkg2srchv, s, strlen(s), 0);
if (svp)
{
char *ns = SvPV_nolen(*svp);
if (ns)
s = ns;
}
depnames[i] = s;
h = strhash(s) & hm;
hh = HASHCHAIN_START;
while ((id = ht[h]) != 0)
h = HASHCHAIN_NEXT(h, hh, hm);
ht[h] = i;
}
}
/* we now know all vertices, create edges */
queue_push(&vedge, 0);
queue_push(&edata, 0);
map_init(&edgeunifymap, nv);
for (i = 1; i < nv; i++)
{
int edgestart = edata.count;
svp = hv_fetch(deps, names[i], strlen(names[i]), 0);
sv = svp ? *svp : 0;
queue_push(&vedge, edgestart);
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(depnames[id], s))
{
if (id != i && !MAPTST(&edgeunifymap, id))
{
MAPSET(&edgeunifymap, id);
queue_push(&edata, id);
}
if (names == depnames)
break; /* no other entry with same name */
}
h = HASHCHAIN_NEXT(h, hh, hm);
}
}
}
for (j = edgestart; j < edata.count; j++)
{
#ifdef MAPCLR_AT
MAPCLR_AT(&edgeunifymap, edata.elements[j]);
#else
MAPCLR(&edgeunifymap, edata.elements[j]);
#endif
}
queue_push(&edata, 0); /* terminate edge array */
}
/* free no longer needed stuff */
map_free(&edgeunifymap);
solv_free(ht);
if (depnames != names)
depnames = solv_free(depnames);
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 */
if (depsortsccs && !didsccs && cycp)
{
/* use Tarjan's SCC algorithm */
find_sccs(&edata, &vedge, &cycles);
queue_push(&cycles, 0);
didsccs = cycles.count;
}
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;
}
if (didsccs && depsortsccs != 2)
queue_truncate(&cycles, didsccs - 1);
/* record 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);
}
int
setdepsortsccs(int flag)
CODE:
depsortsccs = flag;
RETVAL = flag;
OUTPUT:
RETVAL
int
setgenmetaalgo(int algo)
CODE:
if (algo < 0)
algo = 1;
if (algo > 1)
croak("BSSolv::setgenmetaalgo: unsupported algo %d\n", algo);
genmetaalgo = algo;
RETVAL = algo;
OUTPUT:
RETVAL
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; /* killed because line includes a subpackage */
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;
/* create hash of cycle packages */
cycledata = solv_extend(cycledata, cycledatalen, 1, 1, 255);
cycledata[cycledatalen++] = 0;
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)
{
int l = strlen(s);
cycledata = solv_extend(cycledata, cycledatalen, l + 1, 1, 255);
ht[h] = cycledatalen; /* point to name */
strcpy(cycledata + cycledatalen, s);
cycledatalen += l + 1;
}
if (se)
*se = '/';
}
for (i = 0, lp = lines; i < nlines; i++, lp++)
{
if (!lp->nslash)
continue;
if (lp->killed && genmetaalgo == 0)
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 = 2; /* killed because it containes a cycle package */
break;
}
lo = s2 + 1;
}
if (lp->killed == 2)
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 = 2; /* killed because it containes a cycle package */
}
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)
{
if (genmetaalgo == 0 || lp->killed != 2)
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 && genmetaalgo == 1 && lp->killed == 2)
{
/* also kill old line of same level */
struct metaline *lp2 = lines + (id - 1);
if (!lp2->killed && lp2->nslash == lp->nslash)
lp2->killed = 1;
}
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);
}
void
add_meta(AV *new_meta, SV *sv, const char *bin, const char *packid = 0)
PPCODE:
{
const char *p, *np;
char *buf;
size_t l, bufl, binl, packidl;
int first = 1;
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) {
AV *av = (AV *)SvRV(sv);
SV **svp = av_fetch(av, 0, 0);
sv = svp ? *svp : 0;
}
if (!sv)
XSRETURN_EMPTY;
p = SvPV_nolen(sv);
binl = strlen(bin);
bufl = binl + 256;
buf = malloc(bufl);
if (!buf) {
croak("out of mem\n");
XSRETURN_EMPTY;
}
packidl = packid ? strlen(packid) : 0;
for (;;) {
np = strchr(p, '\n');
l = np ? np - p : strlen(p);
if (l > 34) {
if (l + binl + 1 + 1 > bufl) {
bufl = l + binl + 256;
buf = realloc(buf, bufl);
if (!buf) {
croak("out of mem\n");
XSRETURN_EMPTY;
}
}
strncpy(buf, p, 34);
strcpy(buf + 34, bin);
buf[34 + binl] = '/';
strncpy(buf + 34 + binl + 1, p + 34, l - 34);
l += binl + 1;
buf[l] = 0;
if (first) {
if (packidl && l > packidl + 1 && buf[l - packidl - 1] == '/' && !strcmp(buf + l - packidl, packid)) {
free(buf);
XSRETURN_EMPTY;
}
l = 34 + binl;
buf[l] = 0;
first = 0;
}
av_push(new_meta, newSVpvn(buf, l));
}
if (!np)
break;
p = np + 1;
}
free(buf);
}
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);
buildservice_dodresources = pool_str2id(pool, "buildservice:dodresources", 1);
buildservice_annotation = pool_str2id(pool, "buildservice:annotation", 1);
buildservice_modules = pool_str2id(pool, "buildservice:modules", 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")
&& (sl < 10 || strcmp(s + sl - 10, ".obsbinlnk"))
#ifdef ARCH_ADD_WITH_PKGID
&& (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
&& (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
&& (sl < 12 || strcmp(s + sl - 12, ".pkg.tar.zst"))
#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, SV *rv)
CODE:
{
Repo *repo;
Repodata *data;
if (!SvROK(rv) || (SvTYPE(SvRV(rv)) != SVt_PVHV && SvTYPE(SvRV(rv)) != SVt_PVAV))
croak("BSSolv::pool::repofromdata: rv is not a HASH or ARRAY reference");
repo = repo_create(pool, name);
data = repo_add_repodata(repo, 0);
data2solvables(repo, data, SvRV(rv), 0);
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, int unorderedrepos = 0)
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, unorderedrepos);
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 = testcase_str2dep(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 = testcase_str2dep(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 *
pkg2evr(BSSolv::pool pool, int p)
CODE:
RETVAL = pool_id2str(pool, pool->solvables[p].evr);
OUTPUT:
RETVAL
const char *
pkg2arch(BSSolv::pool pool, int p)
CODE:
RETVAL = pool_id2str(pool, pool->solvables[p].arch);
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
pkg2inmodule(BSSolv::pool pool, int p)
CODE:
RETVAL = solvable_lookup_type(pool->solvables + p, buildservice_modules) != 0;
OUTPUT:
RETVAL
void
pkg2modules(BSSolv::pool pool, int p)
PPCODE:
{
Solvable *s = pool->solvables + p;
Queue modules;
int i;
queue_init(&modules);
solvable_lookup_idarray(s, buildservice_modules, &modules);
if (!modules.count && !is_dod_package(s))
{
Solvable *s2 = find_corresponding_dod(s);
if (s2)
solvable_lookup_idarray(s2, buildservice_modules, &modules);
}
for (i = 0; i < modules.count; i++)
XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, modules.elements[i]), 0)));
queue_free(&modules);
}
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);
ss = solvable_lookup_str(s, buildservice_annotation);
if (ss)
(void)hv_store(RETVAL, "annotation", 10, newSVpv(ss, 0), 0);
if (solvable_lookup_type(s, buildservice_modules))
{
Queue modules;
int i;
queue_init(&modules);
solvable_lookup_idarray(s, buildservice_modules, &modules);
if (modules.count)
{
AV *av = newAV();
for (i = 0; i < modules.count; i++)
av_push(av, newSVpv(pool_id2str(pool, modules.elements[i]), 0));
(void)hv_store(RETVAL, "modules", 7, newRV_noinc((SV*)av), 0);
}
}
}
OUTPUT:
RETVAL
const char *
pkg2annotation(BSSolv::pool pool, int p)
CODE:
RETVAL = solvable_lookup_str(pool->solvables + p, buildservice_annotation);
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
setmodules(BSSolv::pool pool, AV *modulesav)
CODE:
{
SSize_t i, n = av_len(modulesav);
pool->appdata = solv_free(pool->appdata);
if (n >= 0 && n < 1000000)
{
Id *modules = pool->appdata = solv_calloc(n + 2, sizeof(Id));
for (i = 0; i <= n; i++)
modules[i] = pool_str2id(pool, avlookupstr(modulesav, i), 1);
modules[i] = 0;
}
}
void
getmodules(BSSolv::pool pool)
PPCODE:
if (pool->appdata)
{
Id *modules = pool->appdata;
int i;
for (i = 0; modules[i]; i++)
XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, modules[i]), 0)));
}
void
DESTROY(BSSolv::pool pool)
CODE:
if (pool->considered)
{
map_free(pool->considered);
pool->considered = solv_free(pool->considered);
}
pool->appdata = solv_free(pool->appdata);
pool_free(pool);
MODULE = BSSolv PACKAGE = BSSolv::repo PREFIX = repo
void
freerepo(BSSolv::repo repo)
CODE:
{
repo_free(repo, 1);
}
void
allpackages(BSSolv::repo repo)
PPCODE:
{
Id p;
Solvable *s;
EXTEND(SP, repo->nsolvables);
FOR_REPO_SOLVABLES(repo, p, s)
PUSHs(sv_2mortal(newSViv(p)));
}
void
pkgnames(BSSolv::repo repo)
PPCODE:
{
Pool *pool = repo->pool;
Id p;
Solvable *s;
Map c;
create_considered(pool, repo, &c, 0);
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, 0);
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 (ht[h])
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")
&& (sl < 10 || strcmp(s + sl - 10, ".obsbinlnk"))
#ifdef ARCH_ADD_WITH_PKGID
&& (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
&& (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
&& (sl < 12 || strcmp(s + sl - 12, ".pkg.tar.zst"))
#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;
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 (unless it's a obsbinlnk where the location comes from the content) */
unsigned int medianr;
str = solvable_get_location(pool->solvables + id, &medianr);
if (str[0] == '.' && str[1] == '/')
str += 2;
if (!strcmp(str, s) || (sl >= 10 && !strcmp(s + sl - 10, ".obsbinlnk")))
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
modulesfrombins(BSSolv::repo repo, ...)
PPCODE:
{
Pool *pool = repo->pool;
Hashtable ht;
Hashval h, hh, hm;
Queue modules;
Queue collectedmodules;
Id p, lastid;
Solvable *s;
int i, j;
queue_init(&collectedmodules);
queue_init(&modules);
hm = mkmask(2 * repo->nsolvables + 1);
ht = solv_calloc(hm + 1, sizeof(*ht));
FOR_REPO_SOLVABLES(repo, p, s)
{
const char *bsid = solvable_lookup_str(s, buildservice_id);
if (!bsid)
continue;
if (!strcmp(bsid, "dod"))
h = s->name + s->evr * 37 + s->arch * 129;
else
h = strhash(bsid);
h &= hm;
hh = HASHCHAIN_START;
while (ht[h])
h = HASHCHAIN_NEXT(h, hh, hm);
ht[h] = p;
}
for (i = 1; i + 1 < items; i += 2)
{
const char *bsid = SvPV_nolen(ST(i + 1));
h = strhash(bsid) & hm;
hh = HASHCHAIN_START;
while ((p = ht[h]) != 0)
{
const char *bsid2 = solvable_lookup_str(pool->solvables + p, buildservice_id);
if (!strcmp(bsid, bsid2))
break;
h = HASHCHAIN_NEXT(h, hh, hm);
}
if (!p)
continue;
s = pool->solvables + p;
h = (s->name + s->evr * 37 + s->arch * 129) & hm;
hh = HASHCHAIN_START;
while ((p = ht[h]) != 0)
{
Solvable *s2 = pool->solvables + p;
if (s->name == s2->name && s->evr == s2->evr && s->arch == s2->arch)
{
lastid = collectedmodules.count ? collectedmodules.elements[collectedmodules.count - 1] : 0;
solvable_lookup_idarray(s2, buildservice_modules, &modules);
for (j = 0; j < modules.count; j++)
if (modules.elements[j] != lastid)
queue_push(&collectedmodules, modules.elements[j]);
}
h = HASHCHAIN_NEXT(h, hh, hm);
}
}
solv_free(ht);
queue_free(&modules);
/* sort and unify */
solv_sort(collectedmodules.elements, collectedmodules.count, sizeof(Id), unifymodules_cmp, 0);
lastid = -1;
for (i = 0; i < collectedmodules.count; i++)
{
if (collectedmodules.elements[i] == lastid)
continue;
lastid = collectedmodules.elements[i];
XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, lastid), 0)));
}
queue_free(&collectedmodules);
}
void
missingmodules(BSSolv::repo repo, ...)
PPCODE:
{
Pool *pool = repo->pool;
Id p, *pp, *modules, id, req, lastid1, lastid2;
Solvable *s;
Queue missingq;
int i, missing;
queue_init(&missingq);
if (pool->appdata && ((Id *)pool->appdata)[0] && has_keyname(repo, buildservice_modules))
{
FOR_REPO_SOLVABLES(repo, p, s)
{
if (s->name != buildservice_modules || s->arch != ARCH_SRC || !s->requires)
continue;
id = s->repo->idarraydata[s->provides];
for (modules = pool->appdata; *modules; modules++)
if (id == *modules)
break;
if (!*modules)
continue;
missing = 0;
for (pp = s->repo->idarraydata + s->requires; (req = *pp) != 0; pp++)
if (!match_modules_req(pool, req))
{
missing = 1;
queue_push2(&missingq, id, req);
}
if (!missing) /* we're good */
queue_push2(&missingq, id, 0);
}
/* sort and unify */
solv_sort(missingq.elements, missingq.count / 2, sizeof(Id) * 2, missingmodules_cmp, 0);
lastid1 = lastid2 = -1;
for (i = 0; i < missingq.count; i += 2)
{
if (missingq.elements[i] == lastid1 && missingq.elements[i + 1] == lastid2)
continue;
if (missingq.elements[i] != lastid1)
{
lastid1 = missingq.elements[i];
lastid2 = missingq.elements[i + 1];
}
if (!lastid2)
continue;
lastid2 = missingq.elements[i + 1];
XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, lastid1), 0)));
XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, lastid2), 0)));
}
queue_free(&missingq);
}
}
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);
/* We need to special case .obsbinlink here where the location
* points back into the package. We currently assume that
* the name in the full tree is always <name>.obsbinlnk */
if (!strncmp(str, "../", 3))
str = pool_tmpjoin(repo->pool, pool_id2str(repo->pool, s->name), ".obsbinlnk", 0);
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
dodresources(BSSolv::repo repo)
PPCODE:
{
Pool *pool = repo->pool;
Queue dodresources;
int i;
queue_init(&dodresources);
repo_lookup_idarray(repo, SOLVID_META, buildservice_dodresources, &dodresources);
for (i = 0; i < dodresources.count; i++)
XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, dodresources.elements[i]), 0)));
queue_free(&dodresources);
}
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);
repodata_unset(data, SOLVID_META, buildservice_dodresources);
/* add new data */
if (rhv)
data2solvables(repo, data, (SV *)rhv, 1);
repo_internalize(repo);
}
void
setpriority(BSSolv::repo repo, int priority)
PPCODE:
repo->priority = priority;
int
mayhavemodules(BSSolv::repo repo)
CODE:
RETVAL = has_keyname(repo, buildservice_modules);
OUTPUT:
RETVAL
void
getmodules(BSSolv::repo repo)
PPCODE:
if (has_keyname(repo, buildservice_modules))
{
Pool *pool = repo->pool;
Id p, lastid = -1;
Solvable *s;
Queue collectedmodules;
int i;
queue_init(&collectedmodules);
FOR_REPO_SOLVABLES(repo, p, s)
if (s->name == buildservice_modules && s->arch == ARCH_SRC && s->repo->idarraydata[s->provides])
queue_push(&collectedmodules, s->repo->idarraydata[s->provides]);
if (!collectedmodules.count)
{
Queue modules;
queue_init(&modules);
FOR_REPO_SOLVABLES(repo, p, s)
{
solvable_lookup_idarray(pool->solvables + p, buildservice_modules, &modules);
for (i = 0; i < modules.count; i++)
{
if (modules.elements[i] == lastid)
continue;
lastid = modules.elements[i];
queue_push(&collectedmodules, modules.elements[i]);
}
}
queue_free(&modules);
}
/* sort and unify */
solv_sort(collectedmodules.elements, collectedmodules.count, sizeof(Id), unifymodules_cmp, 0);
lastid = -1;
for (i = 0; i < collectedmodules.count; i++)
{
if (collectedmodules.elements[i] == lastid)
continue;
lastid = collectedmodules.elements[i];
XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, lastid), 0)));
}
queue_free(&collectedmodules);
}
void
getdodblobs(BSSolv::repo repo)
PPCODE:
{
Pool *pool = repo->pool;
int i;
Id p;
Solvable *s;
Stringpool ss;
stringpool_init_empty(&ss);
FOR_REPO_SOLVABLES(repo, p, s)
{
const char *str = solvable_lookup_str(s, buildservice_id);
unsigned int medianr;
const char *s, *se;
if (!str || strcmp(str, "dod") != 0)
continue;
s = solvable_get_location(pool->solvables + p, &medianr);
if ((s = strrchr(s, '?')) == 0)
continue;
for (++s; s; s = se ? se + 1 : 0)
{
se = strchr(s, ',');
if (se)
stringpool_strn2id(&ss, s, se - s, 1);
else
stringpool_str2id(&ss, s, 1);
}
}
for (i = 2; i < ss.nstrings; i++)
{
XPUSHs(sv_2mortal(newSVpv(stringpool_id2str(&ss, i), 0)));
}
stringpool_free(&ss);
}
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, *p;
int i;
Id id;
Expander *xp;
Queue preferpos;
Queue preferneg;
Queue ignore;
Queue conflict;
Queue fileprovides;
int debug = 0;
int options = 0;
queue_init(&preferpos);
queue_init(&preferneg);
queue_init(&ignore);
queue_init(&conflict);
queue_init(&fileprovides);
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;
if (*str == '-')
queue_push(&preferneg, pool_str2id(pool, str + 1, 1));
else
queue_push(&preferpos, pool_str2id(pool, str, 1));
}
}
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;
queue_push(&ignore, pool_str2id(pool, str, 1));
}
}
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++)
{
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)
{
queue_push2(&conflict, id, pool_strn2id(pool, str, p - str, 1));
str = p + 1;
}
queue_push2(&conflict, id, pool_str2id(pool, str, 1));
}
}
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;
hv_iterinit(hv);
while ((sv = hv_iternextsv(hv, &str, &strl)) != 0)
{
AV *av;
Id id2;
if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV)
continue;
id = pool_str2id(pool, str, 1);
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);
if (!id2)
continue;
if (id)
{
queue_push(&fileprovides, id); /* start name block */
id = 0;
}
queue_push(&fileprovides, id2);
}
if (id == 0)
queue_push(&fileprovides, 0); /* had at least one entry, finish name block */
}
}
options |= EXPANDER_OPTION_USERECOMMENDSFORCHOICES;
svp = hv_fetch(config, "expandflags:ignoreconflicts", 27, 0);
sv = svp ? *svp : 0;
if (sv && SvTRUE(sv))
options |= EXPANDER_OPTION_IGNORECONFLICTS;
svp = hv_fetch(config, "expandflags:dorecommends", 24, 0);
sv = svp ? *svp : 0;
if (sv && SvTRUE(sv))
options |= EXPANDER_OPTION_DORECOMMENDS;
svp = hv_fetch(config, "expandflags:dosupplements", 25, 0);
sv = svp ? *svp : 0;
if (sv && SvTRUE(sv))
options |= EXPANDER_OPTION_DOSUPPLEMENTS | EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES;
svp = hv_fetch(config, "expand_dbg", 10, 0);
sv = svp ? *svp : 0;
if (sv && SvOK(sv))
debug = SvIV(sv);
else
{
sv = get_sv("Build::expand_dbg", FALSE);
if (sv && SvOK(sv))
debug = SvIV(sv);
}
xp = expander_create(pool, &preferpos, &preferneg, &ignore, &conflict, &fileprovides, debug, options);
queue_free(&preferpos);
queue_free(&preferneg);
queue_free(&ignore);
queue_free(&conflict);
queue_free(&fileprovides);
RETVAL = xp;
}
OUTPUT:
RETVAL
void
expand(BSSolv::expander xp, ...)
PPCODE:
{
Pool *pool;
int i, nerrors;
Id id, who, indepbuf[64];
Queue ignoreq, in, out, indep;
int directdepsend = 0;
int options = 0;
queue_init(&ignoreq);
queue_init(&in);
queue_init(&out);
queue_init_buffer(&indep, indepbuf, sizeof(indepbuf)/sizeof(*indepbuf));
pool = xp->pool;
if (xp->debug)
expander_dbg(xp, "expand args:");
for (i = 1; i < items; i++)
{
char *s = SvPV_nolen(ST(i));
int deptype = DEPTYPE_REQUIRES;
if (xp->debug)
expander_dbg(xp, " %s", s);
if (*s == '-' && s[1] == '-')
{
/* expand option */
if (!strcmp(s, "--ignoreignore--"))
options |= EXPANDER_OPTION_IGNOREIGNORE;
else if (!strcmp(s, "--directdepsend--"))
directdepsend = 1;
else if (!strcmp(s, "--dorecommends--"))
options |= EXPANDER_OPTION_DORECOMMENDS;
else if (!strcmp(s, "--dosupplements--"))
options |= EXPANDER_OPTION_DOSUPPLEMENTS | EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES;
else if (!strcmp(s, "--ignoreconflicts--"))
options |= EXPANDER_OPTION_IGNORECONFLICTS;
continue;
}
if (*s == '-')
{
/* ignore dependency */
id = pool_str2id(pool, s + 1, 1);
queue_push(&ignoreq, id);
continue;
}
if (*s == '!')
{
deptype = DEPTYPE_CONFLICTS;
s++;
if (*s == '!')
{
deptype = DEPTYPE_OBSOLETES;
s++;
}
}
id = dep2id(pool, s);
if (deptype == DEPTYPE_REQUIRES && !directdepsend)
queue_push(&in, id);
else
queue_push2(&indep, deptype, id);
}
if (xp->debug)
expander_dbg(xp, "\n");
nerrors = expander_expand(xp, &in, &indep, &out, &ignoreq, options);
queue_free(&in);
queue_free(&indep);
queue_free(&ignoreq);
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), solvid2name(pool, who));
else
sv = newSVpvf("nothing provides %s", pool_dep2str(pool, id));
i += 3;
}
else if (type == ERROR_ALLCONFLICT)
{
id = out.elements[i + 1];
who = out.elements[i + 2];
if (who)
sv = newSVpvf("%s conflicts with always true %s", solvid2name(pool, who), pool_dep2str(pool, id));
else
sv = newSVpvf("conflict with always true %s", pool_dep2str(pool, id));
i += 3;
}
else if (type == ERROR_CONFLICT)
{
Id who2 = out.elements[i + 2];
who = out.elements[i + 1];
if (!who && who2 >= 0)
sv = newSVpvf("conflicts with %s", solvid2name(pool, who2));
else if (who2 < 0)
sv = newSVpvf("%s obsoletes %s", solvid2name(pool, who), solvid2name(pool, -who2));
else
sv = newSVpvf("%s conflicts with %s", solvid2name(pool, who), solvid2name(pool, who2));
i += 3;
}
else if (type == ERROR_CONFLICT2)
{
Id who2 = out.elements[i + 2];
who = out.elements[i + 1];
if (who2 < 0)
sv = newSVpvf("%s is obsoleted by %s", solvid2name(pool, who), solvid2name(pool, -who2));
else if (who2 > 0)
sv = newSVpvf("%s is in conflict with %s", solvid2name(pool, who), solvid2name(pool, who2));
else
sv = newSVpvf("%s is in conflict", solvid2name(pool, who));
i += 3;
}
else if (type == ERROR_CONFLICTINGPROVIDERS)
{
id = out.elements[i + 1];
who = out.elements[i + 2];
if (who)
sv = newSVpvf("conflict for providers of %s needed by %s", pool_dep2str(pool, id), solvid2name(pool, who));
else
sv = newSVpvf("conflict for 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 %s)", solvid2name(pool, who), solvid2name(pool, -who2));
else
sv = newSVpvf("(provider %s conflicts with %s)", solvid2name(pool, who), solvid2name(pool, who2));
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 %s)", solvid2name(pool, who), solvid2name(pool, -who2));
else if (who2 > 0)
sv = newSVpvf("(provider %s is in conflict with %s)", solvid2name(pool, who), solvid2name(pool, who2));
else
sv = newSVpvf("(provider %s is in conflict)", solvid2name(pool, who));
i += 3;
}
else if (type == ERROR_CHOICE)
{
int j;
char *str = "";
for (j = i + 3; out.elements[j]; j++)
;
solv_sort(out.elements + i + 3, j - (i + 3), sizeof(Id), pkgname_sort_cmp, pool);
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), solvid2name(pool, who), str);
else
sv = newSVpvf("have choice for %s: %s", pool_dep2str(pool, id), str);
i = j + 1;
}
else if (type == ERROR_BADDEPENDENCY)
{
id = out.elements[i + 1];
who = out.elements[i + 2];
if (who)
sv = newSVpvf("cannot parse dependency %s from %s", pool_dep2str(pool, id), solvid2name(pool, who));
else
sv = newSVpvf("cannot parse dependency %s", pool_dep2str(pool, id));
i += 3;
}
else if (type == ERROR_NOPROVIDERINFO)
{
id = out.elements[i + 1];
who = out.elements[i + 2];
if (who)
sv = newSVpvf("(got version %s provided by %s)", pool_id2str(pool, id), solvid2name(pool, who));
else
sv = newSVpvf("(got version %s)", pool_id2str(pool, id));
i += 3;
}
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);
}
void
debug(BSSolv::expander xp, const char *str)
CODE:
expander_dbg(xp, "%s", str);
const char *
debugstr(BSSolv::expander xp)
CODE:
RETVAL = xp->debugstr ? xp->debugstr : "";
OUTPUT:
RETVAL
const char *
debugstrclr(BSSolv::expander xp)
CODE:
RETVAL = xp->debugstr ? xp->debugstr : "";
OUTPUT:
RETVAL
CLEANUP:
expander_clrdbg(xp);
void
DESTROY(BSSolv::expander xp)
CODE:
expander_free(xp);