X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fpool.c;h=e375b915c4989b541f8b57c8e2a37c8a5e2880e1;hb=42f00b0f81834d61893165bf2486ee2771fe2446;hp=6e05c643eb783b0c5ad92ca4c8b3fd766dae6c40;hpb=c9b149fcab3c154479acbcb3d356a3d976211650;p=platform%2Fupstream%2Flibsolv.git diff --git a/src/pool.c b/src/pool.c index 6e05c64..e375b91 100644 --- a/src/pool.c +++ b/src/pool.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Novell Inc. + * Copyright (c) 2007-2009, Novell Inc. * * This program is licensed under the BSD license, read LICENSE.BSD * for further information @@ -24,41 +24,14 @@ #include "poolid_private.h" #include "poolarch.h" #include "util.h" +#include "bitmap.h" #include "evr.h" #define SOLVABLE_BLOCK 255 - -// list of string constants, so we can do pointer/Id instead of string comparison -// index into array matches ID_xxx constants in pool.h - -static const char *initpool_data[] = { - "", // ID_NULL - "", // ID_EMPTY - "solvable:name", - "solvable:arch", - "solvable:evr", - "solvable:vendor", - "solvable:provides", - "solvable:obsoletes", - "solvable:conflicts", - "solvable:requires", - "solvable:recommends", - "solvable:suggests", - "solvable:supplements", - "solvable:enhances", - "solvable:freshens", - "rpm:dbid", /* direct key into rpmdb */ - "solvable:prereqmarker", - "solvable:filemarker", - "namespace:installed", - "namespace:modalias", - "system:system", - "src", - "nosrc", - "noarch", - 0 -}; +#define KNOWNID_INITIALIZE +#include "knownid.h" +#undef KNOWNID_INITIALIZE /* create pool */ Pool * @@ -67,23 +40,26 @@ pool_create(void) Pool *pool; Solvable *s; - pool = (Pool *)xcalloc(1, sizeof(*pool)); + pool = (Pool *)sat_calloc(1, sizeof(*pool)); stringpool_init (&pool->ss, initpool_data); - // pre-alloc space for a RelDep - pool->rels = (Reldep *)xcalloc(1 + REL_BLOCK, sizeof(Reldep)); + /* alloc space for RelDep 0 */ + pool->rels = sat_extend_resize(0, 1, sizeof(Reldep), REL_BLOCK); pool->nrels = 1; + memset(pool->rels, 0, sizeof(Reldep)); - // pre-alloc space for a Solvable - pool->solvables = (Solvable *)xcalloc(SOLVABLE_BLOCK + 1, sizeof(Solvable)); + /* alloc space for Solvable 0 and system solvable */ + pool->solvables = sat_extend_resize(0, 2, sizeof(Solvable), SOLVABLE_BLOCK); pool->nsolvables = 2; - queue_init(&pool->vendormap); + memset(pool->solvables, 0, 2 * sizeof(Solvable)); s = pool->solvables + SYSTEMSOLVABLE; s->name = SYSTEM_SYSTEM; s->arch = ARCH_NOARCH; s->evr = ID_EMPTY; + queue_init(&pool->vendormap); + pool->debugmask = SAT_DEBUG_RESULT; /* FIXME */ return pool; } @@ -98,22 +74,24 @@ pool_free(Pool *pool) pool_freewhatprovides(pool); pool_freeidhashes(pool); repo_freeallrepos(pool, 1); - xfree(pool->id2arch); - xfree(pool->solvables); - xfree(pool->ss.stringspace); - xfree(pool->ss.strings); - xfree(pool->rels); + sat_free(pool->id2arch); + sat_free(pool->solvables); + stringpool_free(&pool->ss); + sat_free(pool->rels); queue_free(&pool->vendormap); - for (i = 0; i < DEP2STRBUF; i++) - xfree(pool->dep2strbuf[i]); - xfree(pool); + for (i = 0; i < POOL_TMPSPACEBUF; i++) + sat_free(pool->tmpspacebuf[i]); + for (i = 0; i < pool->nlanguages; i++) + free((char *)pool->languages[i]); + sat_free(pool->languages); + sat_free(pool->languagecache); + sat_free(pool); } Id pool_add_solvable(Pool *pool) { - if ((pool->nsolvables & SOLVABLE_BLOCK) == 0) - pool->solvables = xrealloc2(pool->solvables, pool->nsolvables + (SOLVABLE_BLOCK + 1), sizeof(Solvable)); + pool->solvables = sat_extend(pool->solvables, pool->nsolvables, 1, sizeof(Solvable), SOLVABLE_BLOCK); memset(pool->solvables + pool->nsolvables, 0, sizeof(Solvable)); return pool->nsolvables++; } @@ -124,8 +102,7 @@ pool_add_solvable_block(Pool *pool, int count) Id nsolvables = pool->nsolvables; if (!count) return nsolvables; - if (((nsolvables - 1) | SOLVABLE_BLOCK) != ((nsolvables + count - 1) | SOLVABLE_BLOCK)) - pool->solvables = xrealloc2(pool->solvables, (nsolvables + count + SOLVABLE_BLOCK) & ~SOLVABLE_BLOCK, sizeof(Solvable)); + pool->solvables = sat_extend(pool->solvables, pool->nsolvables, count, sizeof(Solvable), SOLVABLE_BLOCK); memset(pool->solvables + nsolvables, 0, sizeof(Solvable) * count); pool->nsolvables += count; return nsolvables; @@ -146,32 +123,20 @@ pool_free_solvable_block(Pool *pool, Id start, int count, int reuseids) } -const char * -solvable2str(Pool *pool, Solvable *s) +void +pool_set_installed(Pool *pool, Repo *installed) { - int l, nn = pool->dep2strn; - const char *n, *e, *a; - n = id2str(pool, s->name); - e = id2str(pool, s->evr); - a = id2str(pool, s->arch); - l = strlen(n) + strlen(e) + strlen(a) + 3; - if (l > pool->dep2strlen[nn]) - { - pool->dep2strbuf[nn] = xrealloc(pool->dep2strbuf[nn], l + 32); - pool->dep2strlen[nn] = l + 32; - } - sprintf(pool->dep2strbuf[nn], "%s-%s.%s", n, e, a); - pool->dep2strn = (nn + 1) % DEP2STRBUF; - return pool->dep2strbuf[nn]; + if (pool->installed == installed) + return; + pool->installed = installed; + pool_freewhatprovides(pool); } -static Pool *pool_shrink_whatprovides_sortcmp_data; - static int -pool_shrink_whatprovides_sortcmp(const void *ap, const void *bp) +pool_shrink_whatprovides_sortcmp(const void *ap, const void *bp, void *dp) { int r; - Pool *pool = pool_shrink_whatprovides_sortcmp_data; + Pool *pool = dp; Id oa, ob, *da, *db; oa = pool->whatprovides[*(Id *)ap]; ob = pool->whatprovides[*(Id *)bp]; @@ -208,11 +173,10 @@ pool_shrink_whatprovides(Pool *pool) if (pool->ss.nstrings < 3) return; - sorted = xmalloc2(pool->ss.nstrings, sizeof(Id)); + sorted = sat_malloc2(pool->ss.nstrings, sizeof(Id)); for (id = 0; id < pool->ss.nstrings; id++) sorted[id] = id; - pool_shrink_whatprovides_sortcmp_data = pool; - qsort(sorted + 1, pool->ss.nstrings - 1, sizeof(Id), pool_shrink_whatprovides_sortcmp); + sat_sort(sorted + 1, pool->ss.nstrings - 1, sizeof(Id), pool_shrink_whatprovides_sortcmp, pool); last = 0; lastid = 0; for (i = 1; i < pool->ss.nstrings; i++) @@ -242,7 +206,7 @@ pool_shrink_whatprovides(Pool *pool) last = pool->whatprovidesdata + o; lastid = id; } - xfree(sorted); + sat_free(sorted); dp = pool->whatprovidesdata + 2; for (id = 1; id < pool->ss.nstrings; id++) { @@ -270,7 +234,7 @@ pool_shrink_whatprovides(Pool *pool) return; r = pool->whatprovidesdataoff - o; pool->whatprovidesdataoff = o; - pool->whatprovidesdata = xrealloc(pool->whatprovidesdata, (o + pool->whatprovidesdataleft) * sizeof(Id)); + pool->whatprovidesdata = sat_realloc(pool->whatprovidesdata, (o + pool->whatprovidesdataleft) * sizeof(Id)); if (r > pool->whatprovidesdataleft) r = pool->whatprovidesdataleft; memset(pool->whatprovidesdata + o, 0, r * sizeof(Id)); @@ -293,15 +257,18 @@ pool_createwhatprovides(Pool *pool) Offset *idp, n; Offset *whatprovides; Id *whatprovidesdata, *d; + Repo *installed = pool->installed; + unsigned int now; + now = sat_timems(0); POOL_DEBUG(SAT_DEBUG_STATS, "number of solvables: %d\n", pool->nsolvables); POOL_DEBUG(SAT_DEBUG_STATS, "number of ids: %d + %d\n", pool->ss.nstrings, pool->nrels); pool_freeidhashes(pool); /* XXX: should not be here! */ pool_freewhatprovides(pool); num = pool->ss.nstrings; - pool->whatprovides = whatprovides = (Offset *)xcalloc((num + WHATPROVIDES_BLOCK) & ~WHATPROVIDES_BLOCK, sizeof(Offset)); - pool->whatprovides_rel = (Offset *)xcalloc((pool->nrels + WHATPROVIDES_BLOCK) & ~WHATPROVIDES_BLOCK, sizeof(Offset)); + pool->whatprovides = whatprovides = sat_calloc_block(num, sizeof(Offset), WHATPROVIDES_BLOCK); + pool->whatprovides_rel = sat_calloc_block(pool->nrels, sizeof(Offset), WHATPROVIDES_BLOCK); /* count providers for each name */ for (i = 1; i < pool->nsolvables; i++) @@ -310,7 +277,9 @@ pool_createwhatprovides(Pool *pool) s = pool->solvables + i; if (!s->provides) continue; - if (!pool_installable(pool, s)) + /* we always need the installed solvable in the whatprovides data, + otherwise obsoletes/conflicts on them won't work */ + if (s->repo != installed && !pool_installable(pool, s)) continue; pp = s->repo->idarraydata + s->provides; while ((id = *pp++) != ID_NULL) @@ -347,7 +316,7 @@ pool_createwhatprovides(Pool *pool) POOL_DEBUG(SAT_DEBUG_STATS, "provide space needed: %d + %d\n", off, extra); /* alloc space for all providers + extra */ - whatprovidesdata = (Id *)xcalloc(off + extra, sizeof(Id)); + whatprovidesdata = sat_calloc(off + extra, sizeof(Id)); /* now fill data for all provides */ for (i = 1; i < pool->nsolvables; i++) @@ -356,7 +325,7 @@ pool_createwhatprovides(Pool *pool) s = pool->solvables + i; if (!s->provides) continue; - if (!pool_installable(pool, s)) + if (s->repo != installed && !pool_installable(pool, s)) continue; /* for all provides of this solvable */ @@ -374,8 +343,8 @@ pool_createwhatprovides(Pool *pool) d++; while (*d) /* find free slot */ d++; - if (d[-1] == i) - continue; + if (d[-1] == i) /* solvable already tacked at end ? */ + continue; /* Y: skip, on to next provides */ } *d = i; /* put solvable Id into data */ } @@ -384,18 +353,21 @@ pool_createwhatprovides(Pool *pool) pool->whatprovidesdataoff = off; pool->whatprovidesdataleft = extra; pool_shrink_whatprovides(pool); + POOL_DEBUG(SAT_DEBUG_STATS, "whatprovides memory used: %d K id array, %d K data\n", (pool->ss.nstrings + pool->nrels + WHATPROVIDES_BLOCK) / (int)(1024/sizeof(Id)), (pool->whatprovidesdataoff + pool->whatprovidesdataleft) / (int)(1024/sizeof(Id))); + POOL_DEBUG(SAT_DEBUG_STATS, "createwhatprovides took %d ms\n", sat_timems(now)); } /* * free all of our whatprovides data - * be careful, everything internalized with pool_queuetowhatprovides is gone, too + * be careful, everything internalized with pool_queuetowhatprovides is + * gone, too */ void pool_freewhatprovides(Pool *pool) { - pool->whatprovides = xfree(pool->whatprovides); - pool->whatprovides_rel = xfree(pool->whatprovides_rel); - pool->whatprovidesdata = xfree(pool->whatprovidesdata); + pool->whatprovides = sat_free(pool->whatprovides); + pool->whatprovides_rel = sat_free(pool->whatprovides_rel); + pool->whatprovidesdata = sat_free(pool->whatprovidesdata); pool->whatprovidesdataoff = 0; pool->whatprovidesdataleft = 0; } @@ -418,14 +390,14 @@ pool_queuetowhatprovides(Pool *pool, Queue *q) Offset off; int count = q->count; - if (count == 0) /* queue empty -> ID_EMPTY */ - return ID_EMPTY; + if (count == 0) /* queue empty -> 1 */ + return 1; /* extend whatprovidesdata if needed, +1 for ID_NULL-termination */ if (pool->whatprovidesdataleft < count + 1) { POOL_DEBUG(SAT_DEBUG_STATS, "growing provides hash data...\n"); - pool->whatprovidesdata = (Id *)xrealloc(pool->whatprovidesdata, (pool->whatprovidesdataoff + count + 4096) * sizeof(Id)); + pool->whatprovidesdata = sat_realloc(pool->whatprovidesdata, (pool->whatprovidesdataoff + count + 4096) * sizeof(Id)); pool->whatprovidesdataleft = count + 4096; } @@ -444,6 +416,109 @@ pool_queuetowhatprovides(Pool *pool, Queue *q) /*************************************************************************/ +/* check if a package's nevr matches a dependency */ + +int +pool_match_nevr_rel(Pool *pool, Solvable *s, Id d) +{ + Reldep *rd = GETRELDEP(pool, d); + Id name = rd->name; + Id evr = rd->evr; + int flags = rd->flags; + + if (flags > 7) + { + switch (flags) + { + case REL_ARCH: + if (s->arch != evr) + return 0; + return pool_match_nevr(pool, s, name); + case REL_OR: + if (pool_match_nevr(pool, s, name)) + return 1; + return pool_match_nevr(pool, s, evr); + case REL_AND: + case REL_WITH: + if (!pool_match_nevr(pool, s, name)) + return 0; + return pool_match_nevr(pool, s, evr); + default: + return 0; + } + } + if (!pool_match_nevr(pool, s, name)) + return 0; + if (evr == s->evr) + return flags & 2 ? 1 : 0; + if (!flags) + return 0; + if (flags == 7) + return 1; + if (flags != 2 && flags != 5) + flags ^= 5; +#ifdef DEBIAN_SEMANTICS + if ((flags & (1 << (1 + evrcmp(pool, s->evr, evr, EVRCMP_COMPARE)))) != 0) + return 1; +#else + if ((flags & (1 << (1 + evrcmp(pool, s->evr, evr, EVRCMP_MATCH_RELEASE)))) != 0) + return 1; +#endif + return 0; +} + +/* match two dependencies */ + +int +pool_match_dep(Pool *pool, Id d1, Id d2) +{ + Reldep *rd1, *rd2; + int pflags, flags; + + if (d1 == d2) + return 1; + if (!ISRELDEP(d1)) + { + if (!ISRELDEP(d2)) + return 0; + rd2 = GETRELDEP(pool, d2); + return pool_match_dep(pool, d1, rd2->name); + } + rd1 = GETRELDEP(pool, d1); + if (!ISRELDEP(d2)) + { + return pool_match_dep(pool, rd1->name, d2); + } + rd2 = GETRELDEP(pool, d2); + if (!pool_match_dep(pool, rd1->name, rd2->name)) + return 0; + pflags = rd1->flags; + flags = rd2->flags; + if (!pflags || !flags || pflags >= 8 || flags >= 8) + return 0; + if (flags == 7 || pflags == 7) + return 1; + if ((pflags & flags & 5) != 0) + return 1; + if (rd1->evr == rd2->evr) + { + if ((pflags & flags & 2) != 0) + return 1; + } + else + { + int f = flags == 5 ? 5 : flags == 2 ? pflags : (flags ^ 5) & (pflags | 5); +#ifdef DEBIAN_SEMANTICS + if ((f & (1 << (1 + evrcmp(pool, rd1->evr, rd2->evr, EVRCMP_COMPARE)))) != 0) + return 1; +#else + if ((f & (1 << (1 + evrcmp(pool, rd1->evr, rd2->evr, EVRCMP_MATCH_RELEASE)))) != 0) + return 1; +#endif + } + return 0; +} + /* * addrelproviders * @@ -452,7 +527,7 @@ pool_queuetowhatprovides(Pool *pool, Queue *q) * */ -Id * +Id pool_addrelproviders(Pool *pool, Id d) { Reldep *rd = GETRELDEP(pool, d); @@ -463,7 +538,7 @@ pool_addrelproviders(Pool *pool, Id d) Id evr = rd->evr; int flags = rd->flags; Id pid, *pidp; - Id p, *pp, *pp2, *pp3; + Id p, wp, *pp, *pp2, *pp3; d = GETRELID(d); queue_init_buffer(&plist, buf, sizeof(buf)/sizeof(*buf)); @@ -471,8 +546,8 @@ pool_addrelproviders(Pool *pool, Id d) { case REL_AND: case REL_WITH: - pp = pool_whatprovides(pool, name); - pp2 = pool_whatprovides(pool, evr); + pp = pool_whatprovides_ptr(pool, name); + pp2 = pool_whatprovides_ptr(pool, evr); while ((p = *pp++) != 0) { for (pp3 = pp2; *pp3;) @@ -484,44 +559,102 @@ pool_addrelproviders(Pool *pool, Id d) } break; case REL_OR: - pp = pool_whatprovides(pool, name); + pp = pool_whatprovides_ptr(pool, name); while ((p = *pp++) != 0) queue_push(&plist, p); - pp = pool_whatprovides(pool, evr); + pp = pool_whatprovides_ptr(pool, evr); while ((p = *pp++) != 0) queue_pushunique(&plist, p); break; case REL_NAMESPACE: + if (name == NAMESPACE_OTHERPROVIDERS) + { + wp = pool_whatprovides(pool, evr); + pool->whatprovides_rel[d] = wp; + return wp; + } if (pool->nscallback) { + /* ask callback which packages provide the dependency + * 0: none + * 1: the system (aka SYSTEMSOLVABLE) + * >1: a set of packages, stored as offset on whatprovidesdata + */ p = pool->nscallback(pool, pool->nscallbackdata, name, evr); if (p > 1) { queue_free(&plist); pool->whatprovides_rel[d] = p; - return pool->whatprovidesdata + p; + return p; } if (p == 1) queue_push(&plist, SYSTEMSOLVABLE); } break; + case REL_ARCH: + /* small hack: make it possible to match .src + * we have to iterate over the solvables as src packages do not + * provide anything, thus they are not indexed in our + * whatprovides hash */ + if (evr == ARCH_SRC) + { + Solvable *s; + for (p = 1, s = pool->solvables + p; p < pool->nsolvables; p++, s++) + { + if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC) + continue; + if (pool_match_nevr(pool, s, name)) + queue_push(&plist, p); + } + break; + } + wp = pool_whatprovides(pool, name); + pp = pool->whatprovidesdata + wp; + while ((p = *pp++) != 0) + { + Solvable *s = pool->solvables + p; + if (s->arch == evr) + queue_push(&plist, p); + else + wp = 0; + } + if (wp) + { + pool->whatprovides_rel[d] = wp; + return wp; + } + break; default: break; } /* convert to whatprovides id */ #if 0 - POOL_DEBUG(DEBUG_1, "addrelproviders: what provides %s?\n", id2str(pool, name)); + POOL_DEBUG(SAT_DEBUG_STATS, "addrelproviders: what provides %s?\n", dep2str(pool, name)); #endif if (flags && flags < 8) { - FOR_PROVIDES(p, pp, name) + pp = pool_whatprovides_ptr(pool, name); + while (ISRELDEP(name)) { + rd = GETRELDEP(pool, name); + name = rd->name; + } + while ((p = *pp++) != 0) + { + Solvable *s = pool->solvables + p; #if 0 - POOL_DEBUG(DEBUG_1, "addrelproviders: checking package %s\n", id2str(pool, pool->p[p].name)); + POOL_DEBUG(DEBUG_1, "addrelproviders: checking package %s\n", id2str(pool, s->name)); #endif + if (!s->provides) + { + /* no provides - check nevr */ + if (pool_match_nevr_rel(pool, s, MAKERELDEP(d))) + queue_push(&plist, p); + continue; + } /* solvable p provides name in some rels */ - pidp = pool->solvables[p].repo->idarraydata + pool->solvables[p].provides; + pidp = s->repo->idarraydata + s->provides; while ((pid = *pidp++) != 0) { int pflags; @@ -558,8 +691,13 @@ pool_addrelproviders(Pool *pool, Id d) else { int f = flags == 5 ? 5 : flags == 2 ? pflags : (flags ^ 5) & (pflags | 5); +#ifdef DEBIAN_SEMANTICS + if ((f & (1 << (1 + evrcmp(pool, pevr, evr, EVRCMP_COMPARE)))) != 0) + break; +#else if ((f & (1 << (1 + evrcmp(pool, pevr, evr, EVRCMP_MATCH_RELEASE)))) != 0) break; +#endif } } if (!pid) @@ -572,12 +710,12 @@ pool_addrelproviders(Pool *pool, Id d) } /* add providers to whatprovides */ #if 0 - POOL_DEBUG(DEBUG_1, "addrelproviders: adding %d packages to %d\n", plist.count, d); + POOL_DEBUG(SAT_DEBUG_STATS, "addrelproviders: adding %d packages to %d\n", plist.count, d); #endif pool->whatprovides_rel[d] = pool_queuetowhatprovides(pool, &plist); queue_free(&plist); - return pool->whatprovidesdata + pool->whatprovides_rel[d]; + return pool->whatprovides_rel[d]; } /*************************************************************************/ @@ -588,7 +726,7 @@ pool_debug(Pool *pool, int type, const char *format, ...) va_list args; char buf[1024]; - if ((type & SAT_FATAL) == 0) + if ((type & (SAT_FATAL|SAT_ERROR)) == 0) { if ((pool->debugmask & type) == 0) return; @@ -623,5 +761,884 @@ pool_setdebuglevel(Pool *pool, int level) pool->debugmask = mask; } +/*************************************************************************/ + +struct searchfiles { + Id *ids; + char **dirs; + char **names; + int nfiles; + Map seen; +}; + +#define SEARCHFILES_BLOCK 127 + +static void +pool_addfileprovides_dep(Pool *pool, Id *ida, struct searchfiles *sf, struct searchfiles *isf) +{ + Id dep, sid; + const char *s, *sr; + struct searchfiles *csf; + + while ((dep = *ida++) != 0) + { + csf = sf; + while (ISRELDEP(dep)) + { + Reldep *rd; + sid = pool->ss.nstrings + GETRELID(dep); + if (MAPTST(&csf->seen, sid)) + { + dep = 0; + break; + } + MAPSET(&csf->seen, sid); + rd = GETRELDEP(pool, dep); + if (rd->flags < 8) + dep = rd->name; + else if (rd->flags == REL_NAMESPACE) + { + if (rd->name == NAMESPACE_INSTALLED || rd->name == NAMESPACE_SPLITPROVIDES) + { + csf = isf; + if (!csf || MAPTST(&csf->seen, sid)) + { + dep = 0; + break; + } + MAPSET(&csf->seen, sid); + } + dep = rd->evr; + } + else + { + Id ids[2]; + ids[0] = rd->name; + ids[1] = 0; + pool_addfileprovides_dep(pool, ids, csf, isf); + dep = rd->evr; + } + } + if (!dep) + continue; + if (MAPTST(&csf->seen, dep)) + continue; + MAPSET(&csf->seen, dep); + s = id2str(pool, dep); + if (*s != '/') + continue; + csf->ids = sat_extend(csf->ids, csf->nfiles, 1, sizeof(Id), SEARCHFILES_BLOCK); + csf->dirs = sat_extend(csf->dirs, csf->nfiles, 1, sizeof(const char *), SEARCHFILES_BLOCK); + csf->names = sat_extend(csf->names, csf->nfiles, 1, sizeof(const char *), SEARCHFILES_BLOCK); + csf->ids[csf->nfiles] = dep; + sr = strrchr(s, '/'); + csf->names[csf->nfiles] = strdup(sr + 1); + csf->dirs[csf->nfiles] = sat_malloc(sr - s + 1); + if (sr != s) + strncpy(csf->dirs[csf->nfiles], s, sr - s); + csf->dirs[csf->nfiles][sr - s] = 0; + csf->nfiles++; + } +} + +struct addfileprovides_cbdata { + int nfiles; + Id *ids; + char **dirs; + char **names; + + Repodata *olddata; + Id *dids; + Map useddirs; +}; + +static int +addfileprovides_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value) +{ + struct addfileprovides_cbdata *cbd = cbdata; + int i; + + if (data != cbd->olddata) + { + map_free(&cbd->useddirs); + map_init(&cbd->useddirs, data->dirpool.ndirs); + for (i = 0; i < cbd->nfiles; i++) + { + Id did = repodata_str2dir(data, cbd->dirs[i], 0); + cbd->dids[i] = did; + if (did) + MAPSET(&cbd->useddirs, did); + } + cbd->olddata = data; + } + if (!MAPTST(&cbd->useddirs, value->id)) + return 0; + for (i = 0; i < cbd->nfiles; i++) + { + if (cbd->dids[i] != value->id) + continue; + if (!strcmp(cbd->names[i], value->str)) + break; + } + if (i == cbd->nfiles) + return 0; + s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER); + return 0; +} + +static int +addfileprovides_setid_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv) +{ + Map *provideids = cbdata; + if (key->type != REPOKEY_TYPE_IDARRAY) + return 0; + MAPSET(provideids, kv->id); + return kv->eof ? SEARCH_NEXT_SOLVABLE : 0; +} + + +static void +pool_addfileprovides_search(Pool *pool, struct addfileprovides_cbdata *cbd, struct searchfiles *sf, Repo *repoonly) +{ + Id p, start, end; + Solvable *s; + Repodata *data = 0, *nextdata; + Repo *oldrepo = 0; + int dataincludes = 0; + int i, j; + Map providedids; + + cbd->nfiles = sf->nfiles; + cbd->ids = sf->ids; + cbd->dirs = sf->dirs; + cbd->names = sf->names; + cbd->olddata = 0; + cbd->dids = sat_realloc2(cbd->dids, sf->nfiles, sizeof(Id)); + if (repoonly) + { + start = repoonly->start; + end = repoonly->end; + } + else + { + start = 2; /* skip system solvable */ + end = pool->nsolvables; + } + for (p = start, s = pool->solvables + p; p < end; p++, s++) + { + if (!s->repo || (repoonly && s->repo != repoonly)) + continue; + /* check if p is in (oldrepo,data) */ + if (s->repo != oldrepo || (data && p >= data->end)) + { + data = 0; + oldrepo = 0; + } + if (oldrepo == 0) + { + /* nope, find new repo/repodata */ + /* if we don't find a match, set data to the next repodata */ + nextdata = 0; + for (i = 0, data = s->repo->repodata; i < s->repo->nrepodata; i++, data++) + { + if (p >= data->end) + continue; + if (data->state != REPODATA_AVAILABLE) + continue; + for (j = 1; j < data->nkeys; j++) + if (data->keys[j].name == REPOSITORY_ADDEDFILEPROVIDES && data->keys[j].type == REPOKEY_TYPE_IDARRAY) + break; + if (j == data->nkeys) + continue; + /* great, this repodata contains addedfileprovides */ + if (!nextdata || nextdata->start > data->start) + nextdata = data; + if (p >= data->start) + break; + } + if (i == s->repo->nrepodata) + data = nextdata; /* no direct hit, use next repodata */ + if (data) + { + map_init(&providedids, pool->ss.nstrings); + repodata_search(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, 0, addfileprovides_setid_cb, &providedids); + for (i = 0; i < cbd->nfiles; i++) + if (!MAPTST(&providedids, cbd->ids[i])) + break; + map_free(&providedids); + dataincludes = i == cbd->nfiles; + } + oldrepo = s->repo; + } + if (data && p >= data->start && dataincludes) + continue; + repo_search(s->repo, p, SOLVABLE_FILELIST, 0, 0, addfileprovides_cb, cbd); + } +} + +void +pool_addfileprovides_ids(Pool *pool, Repo *installed, Id **idp) +{ + Solvable *s; + Repo *repo; + struct searchfiles sf, isf, *isfp; + struct addfileprovides_cbdata cbd; + int i; + + memset(&sf, 0, sizeof(sf)); + map_init(&sf.seen, pool->ss.nstrings + pool->nrels); + memset(&isf, 0, sizeof(isf)); + map_init(&isf.seen, pool->ss.nstrings + pool->nrels); + + isfp = installed ? &isf : 0; + for (i = 1, s = pool->solvables + i; i < pool->nsolvables; i++, s++) + { + repo = s->repo; + if (!repo) + continue; + if (s->obsoletes) + pool_addfileprovides_dep(pool, repo->idarraydata + s->obsoletes, &sf, isfp); + if (s->conflicts) + pool_addfileprovides_dep(pool, repo->idarraydata + s->conflicts, &sf, isfp); + if (s->requires) + pool_addfileprovides_dep(pool, repo->idarraydata + s->requires, &sf, isfp); + if (s->recommends) + pool_addfileprovides_dep(pool, repo->idarraydata + s->recommends, &sf, isfp); + if (s->suggests) + pool_addfileprovides_dep(pool, repo->idarraydata + s->suggests, &sf, isfp); + if (s->supplements) + pool_addfileprovides_dep(pool, repo->idarraydata + s->supplements, &sf, isfp); + if (s->enhances) + pool_addfileprovides_dep(pool, repo->idarraydata + s->enhances, &sf, isfp); + } + map_free(&sf.seen); + map_free(&isf.seen); + POOL_DEBUG(SAT_DEBUG_STATS, "found %d file dependencies\n", sf.nfiles); + POOL_DEBUG(SAT_DEBUG_STATS, "found %d installed file dependencies\n", isf.nfiles); + cbd.dids = 0; + map_init(&cbd.useddirs, 1); + if (idp) + *idp = 0; + if (sf.nfiles) + { +#if 0 + for (i = 0; i < sf.nfiles; i++) + POOL_DEBUG(SAT_DEBUG_STATS, "looking up %s in filelist\n", id2str(pool, sf.ids[i])); +#endif + pool_addfileprovides_search(pool, &cbd, &sf, 0); + if (idp) + { + sf.ids = sat_extend(sf.ids, sf.nfiles, 1, sizeof(Id), SEARCHFILES_BLOCK); + sf.ids[sf.nfiles] = 0; + *idp = sf.ids; + sf.ids = 0; + } + sat_free(sf.ids); + for (i = 0; i < sf.nfiles; i++) + { + sat_free(sf.dirs[i]); + sat_free(sf.names[i]); + } + sat_free(sf.dirs); + sat_free(sf.names); + } + if (isf.nfiles) + { +#if 0 + for (i = 0; i < isf.nfiles; i++) + POOL_DEBUG(SAT_DEBUG_STATS, "looking up %s in installed filelist\n", id2str(pool, isf.ids[i])); +#endif + if (installed) + pool_addfileprovides_search(pool, &cbd, &isf, installed); + sat_free(isf.ids); + for (i = 0; i < isf.nfiles; i++) + { + sat_free(isf.dirs[i]); + sat_free(isf.names[i]); + } + sat_free(isf.dirs); + sat_free(isf.names); + } + map_free(&cbd.useddirs); + sat_free(cbd.dids); + pool_freewhatprovides(pool); /* as we have added provides */ +} + +void +pool_addfileprovides(Pool *pool) +{ + pool_addfileprovides_ids(pool, pool->installed, 0); +} + +void +pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, struct _Repodata *data, struct _Repokey *key, struct _KeyValue *kv), void *cbdata) +{ + if (p) + { + if (pool->solvables[p].repo) + repo_search(pool->solvables[p].repo, p, key, match, flags, callback, cbdata); + return; + } + /* FIXME: obey callback return value! */ + for (p = 1; p < pool->nsolvables; p++) + if (pool->solvables[p].repo) + repo_search(pool->solvables[p].repo, p, key, match, flags, callback, cbdata); +} + +void +pool_clear_pos(Pool *pool) +{ + memset(&pool->pos, 0, sizeof(pool->pos)); +} + + +void +pool_set_languages(Pool *pool, const char **languages, int nlanguages) +{ + int i; + + pool->languagecache = sat_free(pool->languagecache); + pool->languagecacheother = 0; + if (pool->nlanguages) + { + for (i = 0; i < pool->nlanguages; i++) + free((char *)pool->languages[i]); + free(pool->languages); + } + pool->nlanguages = nlanguages; + if (!nlanguages) + return; + pool->languages = sat_calloc(nlanguages, sizeof(const char **)); + for (i = 0; i < pool->nlanguages; i++) + pool->languages[i] = strdup(languages[i]); +} + +Id +pool_id2langid(Pool *pool, Id id, const char *lang, int create) +{ + const char *n; + char buf[256], *p; + int l; + + if (!lang) + return id; + n = id2str(pool, id); + l = strlen(n) + strlen(lang) + 2; + if (l > sizeof(buf)) + p = sat_malloc(strlen(n) + strlen(lang) + 2); + else + p = buf; + sprintf(p, "%s:%s", n, lang); + id = str2id(pool, p, create); + if (p != buf) + free(p); + return id; +} + +char * +pool_alloctmpspace(Pool *pool, int len) +{ + int n = pool->tmpspacen; + if (!len) + return 0; + if (len > pool->tmpspacelen[n]) + { + pool->tmpspacebuf[n] = sat_realloc(pool->tmpspacebuf[n], len + 32); + pool->tmpspacelen[n] = len + 32; + } + pool->tmpspacen = (n + 1) % POOL_TMPSPACEBUF; + return pool->tmpspacebuf[n]; +} + +/*******************************************************************/ + +struct mptree { + Id sibling; + Id child; + const char *comp; + int compl; + Id mountpoint; +}; + +struct ducbdata { + DUChanges *mps; + struct mptree *mptree; + int addsub; + int hasdu; + + Id *dirmap; + int nmap; + Repodata *olddata; +}; + + +static int +solver_fill_DU_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value) +{ + struct ducbdata *cbd = cbdata; + Id mp; + + if (data != cbd->olddata) + { + Id dn, mp, comp, *dirmap, *dirs; + int i, compl; + const char *compstr; + struct mptree *mptree; + + /* create map from dir to mptree */ + cbd->dirmap = sat_free(cbd->dirmap); + cbd->nmap = 0; + dirmap = sat_calloc(data->dirpool.ndirs, sizeof(Id)); + mptree = cbd->mptree; + mp = 0; + for (dn = 2, dirs = data->dirpool.dirs + dn; dn < data->dirpool.ndirs; dn++) + { + comp = *dirs++; + if (comp <= 0) + { + mp = dirmap[-comp]; + continue; + } + if (mp < 0) + { + /* unconnected */ + dirmap[dn] = mp; + continue; + } + if (!mptree[mp].child) + { + dirmap[dn] = -mp; + continue; + } + if (data->localpool) + compstr = stringpool_id2str(&data->spool, comp); + else + compstr = id2str(data->repo->pool, comp); + compl = strlen(compstr); + for (i = mptree[mp].child; i; i = mptree[i].sibling) + if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl)) + break; + dirmap[dn] = i ? i : -mp; + } + /* change dirmap to point to mountpoint instead of mptree */ + for (dn = 0; dn < data->dirpool.ndirs; dn++) + { + mp = dirmap[dn]; + dirmap[dn] = mptree[mp > 0 ? mp : -mp].mountpoint; + } + cbd->dirmap = dirmap; + cbd->nmap = data->dirpool.ndirs; + cbd->olddata = data; + } + cbd->hasdu = 1; + if (value->id < 0 || value->id >= cbd->nmap) + return 0; + mp = cbd->dirmap[value->id]; + if (mp < 0) + return 0; + if (cbd->addsub > 0) + { + cbd->mps[mp].kbytes += value->num; + cbd->mps[mp].files += value->num2; + } + else + { + cbd->mps[mp].kbytes -= value->num; + cbd->mps[mp].files -= value->num2; + } + return 0; +} + +static void +propagate_mountpoints(struct mptree *mptree, int pos, Id mountpoint) +{ + int i; + if (mptree[pos].mountpoint == -1) + mptree[pos].mountpoint = mountpoint; + else + mountpoint = mptree[pos].mountpoint; + for (i = mptree[pos].child; i; i = mptree[i].sibling) + propagate_mountpoints(mptree, i, mountpoint); +} + +#define MPTREE_BLOCK 15 + +void +pool_calc_duchanges(Pool *pool, Map *installedmap, DUChanges *mps, int nmps) +{ + char *p; + const char *path, *compstr; + struct mptree *mptree; + int i, nmptree; + int pos, compl; + int mp; + struct ducbdata cbd; + Solvable *s; + Id sp; + Map ignoredu; + Repo *oldinstalled = pool->installed; + + memset(&ignoredu, 0, sizeof(ignoredu)); + cbd.mps = mps; + cbd.addsub = 0; + cbd.dirmap = 0; + cbd.nmap = 0; + cbd.olddata = 0; + + mptree = sat_extend_resize(0, 1, sizeof(struct mptree), MPTREE_BLOCK); + + /* our root node */ + mptree[0].sibling = 0; + mptree[0].child = 0; + mptree[0].comp = 0; + mptree[0].compl = 0; + mptree[0].mountpoint = -1; + nmptree = 1; + + /* create component tree */ + for (mp = 0; mp < nmps; mp++) + { + mps[mp].kbytes = 0; + mps[mp].files = 0; + pos = 0; + path = mps[mp].path; + while(*path == '/') + path++; + while (*path) + { + if ((p = strchr(path, '/')) == 0) + { + compstr = path; + compl = strlen(compstr); + path += compl; + } + else + { + compstr = path; + compl = p - path; + path = p + 1; + while(*path == '/') + path++; + } + for (i = mptree[pos].child; i; i = mptree[i].sibling) + if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl)) + break; + if (!i) + { + /* create new node */ + mptree = sat_extend(mptree, nmptree, 1, sizeof(struct mptree), MPTREE_BLOCK); + i = nmptree++; + mptree[i].sibling = mptree[pos].child; + mptree[i].child = 0; + mptree[i].comp = compstr; + mptree[i].compl = compl; + mptree[i].mountpoint = -1; + mptree[pos].child = i; + } + pos = i; + } + mptree[pos].mountpoint = mp; + } + + propagate_mountpoints(mptree, 0, mptree[0].mountpoint); + +#if 0 + for (i = 0; i < nmptree; i++) + { + printf("#%d sibling: %d\n", i, mptree[i].sibling); + printf("#%d child: %d\n", i, mptree[i].child); + printf("#%d comp: %s\n", i, mptree[i].comp); + printf("#%d compl: %d\n", i, mptree[i].compl); + printf("#%d mountpont: %d\n", i, mptree[i].mountpoint); + } +#endif + + cbd.mptree = mptree; + cbd.addsub = 1; + for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++) + { + if (!s->repo || (oldinstalled && s->repo == oldinstalled)) + continue; + if (!MAPTST(installedmap, sp)) + continue; + cbd.hasdu = 0; + repo_search(s->repo, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd); + if (!cbd.hasdu && oldinstalled) + { + Id op, opp; + /* no du data available, ignore data of all installed solvables we obsolete */ + if (!ignoredu.map) + map_init(&ignoredu, oldinstalled->end - oldinstalled->start); + if (s->obsoletes) + { + Id obs, *obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + FOR_PROVIDES(op, opp, obs) + if (op >= oldinstalled->start && op < oldinstalled->end) + MAPSET(&ignoredu, op - oldinstalled->start); + } + FOR_PROVIDES(op, opp, s->name) + if (pool->solvables[op].name == s->name) + if (op >= oldinstalled->start && op < oldinstalled->end) + MAPSET(&ignoredu, op - oldinstalled->start); + } + } + cbd.addsub = -1; + if (oldinstalled) + { + /* assumes we allways have du data for installed solvables */ + FOR_REPO_SOLVABLES(oldinstalled, sp, s) + { + if (MAPTST(installedmap, sp)) + continue; + if (ignoredu.map && MAPTST(&ignoredu, sp - oldinstalled->start)) + continue; + repo_search(oldinstalled, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd); + } + } + if (ignoredu.map) + map_free(&ignoredu); + sat_free(cbd.dirmap); + sat_free(mptree); +} + +int +pool_calc_installsizechange(Pool *pool, Map *installedmap) +{ + Id sp; + Solvable *s; + int change = 0; + Repo *oldinstalled = pool->installed; + + for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++) + { + if (!s->repo || (oldinstalled && s->repo == oldinstalled)) + continue; + if (!MAPTST(installedmap, sp)) + continue; + change += solvable_lookup_num(s, SOLVABLE_INSTALLSIZE, 0); + } + if (oldinstalled) + { + FOR_REPO_SOLVABLES(oldinstalled, sp, s) + { + if (MAPTST(installedmap, sp)) + continue; + change -= solvable_lookup_num(s, SOLVABLE_INSTALLSIZE, 0); + } + } + return change; +} + +/* map: + * 1: installed + * 2: conflicts with installed + * 8: interesting (only true if installed) + * 16: undecided + */ + +static inline Id dep2name(Pool *pool, Id dep) +{ + while (ISRELDEP(dep)) + { + Reldep *rd = rd = GETRELDEP(pool, dep); + dep = rd->name; + } + return dep; +} + +static int providedbyinstalled_multiversion(Pool *pool, unsigned char *map, Id n, Id dep) +{ + Id p, pp; + Solvable *sn = pool->solvables + n; + + FOR_PROVIDES(p, pp, sn->name) + { + Solvable *s = pool->solvables + p; + if (s->name != sn->name || s->arch != sn->arch) + continue; + if ((map[p] & 9) == 9) + return 1; + } + return 0; +} + +static inline int providedbyinstalled(Pool *pool, unsigned char *map, Id dep, int ispatch, Map *noobsoletesmap) +{ + Id p, pp; + int r = 0; + FOR_PROVIDES(p, pp, dep) + { + if (p == SYSTEMSOLVABLE) + return 1; /* always boring, as never constraining */ + if (ispatch && !pool_match_nevr(pool, pool->solvables + p, dep)) + continue; + if (ispatch && noobsoletesmap && noobsoletesmap->size && MAPTST(noobsoletesmap, p) && ISRELDEP(dep)) + if (providedbyinstalled_multiversion(pool, map, p, dep)) + continue; + if ((map[p] & 9) == 9) + return 9; + r |= map[p] & 17; + } + return r; +} + +/* + * pool_trivial_installable - calculate if a set of solvables is + * trivial installable without any other installs/deinstalls of + * packages not belonging to the set. + * + * the state is returned in the result queue: + * 1: solvable is installable without any other package changes + * 0: solvable is not installable + * -1: solvable is installable, but doesn't constrain any installed packages + */ + +void +pool_trivial_installable_noobsoletesmap(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res, Map *noobsoletesmap) +{ + int i, r, m, did; + Id p, *dp, con, *conp, req, *reqp; + unsigned char *map; + Solvable *s; + + map = sat_calloc(pool->nsolvables, 1); + for (p = 1; p < pool->nsolvables; p++) + { + if (!MAPTST(installedmap, p)) + continue; + map[p] |= 9; + s = pool->solvables + p; + if (!s->conflicts) + continue; + conp = s->repo->idarraydata + s->conflicts; + while ((con = *conp++) != 0) + { + dp = pool_whatprovides_ptr(pool, con); + for (; *dp; dp++) + map[p] |= 2; /* XXX: self conflict ? */ + } + } + for (i = 0; i < pkgs->count; i++) + map[pkgs->elements[i]] = 16; + + for (i = 0, did = 0; did < pkgs->count; i++, did++) + { + if (i == pkgs->count) + i = 0; + p = pkgs->elements[i]; + if ((map[p] & 16) == 0) + continue; + if ((map[p] & 2) != 0) + { + map[p] = 2; + continue; + } + s = pool->solvables + p; + m = 1; + if (s->requires) + { + reqp = s->repo->idarraydata + s->requires; + while ((req = *reqp++) != 0) + { + if (req == SOLVABLE_PREREQMARKER) + continue; + r = providedbyinstalled(pool, map, req, 0, 0); + if (!r) + { + /* decided and miss */ + map[p] = 2; + break; + } + m |= r; /* 1 | 9 | 16 | 17 */ + } + if (req) + continue; + if ((m & 9) == 9) + m = 9; + } + if (s->conflicts) + { + int ispatch = 0; /* see solver.c patch handling */ + + if (!strncmp("patch:", id2str(pool, s->name), 6)) + ispatch = 1; + conp = s->repo->idarraydata + s->conflicts; + while ((con = *conp++) != 0) + { + if ((providedbyinstalled(pool, map, con, ispatch, noobsoletesmap) & 1) != 0) + { + map[p] = 2; + break; + } + if ((m == 1 || m == 17) && ISRELDEP(con)) + { + con = dep2name(pool, con); + if ((providedbyinstalled(pool, map, con, ispatch, noobsoletesmap) & 1) != 0) + m = 9; + } + } + if (con) + continue; /* found a conflict */ + } +#if 0 + if (s->repo && s->repo != oldinstalled) + { + Id p2, obs, *obsp, *pp; + Solvable *s2; + if (s->obsoletes) + { + obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + { + if ((providedbyinstalled(pool, map, obs, 0, 0) & 1) != 0) + { + map[p] = 2; + break; + } + } + if (obs) + continue; + } + FOR_PROVIDES(p2, pp, s->name) + { + s2 = pool->solvables + p2; + if (s2->name == s->name && (map[p2] & 1) != 0) + { + map[p] = 2; + break; + } + } + if (p2) + continue; + } +#endif + if (m != map[p]) + { + map[p] = m; + did = 0; + } + } + queue_free(res); + queue_clone(res, pkgs); + for (i = 0; i < pkgs->count; i++) + { + m = map[pkgs->elements[i]]; + if ((m & 9) == 9) + r = 1; + else if (m & 1) + r = -1; + else + r = 0; + res->elements[i] = r; + } + free(map); +} + +void +pool_trivial_installable(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res) +{ + pool_trivial_installable_noobsoletesmap(pool, installedmap, pkgs, res, 0); +} // EOF