diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/bitmap.c | 50 | ||||
-rw-r--r-- | src/bitmap.h | 7 | ||||
-rw-r--r-- | src/libsolv.ver | 6 | ||||
-rw-r--r-- | src/pool.c | 60 | ||||
-rw-r--r-- | src/pool.h | 2 | ||||
-rw-r--r-- | src/problems.c | 235 | ||||
-rw-r--r-- | src/problems.h | 7 | ||||
-rw-r--r-- | src/repo.h | 9 | ||||
-rw-r--r-- | src/rules.c | 92 | ||||
-rw-r--r-- | src/selection.c | 1478 | ||||
-rw-r--r-- | src/selection.h | 38 | ||||
-rw-r--r-- | src/solver.c | 699 | ||||
-rw-r--r-- | src/solver.h | 1 | ||||
-rw-r--r-- | src/userinstalled.c | 367 |
15 files changed, 1947 insertions, 1107 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 35516ba..e8951f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,7 +19,8 @@ SET (libsolv_SRCS queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c chksum.c md5.c sha1.c sha2.c solvversion.c selection.c - fileprovides.c diskusage.c suse.c solver_util.c cleandeps.c) + fileprovides.c diskusage.c suse.c solver_util.c cleandeps.c + userinstalled.c) SET (libsolv_HEADERS bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h diff --git a/src/bitmap.c b/src/bitmap.c index 1bf1666..e004bf2 100644 --- a/src/bitmap.c +++ b/src/bitmap.c @@ -63,38 +63,48 @@ map_grow(Map *m, int n) void map_and(Map *t, Map *s) { - unsigned char *ti, *si, *end; - ti = t->map; - si = s->map; - end = ti + (t->size < s->size ? t->size : s->size); - while (ti < end) - *ti++ &= *si++; + unsigned char *ti, *si, *end; + ti = t->map; + si = s->map; + end = ti + (t->size < s->size ? t->size : s->size); + while (ti < end) + *ti++ &= *si++; } /* bitwise-ors maps t and s, stores the result in t. */ void map_or(Map *t, Map *s) { - unsigned char *ti, *si, *end; - if (t->size < s->size) - map_grow(t, s->size << 3); - ti = t->map; - si = s->map; - end = ti + (t->size < s->size ? t->size : s->size); - while (ti < end) - *ti++ |= *si++; + unsigned char *ti, *si, *end; + if (t->size < s->size) + map_grow(t, s->size << 3); + ti = t->map; + si = s->map; + end = ti + (t->size < s->size ? t->size : s->size); + while (ti < end) + *ti++ |= *si++; } /* remove all set bits in s from t. */ void map_subtract(Map *t, Map *s) { - unsigned char *ti, *si, *end; - ti = t->map; - si = s->map; - end = ti + (t->size < s->size ? t->size : s->size); - while (ti < end) - *ti++ &= ~*si++; + unsigned char *ti, *si, *end; + ti = t->map; + si = s->map; + end = ti + (t->size < s->size ? t->size : s->size); + while (ti < end) + *ti++ &= ~*si++; +} + +void +map_invertall(Map *m) +{ + unsigned char *ti, *end; + ti = m->map; + end = ti + m->size; + while (ti < end) + *ti++ ^= 0xff; } /* EOF */ diff --git a/src/bitmap.h b/src/bitmap.h index 5784e6c..0050a6a 100644 --- a/src/bitmap.h +++ b/src/bitmap.h @@ -33,6 +33,8 @@ typedef struct _Map { #define MAPCLR(m, n) ((m)->map[(n) >> 3] &= ~(1 << ((n) & 7))) /* test bit */ #define MAPTST(m, n) ((m)->map[(n) >> 3] & (1 << ((n) & 7))) +/* clear some bits at a position */ +#define MAPCLR_AT(m, n) ((m)->map[(n) >> 3] = 0) extern void map_init(Map *m, int n); extern void map_init_clone(Map *t, Map *s); @@ -41,6 +43,7 @@ extern void map_free(Map *m); extern void map_and(Map *t, Map *s); extern void map_or(Map *t, Map *s); extern void map_subtract(Map *t, Map *s); +extern void map_invertall(Map *m); static inline void map_empty(Map *m) { @@ -62,6 +65,10 @@ static inline int map_tst(Map *m, int n) { return MAPTST(m, n); } +static inline void map_clr_at(Map *m, int n) +{ + MAPCLR_AT(m, n); +} #ifdef __cplusplus } diff --git a/src/libsolv.ver b/src/libsolv.ver index 337cd19..64191d4 100644 --- a/src/libsolv.ver +++ b/src/libsolv.ver @@ -427,3 +427,9 @@ SOLV_1.1 { pool_best_solvables; solver_get_cleandeps; } SOLV_1.0; + +SOLV_1.2 { + map_invertall; + pool_set_whatprovides; + selection_subtract; +} SOLV_1.1; @@ -859,7 +859,7 @@ pool_match_dep(Pool *pool, Id d1, Id d2) if (!ISRELDEP(d1)) { if (!ISRELDEP(d2)) - return 0; + return 0; /* cannot match as d1 != d2 */ rd2 = GETRELDEP(pool, d2); return pool_match_dep(pool, d1, rd2->name); } @@ -1410,7 +1410,8 @@ pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr) continue; if (evr && rd->evr != evr) continue; - pool->whatprovides_rel[d] = 0; + if (pool->whatprovides_rel[d]) + pool_set_whatprovides(pool, MAKERELDEP(d), 0); } } @@ -1516,6 +1517,9 @@ pool_error(Pool *pool, int ret, const char *format, ...) { va_list args; int l; + + if (!pool) + return ret; va_start(args, format); if (!pool->errstr) { @@ -1862,24 +1866,66 @@ pool_lookup_deltalocation(Pool *pool, Id entry, unsigned int *medianrp) return loc; } +void +pool_set_whatprovides(Pool *pool, Id id, Id providers) +{ + int d, nrels = pool->nrels; + Reldep *rd; + Map m; + + /* set new entry */ + if (ISRELDEP(id)) + { + d = GETRELID(id); + pool->whatprovides_rel[d] = providers; + d++; + } + else + { + pool->whatprovides[id] = providers; + if (id < pool->whatprovidesauxoff) + pool->whatprovidesaux[id] = 0; /* sorry */ + d = 1; + } + if (!pool->whatprovides_rel) + return; + /* clear cache of all rels that use it */ + map_init(&m, 0); + for (rd = pool->rels + d; d < nrels; d++, rd++) + { + if (rd->name == id || rd->evr == id || + (m.size && ISRELDEP(rd->name) && MAPTST(&m, GETRELID(rd->name))) || + (m.size && ISRELDEP(rd->evr) && MAPTST(&m, GETRELID(rd->evr)))) + { + pool->whatprovides_rel[d] = 0; /* clear cache */ + if (!m.size) + map_init(&m, nrels); + MAPSET(&m, d); + } + } + map_free(&m); +} + static void add_new_provider(Pool *pool, Id id, Id p) { Queue q; Id *pp; + /* find whatprovides entry */ while (ISRELDEP(id)) { Reldep *rd = GETRELDEP(pool, id); id = rd->name; } + /* add new provider to existing list keeping it sorted */ queue_init(&q); for (pp = pool->whatprovidesdata + pool->whatprovides[id]; *pp; pp++) { if (*pp == p) { - queue_free(&q); + queue_free(&q); /* already have it */ return; } if (*pp > p) @@ -1891,9 +1937,7 @@ add_new_provider(Pool *pool, Id id, Id p) } if (p) queue_push(&q, p); - pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q); - if (id < pool->whatprovidesauxoff) - pool->whatprovidesaux[id] = 0; /* sorry */ + pool_set_whatprovides(pool, id, pool_queuetowhatprovides(pool, &q)); queue_free(&q); } @@ -1920,9 +1964,7 @@ pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts) continue; s->provides = repo_addid_dep(s->repo, s->provides, id, SOLVABLE_FILEMARKER); if (pool->whatprovides) - add_new_provider(pool, fn, p); - if (pool->whatprovides_rel) - pool->whatprovides_rel[GETRELID(id)] = 0; /* clear cache */ + add_new_provider(pool, id, p); s = pool->solvables + q; if (!s->repo) continue; @@ -344,6 +344,8 @@ static inline Id *pool_whatprovides_ptr(Pool *pool, Id d) void pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker); void pool_whatcontainsdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker); +void pool_set_whatprovides(Pool *pool, Id id, Id providers); + /* search the pool. the following filters are available: * p - search just this solvable diff --git a/src/problems.c b/src/problems.c index 2ffb6f9..7933f7c 100644 --- a/src/problems.c +++ b/src/problems.c @@ -24,7 +24,6 @@ #include "evr.h" #include "solverdebug.h" - /**********************************************************************************/ /* a problem is an item on the solver's problem list. It can either be >0, in that @@ -32,10 +31,9 @@ * consisting of multiple job rules. */ -void +static void solver_disableproblem(Solver *solv, Id v) { - Rule *r; int i; Id *jp; @@ -62,30 +60,30 @@ solver_disableproblem(Solver *solv, Id v) return; } solver_disablerule(solv, solv->rules + v); -#if 0 - /* XXX: doesn't work */ - if (v >= solv->updaterules && v < solv->updaterules_end) - { - /* enable feature rule if we disabled the update rule */ - r = solv->rules + (v - solv->updaterules + solv->featurerules); - if (r->p) - solver_enablerule(solv, r); - } -#endif return; } v = -(v + 1); jp = solv->ruletojob.elements; - for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++) + if (solv->bestrules_pkg) + { + int ni = solv->bestrules_up - solv->bestrules; + for (i = 0; i < ni; i++) + { + int j = solv->bestrules_pkg[i]; + if (j < 0 && jp[-j - solv->jobrules] == v) + solver_disablerule(solv, solv->rules + solv->bestrules + i); + } + } + for (i = solv->jobrules; i < solv->jobrules_end; i++, jp++) if (*jp == v) - solver_disablerule(solv, r); + solver_disablerule(solv, solv->rules + i); } /*------------------------------------------------------------------- * enableproblem */ -void +static void solver_enableproblem(Solver *solv, Id v) { Rule *r; @@ -133,9 +131,174 @@ solver_enableproblem(Solver *solv, Id v) } v = -(v + 1); jp = solv->ruletojob.elements; - for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++) + if (solv->bestrules_pkg) + { + int ni = solv->bestrules_up - solv->bestrules; + for (i = 0; i < ni; i++) + { + int j = solv->bestrules_pkg[i]; + if (j < 0 && jp[-j - solv->jobrules] == v) + solver_enablerule(solv, solv->rules + solv->bestrules + i); + } + } + for (i = solv->jobrules; i < solv->jobrules_end; i++, jp++) if (*jp == v) - solver_enablerule(solv, r); + solver_enablerule(solv, solv->rules + i); +} + + +/*------------------------------------------------------------------- + * turn a problem rule into a problem id by normalizing it + */ +static Id +solver_ruletoproblem(Solver *solv, Id rid) +{ + if (rid >= solv->jobrules && rid < solv->jobrules_end) + rid = -(solv->ruletojob.elements[rid - solv->jobrules] + 1); + else if (rid >= solv->bestrules && rid < solv->bestrules_up && solv->bestrules_pkg[rid - solv->bestrules] < 0) + rid = -(solv->ruletojob.elements[-solv->bestrules_pkg[rid - solv->bestrules] - solv->jobrules] + 1); + else if (rid > solv->infarchrules && rid < solv->infarchrules_end) + { + Pool *pool = solv->pool; + Id name = pool->solvables[-solv->rules[rid].p].name; + while (rid > solv->infarchrules && pool->solvables[-solv->rules[rid - 1].p].name == name) + rid--; + } + else if (rid > solv->duprules && rid < solv->duprules_end) + { + Pool *pool = solv->pool; + Id name = pool->solvables[-solv->rules[rid].p].name; + while (rid > solv->duprules && pool->solvables[-solv->rules[rid - 1].p].name == name) + rid--; + } + return rid; +} + +/*------------------------------------------------------------------- + * when the solver runs into a problem, it needs to disable all + * involved non-pkg rules and record the rules for solution + * generation. + */ +void +solver_recordproblem(Solver *solv, Id rid) +{ + Id v = solver_ruletoproblem(solv, rid); + /* return if problem already countains our rule */ + if (solv->problems.count) + { + int i; + for (i = solv->problems.count - 1; i >= 0; i--) + if (solv->problems.elements[i] == 0) /* end of last problem reached? */ + break; + else if (solv->problems.elements[i] == v) + return; + } + queue_push(&solv->problems, v); +} + +/*------------------------------------------------------------------- + * this is called when a problem is solved by disabling a rule. + * It calls disableproblem and then re-enables policy rules + */ +void +solver_fixproblem(Solver *solv, Id rid) +{ + Id v = solver_ruletoproblem(solv, rid); + solver_disableproblem(solv, v); + if (v < 0) + solver_reenablepolicyrules(solv, -v); +} + +/*------------------------------------------------------------------- + * disable a set of problems + */ +void +solver_disableproblemset(Solver *solv, int start) +{ + int i; + for (i = start + 1; i < solv->problems.count - 1; i++) + solver_disableproblem(solv, solv->problems.elements[i]); +} + +/*------------------------------------------------------------------- + * try to fix a problem by auto-uninstalling packages + */ +Id +solver_autouninstall(Solver *solv, int start) +{ + Pool *pool = solv->pool; + int i; + int lastfeature = 0, lastupdate = 0; + Id v; + Id extraflags = -1; + Map *m = 0; + + if (!solv->allowuninstall && !solv->allowuninstall_all) + { + if (!solv->allowuninstallmap.size) + return 0; /* why did we get called? */ + m = &solv->allowuninstallmap; + } + for (i = start + 1; i < solv->problems.count - 1; i++) + { + v = solv->problems.elements[i]; + if (v < 0) + extraflags &= solv->job.elements[-v - 1]; + if (v >= solv->updaterules && v < solv->updaterules_end) + { + Rule *r; + if (m && !MAPTST(m, v - solv->updaterules)) + continue; + /* check if identical to feature rule, we don't like that (except for orphans) */ + r = solv->rules + solv->featurerules + (v - solv->updaterules); + if (!r->p) + { + /* update rule == feature rule */ + if (v > lastfeature) + lastfeature = v; + /* prefer orphaned packages in dup mode */ + if (solv->keep_orphans) + { + r = solv->rules + v; + if (!r->d && !r->w2 && r->p == (solv->installed->start + (v - solv->updaterules))) + { + lastfeature = v; + lastupdate = 0; + break; + } + } + continue; + } + if (v > lastupdate) + lastupdate = v; + } + } + if (!lastupdate && !lastfeature) + return 0; + v = lastupdate ? lastupdate : lastfeature; + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "allowuninstall disabling "); + solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + v); + /* should really be solver_fixproblem, but we know v is an update/feature rule */ + solver_disableproblem(solv, v); + if (extraflags != -1 && (extraflags & SOLVER_CLEANDEPS) != 0 && solv->cleandepsmap.size) + { + /* add the package to the updatepkgs list, this will automatically turn + * on cleandeps mode */ + Id p = solv->rules[v].p; + if (!solv->cleandeps_updatepkgs) + { + solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue)); + queue_init(solv->cleandeps_updatepkgs); + } + if (p > 0) + { + int oldupdatepkgscnt = solv->cleandeps_updatepkgs->count; + queue_pushunique(solv->cleandeps_updatepkgs, p); + if (solv->cleandeps_updatepkgs->count != oldupdatepkgscnt) + solver_disablepolicyrules(solv); + } + } + return v; } @@ -202,7 +365,6 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti queue_empty(refined); if (!essentialok && sug < 0 && (solv->job.elements[-sug - 1] & SOLVER_ESSENTIAL) != 0) return; - queue_init(&disabled); queue_push(refined, sug); /* re-enable all problem rules with the exception of "sug"(gestion) */ @@ -211,10 +373,14 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti for (i = 0; problem[i]; i++) if (problem[i] != sug) solver_enableproblem(solv, problem[i]); - if (sug < 0) solver_reenablepolicyrules(solv, -sug); - else if (sug >= solv->updaterules && sug < solv->updaterules_end) + + /* here is where the feature rules come into play: if we disabled an + * update rule, we enable the corresponding feature rule if there is + * one. We do this to make the solver downgrade packages instead of + * deinstalling them */ + if (sug >= solv->updaterules && sug < solv->updaterules_end) { /* enable feature rule */ Rule *r = solv->rules + solv->featurerules + (sug - solv->updaterules); @@ -224,11 +390,15 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti enableweakrules(solv); + /* disabled contains all of the rules we disabled in the refinement process */ + queue_init(&disabled); for (;;) { - int njob, nfeature, nupdate, pass; + int nother, nfeature, nupdate, pass; queue_empty(&solv->problems); solver_reset(solv); + /* we set disablerules to zero because we are only interested in + * the first problem and we don't want the solver to disable the problems */ solver_run_sat(solv, 0, 0); if (!solv->problems.count) @@ -237,16 +407,18 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti break; /* great, no more problems */ } disabledcnt = disabled.count; - /* start with 1 to skip over proof index */ - njob = nfeature = nupdate = 0; + nother = nfeature = nupdate = 0; for (pass = 0; pass < 2; pass++) { + /* start with 1 to skip over proof index */ for (i = 1; i < solv->problems.count - 1; i++) { /* ignore solutions in refined */ v = solv->problems.elements[i]; if (v == 0) break; /* end of problem reached */ + if (!essentialok && v < 0 && (solv->job.elements[-v - 1] & SOLVER_ESSENTIAL) != 0) + continue; /* not that one! */ if (sug != v) { /* check if v is in the given problems list @@ -260,14 +432,10 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti } if (v >= solv->featurerules && v < solv->featurerules_end) nfeature++; - else if (v > 0) + else if (v > solv->updaterules && v < solv->updaterules_end) nupdate++; else - { - if (!essentialok && (solv->job.elements[-v - 1] & SOLVER_ESSENTIAL) != 0) - continue; /* not that one! */ - njob++; - } + nother++; queue_push(&disabled, v); } if (disabled.count != disabledcnt) @@ -280,7 +448,7 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti refined->count = 0; break; } - if (!njob && nupdate && nfeature) + if (!nother && nupdate && nfeature) { /* got only update rules, filter out feature rules */ POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "throwing away feature rules\n"); @@ -300,14 +468,14 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti if (!nfeature && v != sug) queue_push(refined, v); /* do not record feature rules */ solver_disableproblem(solv, v); + if (v < 0) + solver_reenablepolicyrules(solv, -v); if (v >= solv->updaterules && v < solv->updaterules_end) { Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules); if (r->p) solver_enablerule(solv, r); /* enable corresponding feature rule */ } - if (v < 0) - solver_reenablepolicyrules(solv, -v); } else { @@ -339,6 +507,7 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti for (i = 0; i < disabled.count; i++) solver_enableproblem(solv, disabled.elements[i]); queue_free(&disabled); + /* reset policy rules */ for (i = 0; problem[i]; i++) solver_enableproblem(solv, problem[i]); diff --git a/src/problems.h b/src/problems.h index e5b2279..63319d6 100644 --- a/src/problems.h +++ b/src/problems.h @@ -28,8 +28,11 @@ struct _Solver; #define SOLVER_SOLUTION_BEST (-3) #define SOLVER_SOLUTION_POOLJOB (-4) -void solver_disableproblem(struct _Solver *solv, Id v); -void solver_enableproblem(struct _Solver *solv, Id v); +void solver_recordproblem(struct _Solver *solv, Id rid); +void solver_fixproblem(struct _Solver *solv, Id rid); +Id solver_autouninstall(struct _Solver *solv, int start); +void solver_disableproblemset(struct _Solver *solv, int start); + int solver_prepare_solutions(struct _Solver *solv); unsigned int solver_problem_count(struct _Solver *solv); @@ -98,6 +98,15 @@ static inline int pool_disabled_solvable(const Pool *pool, Solvable *s) return 0; } +static inline int pool_badarch_solvable(const Pool *pool, Solvable *s) +{ + if (!s->arch) + return 1; + if (pool->id2arch && (s->arch > pool->lastarch || !pool->id2arch[s->arch])) + return 1; + return 0; +} + static inline int pool_installable(const Pool *pool, Solvable *s) { if (!s->arch || s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) diff --git a/src/rules.c b/src/rules.c index b5f3e3e..cf368e4 100644 --- a/src/rules.c +++ b/src/rules.c @@ -1466,7 +1466,7 @@ disableupdaterule(Solver *solv, Id p) { int i, ni; ni = solv->bestrules_end - solv->bestrules; - for (i = 0; i < ni; i++) + for (i = solv->bestrules_up - solv->bestrules; i < ni; i++) if (solv->bestrules_pkg[i] == p) solver_disablerule(solv, solv->rules + solv->bestrules + i); } @@ -1509,7 +1509,7 @@ reenableupdaterule(Solver *solv, Id p) { int i, ni; ni = solv->bestrules_end - solv->bestrules; - for (i = 0; i < ni; i++) + for (i = solv->bestrules_up - solv->bestrules; i < ni; i++) if (solv->bestrules_pkg[i] == p) solver_enablerule(solv, solv->rules + solv->bestrules + i); } @@ -1922,6 +1922,21 @@ solver_createdupmaps(Solver *solv) } if (solv->dupinvolvedmap.size) MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE); + /* set update for all involved installed packages. We need to do + * this before creating the update rules */ + if (solv->dupinvolvedmap_all) + solv->updatemap_all = 1; + else if (installed && !solv->updatemap_all && solv->dupinvolvedmap.size) + { + FOR_REPO_SOLVABLES(installed, p, s) + { + if (!MAPTST(&solv->dupinvolvedmap, p)) + continue; + if (!solv->updatemap.size) + map_grow(&solv->updatemap, installed->end - installed->start); + MAPSET(&solv->updatemap, p - installed->start); + } + } } void @@ -1943,8 +1958,6 @@ solver_addduprules(Solver *solv, Map *addedmap) Rule *r; solv->duprules = solv->nrules; - if (solv->dupinvolvedmap_all) - solv->updatemap_all = 1; for (i = 1; i < pool->nsolvables; i++) { if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i)) @@ -1964,12 +1977,6 @@ solver_addduprules(Solver *solv, Map *addedmap) continue; if (installed && ps->repo == installed) { - if (!solv->updatemap_all) - { - if (!solv->updatemap.size) - map_grow(&solv->updatemap, installed->end - installed->start); - MAPSET(&solv->updatemap, p - installed->start); - } if (!MAPTST(&solv->dupmap, p)) { Id ip, ipp; @@ -3257,36 +3264,47 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs) { for (i = 0; i < solv->job.count; i += 2) { - if ((solv->job.elements[i] & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST)) + Id how = solv->job.elements[i]; + if ((how & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST)) { int j; Id p2, pp2; for (j = 0; j < solv->ruletojob.count; j++) - if (solv->ruletojob.elements[j] == i) - break; - if (j == solv->ruletojob.count) - continue; - r = solv->rules + solv->jobrules + j; - queue_empty(&q); - FOR_RULELITERALS(p2, pp2, r) - if (p2 > 0) - queue_push(&q, p2); - if (!q.count) - continue; /* orphaned */ - /* select best packages, just look at prio and version */ - oldcnt = q.count; - policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND); - if (q.count == oldcnt) - continue; /* nothing filtered */ - p2 = queue_shift(&q); - if (q.count < 2) - solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0); - else - solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q)); - queue_push(&r2pkg, -(solv->jobrules + j)); + { + if (solv->ruletojob.elements[j] != i) + continue; + r = solv->rules + solv->jobrules + j; + queue_empty(&q); + queue_empty(&q2); + FOR_RULELITERALS(p2, pp2, r) + { + if (p2 > 0) + queue_push(&q, p2); + else if (p2 < 0) + queue_push(&q2, p2); + } + if (!q.count) + continue; /* orphaned */ + /* select best packages, just look at prio and version */ + oldcnt = q.count; + policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND); + if (q.count == oldcnt) + continue; /* nothing filtered */ + if (q2.count) + queue_insertn(&q, 0, q2.count, q2.elements); + p2 = queue_shift(&q); + if (q.count < 2) + solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0); + else + solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q)); + if ((how & SOLVER_WEAK) != 0) + queue_push(&solv->weakruleq, solv->nrules - 1); + queue_push(&r2pkg, -(solv->jobrules + j)); + } } } } + solv->bestrules_up = solv->nrules; if (installed && (solv->bestupdatemap_all || solv->bestupdatemap.size)) { @@ -3398,6 +3416,8 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs) /* yumobs rule handling */ +/* note that we use pool->obsoleteusescolors || pool->implicitobsoleteusescolors + * like in policy_findupdatepackages */ static void find_obsolete_group(Solver *solv, Id obs, Queue *q) @@ -3422,7 +3442,7 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q) Id obs2, *obsp2; if (!os->obsoletes) continue; - if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os)) + if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s2, os)) continue; obsp2 = os->repo->idarraydata + os->obsoletes; while ((obs2 = *obsp2++) != 0) @@ -3440,7 +3460,7 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q) continue; if (!os->obsoletes) continue; - if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os)) + if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s2, os)) continue; obsp2 = os->repo->idarraydata + os->obsoletes; while ((obs2 = *obsp2++) != 0) @@ -3539,7 +3559,7 @@ printf("checking yumobs for %s\n", pool_solvable2str(pool, s)); continue; if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs)) continue; - if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2)) + if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, s2)) continue; queue_pushunique(&qo, obs); break; diff --git a/src/selection.c b/src/selection.c index 16fe3a7..6ca72e5 100644 --- a/src/selection.c +++ b/src/selection.c @@ -37,6 +37,7 @@ str2archid(Pool *pool, const char *arch) return id; } +/* remove empty jobs from the selection */ static void selection_prune(Pool *pool, Queue *selection) { @@ -155,33 +156,49 @@ selection_flatten(Pool *pool, Queue *selection) } } +/* only supports simple rels plus REL_ARCH */ +static int +match_nevr_rel(Pool *pool, Solvable *s, Id rflags, Id revr) +{ + if (rflags == REL_ARCH) + { + if (s->arch != revr) + { + if (revr != ARCH_SRC || s->arch != ARCH_NOSRC) + return 0; + } + return 1; + } + if (rflags > 7) + return 0; + return pool_intersect_evrs(pool, REL_EQ, s->evr, rflags, revr); +} + +/* only supports simple rels plus REL_ARCH */ static void -selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr) +selection_filter_rel_noprune(Pool *pool, Queue *selection, Id relflags, Id relevr) { int i; + if (!selection->count) + return; + for (i = 0; i < selection->count; i += 2) { Id select = selection->elements[i] & SOLVER_SELECTMASK; Id id = selection->elements[i + 1]; if (select == SOLVER_SOLVABLE || select == SOLVER_SOLVABLE_ONE_OF) { - /* done by selection_addsrc, currently implies SELECTION_NAME */ + /* done by selection_addextra, currently implies SELECTION_NAME */ Queue q; Id p, pp; - Id rel = 0, relname = 0; int miss = 0; queue_init(&q); FOR_JOB_SELECT(p, pp, select, id) { Solvable *s = pool->solvables + p; - if (!rel || s->name != relname) - { - relname = s->name; - rel = pool_rel2id(pool, relname, relevr, relflags, 1); - } - if (pool_match_nevr(pool, s, rel)) + if (match_nevr_rel(pool, s, relflags, relevr)) queue_push(&q, p); else miss = 1; @@ -213,31 +230,48 @@ selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr) selection->elements[i + 1] = pool_rel2id(pool, id, relevr, relflags, 1); } else - continue; /* actually internal error */ + continue; /* actually cannot happen */ + + /* now add the setflags we gained */ if (relflags == REL_ARCH) - selection->elements[i] |= SOLVER_SETARCH; + selection->elements[i] |= SOLVER_SETARCH; if (relflags == REL_EQ && select != SOLVER_SOLVABLE_PROVIDES) - { + { if (pool->disttype == DISTTYPE_DEB) - selection->elements[i] |= SOLVER_SETEVR; /* debian can't match version only like rpm */ + selection->elements[i] |= SOLVER_SETEVR; /* debian can't match version only like rpm */ else { const char *rel = strrchr(pool_id2str(pool, relevr), '-'); selection->elements[i] |= rel ? SOLVER_SETEVR : SOLVER_SETEV; } - } + } } +} + +/* only supports simple rels plus REL_ARCH */ +/* prunes empty jobs */ +static void +selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr) +{ + selection_filter_rel_noprune(pool, selection, relflags, relevr); selection_prune(pool, selection); } +/* limit a selection to to repository */ +/* prunes empty jobs */ static void -selection_filter_installed(Pool *pool, Queue *selection) +selection_filter_repo(Pool *pool, Queue *selection, Repo *repo) { Queue q; int i, j; - if (!pool->installed) - queue_empty(selection); + if (!selection->count) + return; + if (!repo) + { + queue_empty(selection); + return; + } queue_init(&q); for (i = j = 0; i < selection->count; i += 2) { @@ -246,11 +280,11 @@ selection_filter_installed(Pool *pool, Queue *selection) if (select == SOLVER_SOLVABLE_ALL) { select = SOLVER_SOLVABLE_REPO; - id = pool->installed->repoid; + id = repo->repoid; } else if (select == SOLVER_SOLVABLE_REPO) { - if (id != pool->installed->repoid) + if (id != repo->repoid) select = 0; } else @@ -260,7 +294,7 @@ selection_filter_installed(Pool *pool, Queue *selection) queue_empty(&q); FOR_JOB_SELECT(p, pp, select, id) { - if (pool->solvables[p].repo != pool->installed) + if (pool->solvables[p].repo != repo) bad = 1; else queue_push(&q, p); @@ -268,7 +302,7 @@ selection_filter_installed(Pool *pool, Queue *selection) if (bad || !q.count) { if (!q.count) - select = 0; + select = 0; /* prune empty jobs */ else if (q.count == 1) { select = SOLVER_SOLVABLE | SOLVER_NOAUTOSET; @@ -281,58 +315,138 @@ selection_filter_installed(Pool *pool, Queue *selection) } } } - if (select) + if (!select) + continue; /* job is now empty */ + if (select == SOLVER_SOLVABLE_REPO) { - selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SETREPO; - selection->elements[j++] = id; + Id p; + Solvable *s; + FOR_REPO_SOLVABLES(repo, p, s) + break; + if (!p) + continue; /* repo is empty */ } + selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SETREPO; + selection->elements[j++] = id; } queue_truncate(selection, j); queue_free(&q); } + +static int +matchprovides(Pool *pool, Solvable *s, Id dep) +{ + Id id, *idp; + idp = s->repo->idarraydata + s->provides; + while ((id = *idp++) != 0) + if (pool_match_dep(pool, id, dep)) + return 1; + return 0; +} + +/* change a SOLVER_SOLVABLE_NAME/PROVIDES selection to something that also includes + * extra packages. + * extra packages are: src, badarch, disabled + */ static void -selection_addsrc(Pool *pool, Queue *selection, int flags) +selection_addextra(Pool *pool, Queue *selection, int flags) { Queue q; - Id p, name; - int i, havesrc; + Id p, pp, dep; + int i, isextra, haveextra, doprovides; if ((flags & SELECTION_INSTALLED_ONLY) != 0) - return; /* sources can't be installed */ + flags &= ~SELECTION_WITH_SOURCE; + + if (!(flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH))) + return; /* nothing to add */ + queue_init(&q); for (i = 0; i < selection->count; i += 2) { - if (selection->elements[i] != SOLVER_SOLVABLE_NAME) + if (selection->elements[i] == SOLVER_SOLVABLE_NAME) + doprovides = 0; + else if (selection->elements[i] == SOLVER_SOLVABLE_PROVIDES) + doprovides = 1; + else continue; - name = selection->elements[i + 1]; - havesrc = 0; + dep = selection->elements[i + 1]; + haveextra = 0; queue_empty(&q); + if (doprovides) + { + /* first put all non-extra packages on the queue */ + FOR_PROVIDES(p, pp, dep) + { + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && pool->solvables[p].repo != pool->installed) + continue; + queue_push(&q, p); + } + } FOR_POOL_SOLVABLES(p) { Solvable *s = pool->solvables + p; - if (s->name != name) + if (!doprovides && !pool_match_nevr(pool, s, dep)) continue; + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + continue; + isextra = 0; if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) { + if (!(flags & SELECTION_WITH_SOURCE) && !(flags & SELECTION_SOURCE_ONLY)) + continue; + if (!(flags & SELECTION_SOURCE_ONLY)) + isextra = 1; if (pool_disabled_solvable(pool, s)) + { + if (!(flags & SELECTION_WITH_DISABLED)) + continue; + isextra = 1; + } + } + else + { + if ((flags & SELECTION_SOURCE_ONLY) != 0) + continue; + if (s->repo != pool->installed) + { + if (pool_disabled_solvable(pool, s)) + { + if (!(flags & SELECTION_WITH_DISABLED)) + continue; + isextra = 1; + } + if (pool_badarch_solvable(pool, s)) + { + if (!(flags & SELECTION_WITH_BADARCH)) + continue; + isextra = 1; + } + } + } + if (doprovides) + { + if (!isextra) + continue; /* already done above in FOR_PROVIDES */ + if (!s->provides || !matchprovides(pool, s, dep)) continue; - havesrc = 1; } - else if (s->repo != pool->installed && !pool_installable(pool, s)) - continue; + haveextra |= isextra; queue_push(&q, p); } - if (!havesrc || !q.count) + if (!haveextra || !q.count) continue; if (q.count == 1) { - selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET; + selection->elements[i] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET; selection->elements[i + 1] = q.elements[0]; } else { - selection->elements[i] = SOLVER_SOLVABLE_ONE_OF; + if (doprovides) + solv_sort(q.elements, q.count, sizeof(Id), selection_solvables_sortcmp, NULL); + selection->elements[i] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF; selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q); } } @@ -360,173 +474,332 @@ queue_pushunique2(Queue *q, Id id1, Id id2) queue_push2(q, id1, id2); } + +/***** provides matching *****/ + static int -selection_depglob_id(Pool *pool, Queue *selection, Id id, int flags) +selection_addextra_provides(Pool *pool, Queue *selection, const char *name, int flags) { - Id p, pp; + Id p, id, *idp; int match = 0; + int doglob, nocase, globflags; - FOR_PROVIDES(p, pp, id) + if ((flags & SELECTION_INSTALLED_ONLY) != 0) + return 0; /* neither disabled nor badarch nor src */ + + nocase = flags & SELECTION_NOCASE; + doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0; + globflags = doglob && nocase ? FNM_CASEFOLD : 0; + + FOR_POOL_SOLVABLES(p) { + const char *n; Solvable *s = pool->solvables + p; - if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + if (!s->provides) + continue; + if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) /* no provides */ + continue; + if (s->repo == pool->installed) continue; - match = 1; - if (s->name == id && (flags & SELECTION_NAME) != 0) + if (pool_disabled_solvable(pool, s)) { - if ((flags & SELECTION_SOURCE_ONLY) != 0) - id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1); - queue_push2(selection, SOLVER_SOLVABLE_NAME, id); - if ((flags & SELECTION_WITH_SOURCE) != 0) - selection_addsrc(pool, selection, flags); - return SELECTION_NAME; + if (!(flags & SELECTION_WITH_DISABLED)) + continue; + if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s)) + continue; } - } - if ((flags & (SELECTION_SOURCE_ONLY | SELECTION_WITH_SOURCE)) != 0 && (flags & SELECTION_NAME) != 0) - { - /* src rpms don't have provides, so we must check every solvable */ - FOR_POOL_SOLVABLES(p) /* slow path */ + else if (pool_badarch_solvable(pool, s)) { - Solvable *s = pool->solvables + p; - if (s->name == id && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)) + if (!(flags & SELECTION_WITH_BADARCH)) + continue; + } + else + continue; + /* here is an extra solvable we need to consider */ + idp = s->repo->idarraydata + s->provides; + while ((id = *idp++) != 0) + { + while (ISRELDEP(id)) { - if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) - continue; /* just in case... src rpms can't be installed */ - if (pool_disabled_solvable(pool, s)) - continue; - if ((flags & SELECTION_SOURCE_ONLY) != 0) - id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1); - queue_push2(selection, SOLVER_SOLVABLE_NAME, id); - if ((flags & SELECTION_WITH_SOURCE) != 0) - selection_addsrc(pool, selection, flags); - return SELECTION_NAME; + Reldep *rd = GETRELDEP(pool, id); + id = rd->name; + } + if (pool->whatprovides[id] > 1) + continue; /* we already did that one in the normal code path */ + n = pool_id2str(pool, id); + if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0) + { + queue_pushunique2(selection, SOLVER_SOLVABLE_PROVIDES, id); + match = 1; } } } - if (match && (flags & SELECTION_PROVIDES) != 0) + return match; +} + +/* this is the fast path of selection_provides: the id for the name + * is known and thus we can quickly check the existance of a + * package with that provides */ +static int +selection_provides_id(Pool *pool, Queue *selection, Id id, int flags) +{ + Id p, pp; + + FOR_PROVIDES(p, pp, id) + { + Solvable *s = pool->solvables + p; + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + continue; + break; + } + if (p) { queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id); return SELECTION_PROVIDES; } + + if ((flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0) + { + queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id); + selection_addextra(pool, selection, flags); + if (selection->elements[0] == SOLVER_SOLVABLE_PROVIDES) + queue_empty(selection); + else + { + selection->elements[0] = SOLVER_SOLVABLE_PROVIDES; + selection->elements[1] = id; + } + return selection->count ? SELECTION_PROVIDES : 0; + } + return 0; } +/* add missing provides matchers to the selection */ +/* match the provides of a package */ +/* note that we only return raw SOLVER_SOLVABLE_PROVIDES jobs + * so that the selection can be modified later. */ static int -selection_depglob(Pool *pool, Queue *selection, const char *name, int flags) +selection_provides(Pool *pool, Queue *selection, const char *name, int flags) { Id id, p, pp; - int match = 0; - int doglob = 0; - int nocase = 0; - int globflags = 0; + int match; + int doglob; + int nocase; + int globflags; + const char *n; if ((flags & SELECTION_SOURCE_ONLY) != 0) - { - flags &= ~SELECTION_PROVIDES; /* sources don't provide anything */ - flags &= ~SELECTION_WITH_SOURCE; - } - - if (!(flags & (SELECTION_NAME|SELECTION_PROVIDES))) - return 0; - - if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed) - return 0; + return 0; /* sources do not have provides */ nocase = flags & SELECTION_NOCASE; - if (!nocase && !(flags & SELECTION_SKIP_KIND)) + if (!nocase) { + /* try the fast path first */ id = pool_str2id(pool, name, 0); if (id) { - /* the id is know, do the fast id matching using the whatprovides lookup */ - int ret = selection_depglob_id(pool, selection, id, flags); + /* the id is known, do the fast id matching */ + int ret = selection_provides_id(pool, selection, id, flags); if (ret) return ret; } } - if ((flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0) - doglob = 1; - - if (!nocase && !(flags & SELECTION_SKIP_KIND) && !doglob) - return 0; /* all done above in depglob_id */ - - if (doglob && nocase) - globflags = FNM_CASEFOLD; + doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0; + if (!nocase && !doglob) + { + /* all done above in selection_provides_id */ + return 0; + } - if ((flags & SELECTION_NAME) != 0) + /* looks like a glob or nocase match. really hard work. */ + match = 0; + globflags = doglob && nocase ? FNM_CASEFOLD : 0; + for (id = 1; id < pool->ss.nstrings; id++) { - /* looks like a name glob. hard work. */ - FOR_POOL_SOLVABLES(p) + /* do we habe packages providing this id? */ + if (!pool->whatprovides[id] || pool->whatprovides[id] == 1) + continue; + n = pool_id2str(pool, id); + if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0) { - Solvable *s = pool->solvables + p; - const char *n; - if (s->repo != pool->installed && !pool_installable(pool, s)) + if ((flags & SELECTION_INSTALLED_ONLY) != 0) { - if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)) - continue; - if (pool_disabled_solvable(pool, s)) + FOR_PROVIDES(p, pp, id) + if (pool->solvables[p].repo == pool->installed) + break; + if (!p) continue; } - if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id); + match = 1; + } + } + + if (flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) + match |= selection_addextra_provides(pool, selection, name, flags); + + return match ? SELECTION_PROVIDES : 0; +} + +/***** name matching *****/ + +/* this is the fast path of selection_name: the id for the name + * is known and thus we can quickly check the existance of a + * package with that name */ +static int +selection_name_id(Pool *pool, Queue *selection, Id id, int flags) +{ + Id p, pp, matchid; + + matchid = id; + if ((flags & SELECTION_SOURCE_ONLY) != 0) + { + /* sources cannot be installed */ + if ((flags & SELECTION_INSTALLED_ONLY) != 0) + return 0; + /* add ARCH_SRC to match only sources */ + matchid = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1); + flags &= ~SELECTION_WITH_SOURCE; + } + + FOR_PROVIDES(p, pp, matchid) + { + Solvable *s = pool->solvables + p; + if (s->name != id) + continue; + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + continue; + /* one match is all we need */ + queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid); + /* add the requested extra packages */ + if ((flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0) + selection_addextra(pool, selection, flags); + return SELECTION_NAME; + } + + if ((flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0) + { + queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid); + selection_addextra(pool, selection, flags); + if (selection->elements[0] == SOLVER_SOLVABLE_NAME) + queue_empty(selection); + return selection->count ? SELECTION_NAME : 0; + } + + if ((flags & SELECTION_WITH_SOURCE) != 0 && (flags & SELECTION_INSTALLED_ONLY) == 0) + { + /* WITH_SOURCE case, but we had no match. try SOURCE_ONLY instead */ + matchid = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1); + FOR_PROVIDES(p, pp, matchid) + { + Solvable *s = pool->solvables + p; + if (s->name != id) continue; - id = s->name; - n = pool_id2str(pool, id); - if (flags & SELECTION_SKIP_KIND) - n = skipkind(n); - if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0) - { - if ((flags & SELECTION_SOURCE_ONLY) != 0) - id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1); - queue_pushunique2(selection, SOLVER_SOLVABLE_NAME, id); - match = 1; - } + queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid); + return SELECTION_NAME; } - if (match) + } + return 0; +} + +/* match the name of a package */ +/* note that for SELECTION_INSTALLED_ONLY the result is not trimmed */ +static int +selection_name(Pool *pool, Queue *selection, const char *name, int flags) +{ + Id id, p; + int match; + int doglob, nocase; + int globflags; + const char *n; + + if ((flags & SELECTION_SOURCE_ONLY) != 0) + flags &= ~SELECTION_WITH_SOURCE; + + nocase = flags & SELECTION_NOCASE; + if (!nocase && !(flags & SELECTION_SKIP_KIND)) + { + /* try the fast path first */ + id = pool_str2id(pool, name, 0); + if (id) { - if ((flags & SELECTION_WITH_SOURCE) != 0) - selection_addsrc(pool, selection, flags); - return SELECTION_NAME; + int ret = selection_name_id(pool, selection, id, flags); + if (ret) + return ret; } } - if ((flags & SELECTION_PROVIDES)) + doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0; + if (!nocase && !(flags & SELECTION_SKIP_KIND) && !doglob) + return 0; /* all done above in selection_name_id */ + + /* do a name match over all packages. hard work. */ + match = 0; + globflags = doglob && nocase ? FNM_CASEFOLD : 0; + FOR_POOL_SOLVABLES(p) { - /* looks like a dep glob. really hard work. */ - for (id = 1; id < pool->ss.nstrings; id++) + Solvable *s = pool->solvables + p; + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + continue; + if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) { - const char *n; - if (!pool->whatprovides[id] || pool->whatprovides[id] == 1) + if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE)) continue; - n = pool_id2str(pool, id); - if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0) + if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s)) + continue; + } + else if (s->repo != pool->installed) + { + if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s)) + continue; + if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s)) + continue; + } + id = s->name; + n = pool_id2str(pool, id); + if (flags & SELECTION_SKIP_KIND) + n = skipkind(n); + if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0) + { + if ((flags & SELECTION_SOURCE_ONLY) != 0) { - if ((flags & SELECTION_INSTALLED_ONLY) != 0) - { - FOR_PROVIDES(p, pp, id) - if (pool->solvables[p].repo == pool->installed) - break; - if (!p) - continue; - } - queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id); - match = 1; + if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC) + continue; + id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1); } + queue_pushunique2(selection, SOLVER_SOLVABLE_NAME, id); + match = 1; } - if (match) - return SELECTION_PROVIDES; + } + if (match) + { + /* if there was a match widen the selector to include all extra packages */ + if ((flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0) + selection_addextra(pool, selection, flags); + return SELECTION_NAME; } return 0; } + +/***** SELECTION_DOTARCH and SELECTION_REL handling *****/ + +/* like selection_name, but check for a .arch suffix if the match did + not work and SELECTION_DOTARCH is used */ static int -selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags) +selection_name_arch(Pool *pool, Queue *selection, const char *name, int flags, int doprovides, int noprune) { int ret; const char *r; Id archid; - if ((ret = selection_depglob(pool, selection, name, flags)) != 0) + if (doprovides) + ret = selection_provides(pool, selection, name, flags); + else + ret = selection_name(pool, selection, name, flags); + if (ret) return ret; if (!(flags & SELECTION_DOTARCH)) return 0; @@ -537,65 +810,22 @@ selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags rname[r - name] = 0; if (archid == ARCH_SRC || archid == ARCH_NOSRC) flags |= SELECTION_SOURCE_ONLY; - if ((ret = selection_depglob(pool, selection, rname, flags)) != 0) + if (doprovides) + ret = selection_provides(pool, selection, rname, flags); + else + ret = selection_name(pool, selection, rname, flags); + if (ret) { - selection_filter_rel(pool, selection, REL_ARCH, archid); - solv_free(rname); - return ret | SELECTION_DOTARCH; + selection_filter_rel_noprune(pool, selection, REL_ARCH, archid); + if (!noprune) + selection_prune(pool, selection); } solv_free(rname); + return ret && selection->count ? ret | SELECTION_DOTARCH : 0; } return 0; } -static int -selection_filelist(Pool *pool, Queue *selection, const char *name, int flags) -{ - Dataiterator di; - Queue q; - int type; - - /* all files in the file list start with a '/' */ - if (*name != '/') - { - if (!(flags & SELECTION_GLOB)) - return 0; - if (*name != '*' && *name != '[' && *name != '?') - return 0; - } - type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB; - if ((flags & SELECTION_NOCASE) != 0) - type |= SEARCH_NOCASE; - queue_init(&q); - dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST); - while (dataiterator_step(&di)) - { - Solvable *s = pool->solvables + di.solvid; - if (!s->repo) - continue; - if (s->repo != pool->installed && !pool_installable(pool, s)) - { - if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)) - continue; - if (pool_disabled_solvable(pool, s)) - continue; - } - if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) - continue; - queue_push(&q, di.solvid); - dataiterator_skip_solvable(&di); - } - dataiterator_free(&di); - if (!q.count) - return 0; - if (q.count > 1) - queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q)); - else - queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]); - queue_free(&q); - return SELECTION_FILELIST; -} - static char * splitrel(char *rname, char *r, int *rflagsp) { @@ -622,43 +852,178 @@ splitrel(char *rname, char *r, int *rflagsp) r++; while (nend && (rname[nend - 1] == ' ' || rname[nend - 1] == '\t')) nend--; - if (!*rname || !*r) + if (nend <= 0 || !*r || !rflags) return 0; *rflagsp = rflags; rname[nend] = 0; return r; } +/* match name/provides, support DOTARCH and REL modifiers + */ static int -selection_rel(Pool *pool, Queue *selection, const char *name, int flags) +selection_name_arch_rel(Pool *pool, Queue *selection, const char *name, int flags, int doprovides) { - int ret, rflags = 0; - char *r, *rname; + int ret, rflags = 0, noprune; + char *r = 0, *rname = 0; - /* relation case, support: - * depglob rel - * depglob.arch rel - */ - rname = solv_strdup(name); - if ((r = strpbrk(rname, "<=>")) != 0) + /* try to split off an relation part */ + if ((flags & SELECTION_REL) != 0) { - if ((r = splitrel(rname, r, &rflags)) == 0) + if ((r = strpbrk(name, "<=>")) != 0) { - solv_free(rname); - return 0; + rname = solv_strdup(name); + r = rname + (r - name); + if ((r = splitrel(rname, r, &rflags)) == 0) + rname = solv_free(rname); } } - if ((ret = selection_depglob_arch(pool, selection, rname, flags)) != 0) + + /* check if we need to call selection_addextra */ + noprune = doprovides && (flags & (SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH)); + + if (!r) { - if (rflags) - selection_filter_rel(pool, selection, rflags, pool_str2id(pool, r, 1)); - solv_free(rname); - return ret | SELECTION_REL; + /* could not split off relation */ + ret = selection_name_arch(pool, selection, name, flags, doprovides, noprune); + if (ret && noprune) + { + selection_addextra(pool, selection, flags); + selection_prune(pool, selection); + } + return ret && selection->count ? ret : 0; + } + + /* we could split of a relation. prune name and then filter rel */ + ret = selection_name_arch(pool, selection, rname, flags, doprovides, noprune); + if (ret) + { + selection_filter_rel_noprune(pool, selection, rflags, pool_str2id(pool, r, 1)); + if (noprune) + selection_addextra(pool, selection, flags); + selection_prune(pool, selection); } solv_free(rname); - return 0; + return ret && selection->count ? ret | SELECTION_REL : 0; +} + +/***** filelist matching *****/ + +static int +selection_filelist_sortcmp(const void *ap, const void *bp, void *dp) +{ + Pool *pool = dp; + const Id *a = ap, *b = bp; + if (a[0] != b[0]) + return strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0])); + return a[1] - b[1]; +} + +static int +selection_filelist(Pool *pool, Queue *selection, const char *name, int flags) +{ + Dataiterator di; + Queue q; + Id id; + int type; + int i, j, lastid; + + /* all files in the file list start with a '/' */ + if (*name != '/') + { + if (!(flags & SELECTION_GLOB)) + return 0; + if (*name != '*' && *name != '[' && *name != '?') + return 0; + } + type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB; + if ((flags & SELECTION_NOCASE) != 0) + type |= SEARCH_NOCASE; + queue_init(&q); + dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST); + while (dataiterator_step(&di)) + { + Solvable *s = pool->solvables + di.solvid; + if (!s->repo) + continue; + if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) + { + if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE)) + continue; + if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s)) + continue; + } + else + { + if ((flags & SELECTION_SOURCE_ONLY) != 0) + continue; + if (s->repo != pool->installed) + { + if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s)) + continue; + if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s)) + continue; + } + } + if ((flags & SELECTION_FLAT) != 0) + { + /* don't bother with the complex stuff */ + queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, di.solvid); + dataiterator_skip_solvable(&di); + continue; + } + id = pool_str2id(pool, di.kv.str, 1); + queue_push2(&q, id, di.solvid); + } + dataiterator_free(&di); + if ((flags & SELECTION_FLAT) != 0) + { + queue_free(&q); + return selection->count ? SELECTION_FILELIST : 0; + } + if (!q.count) + { + queue_free(&q); + return 0; + } + solv_sort(q.elements, q.count / 2, 2 * sizeof(Id), selection_filelist_sortcmp, pool); + lastid = 0; + queue_push2(&q, 0, 0); + for (i = j = 0; i < q.count; i += 2) + { + if (q.elements[i] != lastid) + { + if (j == 1) + queue_pushunique2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]); + else if (j > 1) + { + int k; + Id *idp; + /* check if we already have it */ + for (k = 0; k < selection->count; k += 2) + { + if (selection->elements[k] != SOLVER_SOLVABLE_ONE_OF) + continue; + idp = pool->whatprovidesdata + selection->elements[k + 1]; + if (!memcmp(idp, q.elements, j * sizeof(Id)) && !idp[j]) + break; + } + if (k == selection->count) + queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_ids2whatprovides(pool, q.elements, j)); + } + lastid = q.elements[i]; + j = 0; + } + if (!j || q.elements[j - 1] != q.elements[i]) + q.elements[j++] = q.elements[i + 1]; + } + queue_free(&q); + return SELECTION_FILELIST; } + +/***** canon name matching *****/ + #if defined(MULTI_SEMANTICS) # define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE) #elif defined(DEBIAN) @@ -669,11 +1034,22 @@ selection_rel(Pool *pool, Queue *selection, const char *name, int flags) /* magic epoch promotion code, works only for SELECTION_NAME selections */ static void -selection_filter_evr(Pool *pool, Queue *selection, char *evr) +selection_filter_evr(Pool *pool, Queue *selection, const char *evr) { int i, j; Queue q; Id qbuf[10]; + const char *sp; + + /* do we already have an epoch? */ + for (sp = evr; *sp >= '0' && *sp <= '9'; sp++) + ; + if (*sp == ':' && sp != evr) + { + /* yes, just add the rel filter */ + selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, evr, 1)); + return; + } queue_init(&q); queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf)); @@ -690,7 +1066,6 @@ selection_filter_evr(Pool *pool, Queue *selection, char *evr) { Solvable *s = pool->solvables + p; const char *sevr = pool_id2str(pool, s->evr); - const char *sp; for (sp = sevr; *sp >= '0' && *sp <= '9'; sp++) ; if (*sp != ':') @@ -772,7 +1147,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags) rname = solv_strdup(name); /* so we can modify it */ r = rname + (r - name); *r++ = 0; - if ((ret = selection_depglob(pool, selection, rname, flags)) == 0) + if ((ret = selection_name(pool, selection, rname, flags)) == 0) { solv_free(rname); return 0; @@ -785,7 +1160,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags) } selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1)); solv_free(rname); - return ret | SELECTION_CANON; + return selection->count ? ret | SELECTION_CANON : 0; } if (pool->disttype == DISTTYPE_HAIKU) @@ -795,7 +1170,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags) rname = solv_strdup(name); /* so we can modify it */ r = rname + (r - name); *r++ = 0; - if ((ret = selection_depglob(pool, selection, rname, flags)) == 0) + if ((ret = selection_name(pool, selection, rname, flags)) == 0) { solv_free(rname); return 0; @@ -808,7 +1183,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags) } selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1)); solv_free(rname); - return ret | SELECTION_CANON; + return selection->count ? ret | SELECTION_CANON : 0; } if ((r = strrchr(name, '-')) == 0) @@ -824,7 +1199,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags) flags |= SELECTION_SOURCE_ONLY; /* try with just the version */ - if ((ret = selection_depglob(pool, selection, rname, flags)) == 0) + if ((ret = selection_name(pool, selection, rname, flags)) == 0) { /* no luck, try with version-release */ if ((r2 = strrchr(rname, '-')) == 0) @@ -835,7 +1210,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags) *r = '-'; *r2 = 0; r = r2; - if ((ret = selection_depglob(pool, selection, rname, flags)) == 0) + if ((ret = selection_name(pool, selection, rname, flags)) == 0) { solv_free(rname); return 0; @@ -845,33 +1220,167 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags) selection_filter_rel(pool, selection, REL_ARCH, archid); selection_filter_evr(pool, selection, r + 1); /* magic epoch promotion */ solv_free(rname); - return ret | SELECTION_CANON; + return selection->count ? ret | SELECTION_CANON : 0; +} + +/* return the needed withbits to match the provided selection */ +static int +selection_extrabits(Pool *pool, Queue *selection, int flags) +{ + int i, needflags, isextra; + int allflags; + Id p; + Solvable *s; + Queue qlimit; + + allflags = flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH); + if (!selection->count) + return allflags; + if (selection->count == 2 && selection->elements[0] == SOLVER_SOLVABLE_ALL) + return allflags; /* don't bother */ + queue_init(&qlimit); + selection_solvables(pool, selection, &qlimit); + needflags = 0; + for (i = 0; i < qlimit.count; i++) + { + p = qlimit.elements[i]; + s = pool->solvables + p; + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + continue; + isextra = 0; + if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) + { + if (!(flags & SELECTION_WITH_SOURCE)) + continue; + isextra |= SELECTION_WITH_SOURCE; + if (pool_disabled_solvable(pool, s)) + { + if (!(flags & SELECTION_WITH_DISABLED)) + continue; + isextra |= SELECTION_WITH_DISABLED; + } + } + else + { + if ((flags & SELECTION_SOURCE_ONLY) != 0) + continue; + if (s->repo != pool->installed) + { + if (pool_disabled_solvable(pool, s)) + { + if (!(flags & SELECTION_WITH_DISABLED)) + continue; + isextra |= SELECTION_WITH_DISABLED; + } + if (pool_badarch_solvable(pool, s)) + { + if (!(flags & SELECTION_WITH_BADARCH)) + continue; + isextra |= SELECTION_WITH_BADARCH; + } + } + } + if (isextra) + { + needflags |= isextra; + if (needflags == allflags) + break; + } + } + queue_free(&qlimit); + return needflags; } int selection_make(Pool *pool, Queue *selection, const char *name, int flags) { int ret = 0; + if ((flags & SELECTION_MODEBITS) != 0) + { + Queue q; + queue_init(&q); + if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT || (flags & SELECTION_MODEBITS) == SELECTION_FILTER) + { + if (!selection->count) + { + queue_free(&q); + return 0; + } + if ((flags & (SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH | SELECTION_WITH_SOURCE)) != 0) + { + /* try to drop expensive extra bits */ + flags = (flags & ~(SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH | SELECTION_WITH_SOURCE)) | selection_extrabits(pool, selection, flags); + } + } + ret = selection_make(pool, &q, name, flags & ~SELECTION_MODEBITS); + if ((flags & SELECTION_MODEBITS) == SELECTION_ADD) + selection_add(pool, selection, &q); + else if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT) + selection_subtract(pool, selection, &q); + else if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY)) + { + if ((flags & SELECTION_FILTER_SWAPPED) != 0) + { + selection_filter(pool, &q, selection); + queue_free(selection); + queue_init_clone(selection, &q); + } + else + selection_filter(pool, selection, &q); + } + queue_free(&q); + return ret; + } queue_empty(selection); + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed) + return 0; + + /* here come our four selection modes */ if ((flags & SELECTION_FILELIST) != 0) ret = selection_filelist(pool, selection, name, flags); - if (!ret && (flags & SELECTION_REL) != 0 && strpbrk(name, "<=>") != 0) - ret = selection_rel(pool, selection, name, flags); - if (!ret) - ret = selection_depglob_arch(pool, selection, name, flags); + if (!ret && (flags & SELECTION_NAME) != 0) + ret = selection_name_arch_rel(pool, selection, name, flags, 0); + if (!ret && (flags & SELECTION_PROVIDES) != 0) + ret = selection_name_arch_rel(pool, selection, name, flags, 1); if (!ret && (flags & SELECTION_CANON) != 0) ret = selection_canon(pool, selection, name, flags); - if (selection->count && (flags & SELECTION_INSTALLED_ONLY) != 0) - selection_filter_installed(pool, selection); - if (ret && !selection->count) - ret = 0; /* no match -> always return zero */ + + /* now do result filtering */ + if (ret && (flags & SELECTION_INSTALLED_ONLY) != 0) + selection_filter_repo(pool, selection, pool->installed); + + /* flatten if requested */ if (ret && (flags & SELECTION_FLAT) != 0) selection_flatten(pool, selection); - return ret; + return selection->count ? ret : 0; } -static inline int +struct limiter { + int start; /* either 2 or repofilter->start */ + int end; /* either nsolvables or repofilter->end */ + Id *mapper; + Repo *repofilter; +}; + +/* add matching src packages to simple SOLVABLE_NAME selections */ +static void +setup_limiter(Pool *pool, int flags, struct limiter *limiter) +{ + limiter->start = 2; + limiter->end = pool->nsolvables; + limiter->mapper = 0; + limiter->repofilter = 0; + if ((flags & SELECTION_INSTALLED_ONLY) != 0) + { + Repo *repo = pool->installed; + limiter->repofilter = repo; + limiter->start = repo ? repo->start : 0; + limiter->end = repo ? repo->end : 0; + } +} + +static int matchdep_str(const char *pattern, const char *string, int flags) { if (flags & SELECTION_GLOB) @@ -884,161 +1393,315 @@ matchdep_str(const char *pattern, const char *string, int flags) return strcmp(pattern, string) == 0 ? 1 : 0; } +/* like pool_match_dep but uses matchdep_str to match the name for glob and nocase matching */ static int -matchdep(Pool *pool, Id id, char *rname, int rflags, char *revr, int flags) +matchdep(Pool *pool, Id id, char *rname, int rflags, Id revr, int flags) { if (ISRELDEP(id)) { Reldep *rd = GETRELDEP(pool, id); - if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH || rd->flags == REL_WITHOUT || rd->flags == REL_COND || rd->flags == REL_UNLESS) + if (rd->flags > 7) { - if (matchdep(pool, rd->name, rname, rflags, revr, flags)) - return 1; - if ((rd->flags == REL_COND || rd->flags == REL_UNLESS) && ISRELDEP(rd->evr)) + if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH || rd->flags == REL_WITHOUT || rd->flags == REL_COND || rd->flags == REL_UNLESS) { - rd = GETRELDEP(pool, rd->evr); - if (rd->flags != REL_ELSE) - return 0; + if (matchdep(pool, rd->name, rname, rflags, revr, flags)) + return 1; + if ((rd->flags == REL_COND || rd->flags == REL_UNLESS) && ISRELDEP(rd->evr)) + { + rd = GETRELDEP(pool, rd->evr); + if (rd->flags != REL_ELSE) + return 0; + } + if (rd->flags != REL_COND && rd->flags != REL_UNLESS && rd->flags != REL_WITHOUT && matchdep(pool, rd->evr, rname, rflags, revr, flags)) + return 1; + return 0; } - if (rd->flags != REL_COND && rd->flags != REL_UNLESS && rd->flags != REL_WITHOUT && matchdep(pool, rd->evr, rname, rflags, revr, flags)) - return 1; - return 0; + if (rd->flags == REL_ARCH) + return matchdep(pool, rd->name, rname, rflags, revr, flags); } - if (rd->flags == REL_ARCH) - return matchdep(pool, rd->name, rname, rflags, revr, flags); if (!matchdep(pool, rd->name, rname, rflags, revr, flags)) return 0; - if (rflags) - { - /* XXX: need pool_match_flags_evr here */ - if (!pool_match_dep(pool, pool_rel2id(pool, rd->name, pool_str2id(pool, revr, 1), rflags, 1), id)) - return 0; - } + if (rflags && !pool_intersect_evrs(pool, rd->flags, rd->evr, rflags, revr)) + return 0; return 1; } return matchdep_str(rname, pool_id2str(pool, id), flags); } -/* - * select against the dependencies in keyname - * like SELECTION_REL and SELECTION_PROVIDES, but with the - * deps in keyname instead of provides. - */ -int -selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker) +static int +selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char *name, Id dep, int flags, int keyname, int marker, struct limiter *limiter) { - char *rname, *r = 0; + int li, i, j; + int ret = 0; + char *rname = 0, *r = 0; int rflags = 0; + Id revr = 0; Id p; Queue q; queue_empty(selection); - rname = solv_strdup(name); - if (!(flags & SELECTION_MATCH_DEPSTR)) + if (!limiter->end) + return 0; + if (!name && !dep) + return 0; + + if ((flags & SELECTION_MATCH_DEPSTR) != 0) + flags &= ~SELECTION_REL; + + if (name) { - if ((r = strpbrk(rname, "<=>")) != 0) + rname = solv_strdup(name); + if ((flags & SELECTION_REL) != 0) { - if ((r = splitrel(rname, r, &rflags)) == 0) + if ((r = strpbrk(rname, "<=>")) != 0) { - solv_free(rname); - return 0; + if ((r = splitrel(rname, r, &rflags)) == 0) + { + solv_free(rname); + return 0; + } } + revr = pool_str2id(pool, r, 1); + ret |= SELECTION_REL; + } + if ((flags & SELECTION_GLOB) != 0 && !strpbrk(rname, "[*?") != 0) + flags &= ~SELECTION_GLOB; + + if ((flags & SELECTION_GLOB) == 0 && (flags & SELECTION_NOCASE) == 0 && (flags & SELECTION_MATCH_DEPSTR) == 0) + { + /* we can use the faster selection_make_matchdepid */ + dep = pool_str2id(pool, rname, 1); + if (rflags) + dep = pool_rel2id(pool, dep, revr, rflags, 1); + rname = solv_free(rname); + name = 0; + } + } + if (dep) + { + if (keyname == SOLVABLE_NAME && (flags & SELECTION_MATCH_DEPSTR) != 0) + { + Reldep *rd; + if (!ISRELDEP(dep)) + return 0; + rd = GETRELDEP(pool, dep); + if (!rd->name || rd->flags != REL_EQ) + return 0; + dep = rd->name; + rflags = rd->flags; + revr = rd->evr; } } - if ((flags & SELECTION_GLOB) != 0 && !strpbrk(rname, "[*?") != 0) - flags &= ~SELECTION_GLOB; queue_init(&q); - FOR_POOL_SOLVABLES(p) + for (li = limiter->start; li < limiter->end; li++) { - Solvable *s = pool->solvables + p; - int i; - - if (s->repo != pool->installed && !pool_installable(pool, s)) + Solvable *s; + p = limiter->mapper ? limiter->mapper[li] : li; + s = pool->solvables + p; + if (!s->repo || (limiter->repofilter && s->repo != limiter->repofilter)) + continue; + if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) { - if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)) + if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE)) continue; - if (pool_disabled_solvable(pool, s)) + if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s)) continue; } - if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) - continue; - if ((s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) && !(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE)) - continue; + else + { + if ((flags & SELECTION_SOURCE_ONLY) != 0) + continue; + if (s->repo != pool->installed) + { + if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s)) + continue; + if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s)) + continue; + } + } + if (keyname == SOLVABLE_NAME) /* nevr match hack */ + { + if (dep) + { + if ((flags & SELECTION_MATCH_DEPSTR) != 0) + { + if (s->name != dep || s->evr != revr) + continue; + } + else + { + if (!pool_match_nevr(pool, s, dep)) + continue; + } + } + else + { + if ((flags & SELECTION_MATCH_DEPSTR) != 0) /* mis-use */ + { + char *tmp = pool_tmpjoin(pool, pool_id2str(pool, s->name), " = ", pool_id2str(pool, s->evr)); + if (!matchdep_str(rname, tmp, flags)) + continue; + } + else + { + if (!matchdep(pool, s->name, rname, rflags, revr, flags)) + continue; + if (rflags && !pool_intersect_evrs(pool, rflags, revr, REL_EQ, s->evr)) + continue; + } + } + queue_push(selection, p); + continue; + } queue_empty(&q); repo_lookup_deparray(s->repo, p, keyname, &q, marker); - for (i = 0; i < q.count; i++) + if (!q.count) + continue; + if (dep) + { + if ((flags & SELECTION_MATCH_DEPSTR) != 0) /* mis-use */ + { + for (i = 0; i < q.count; i++) + if (q.elements[i] == dep) + break; + } + else + { + for (i = 0; i < q.count; i++) + if (pool_match_dep(pool, q.elements[i], dep)) + break; + } + } + else { - Id id = q.elements[i]; if ((flags & SELECTION_MATCH_DEPSTR) != 0) { - if (matchdep_str(rname, pool_dep2str(pool, id), flags)) - break; - continue; + for (i = 0; i < q.count; i++) + if (matchdep_str(rname, pool_dep2str(pool, q.elements[i]), flags)) + break; + } + else + { + for (i = 0; i < q.count; i++) + if (matchdep(pool, q.elements[i], rname, rflags, revr, flags)) + break; } - if (matchdep(pool, id, rname, rflags, r, flags)) - break; } if (i < q.count) - queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, p); + queue_push(selection, p); } queue_free(&q); solv_free(rname); if (!selection->count) return 0; + + /* convert package list to selection */ + j = selection->count; + queue_insertn(selection, 0, selection->count, 0); + for (i = 0; i < selection->count; ) + { + selection->elements[i++] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET; + selection->elements[i++] = selection->elements[j++]; + } + if ((flags & SELECTION_FLAT) != 0) selection_flatten(pool, selection); - return SELECTION_PROVIDES; + return ret | (keyname == SOLVABLE_NAME ? SELECTION_NAME : SELECTION_PROVIDES); } -int -selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int flags, int keyname, int marker) +static int +selection_make_matchdeps_common(Pool *pool, Queue *selection, const char *name, Id dep, int flags, int keyname, int marker) { - Id p; - Queue q; + struct limiter limiter; - queue_empty(selection); - if (!dep) - return 0; - queue_init(&q); - FOR_POOL_SOLVABLES(p) + setup_limiter(pool, flags, &limiter); + if ((flags & SELECTION_MODEBITS) != SELECTION_REPLACE) { - Solvable *s = pool->solvables + p; - int i; - - if (s->repo != pool->installed && !pool_installable(pool, s)) + int ret; + Queue q, qlimit; + queue_init(&q); + queue_init(&qlimit); + /* deal with special filter cases */ + if ((flags & SELECTION_MODEBITS) == SELECTION_FILTER && selection->count == 2 && limiter.end) { - if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)) - continue; - if (pool_disabled_solvable(pool, s)) - continue; + if ((selection->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL) + flags = (flags & ~SELECTION_MODEBITS) | SELECTION_REPLACE; + else if ((selection->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_REPO) + { + Repo *repo = pool_id2repo(pool, selection->elements[1]); + if (limiter.repofilter && repo != limiter.repofilter) + repo = 0; + limiter.repofilter = repo; + limiter.start = repo ? repo->start : 0; + limiter.end = repo ? repo->end : 0; + flags = (flags & ~SELECTION_MODEBITS) | SELECTION_REPLACE; + } } - if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) - continue; - if ((s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) && !(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE)) - continue; - queue_empty(&q); - repo_lookup_deparray(s->repo, p, keyname, &q, marker); - for (i = 0; i < q.count; i++) + if (limiter.end && ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT || (flags & SELECTION_MODEBITS) == SELECTION_FILTER)) + { + selection_solvables(pool, selection, &qlimit); + limiter.start = 0; + limiter.end = qlimit.count; + limiter.mapper = qlimit.elements; + } + ret = selection_make_matchdeps_common_limited(pool, &q, name, dep, flags & ~SELECTION_MODEBITS, keyname, marker, &limiter); + queue_free(&qlimit); + if ((flags & SELECTION_MODEBITS) == SELECTION_ADD) + selection_add(pool, selection, &q); + else if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT) + selection_subtract(pool, selection, &q); + else if ((flags & SELECTION_MODEBITS) == SELECTION_FILTER) { - if ((flags & SELECTION_MATCH_DEPSTR) != 0) /* mis-use */ + if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY)) { - if (q.elements[i] == dep) - break; - continue; + if ((flags & SELECTION_FILTER_SWAPPED) != 0) + { + selection_filter(pool, &q, selection); + queue_free(selection); + queue_init_clone(selection, &q); + } + else + selection_filter(pool, selection, &q); } - if (pool_match_dep(pool, q.elements[i], dep)) - break; } - if (i < q.count) - queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, p); + else if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY)) + { + /* special filter case from above */ + int i; + Id f = selection->elements[0] & ~(SOLVER_SELECTMASK|SOLVER_NOAUTOSET); /* job, jobflags, setflags */ + queue_free(selection); + queue_init_clone(selection, &q); + for (i = 0; i < selection->count; i += 2) + selection->elements[i] = (selection->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | f; + } + queue_free(&q); + return ret; } - queue_free(&q); - if (!selection->count) - return 0; - if ((flags & SELECTION_FLAT) != 0) - selection_flatten(pool, selection); - return SELECTION_PROVIDES; + return selection_make_matchdeps_common_limited(pool, selection, name, dep, flags, keyname, marker, &limiter); +} + +/* + * select against the dependencies in keyname + * like SELECTION_PROVIDES, but with the deps in keyname instead of provides. + * supported match modifiers: + * SELECTION_REL + * SELECTION_GLOB + * SELECTION_NOCASE + */ +int +selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker) +{ + return selection_make_matchdeps_common(pool, selection, name, 0, flags, keyname, marker); +} + +/* + * select against the dependency id in keyname + */ +int +selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int flags, int keyname, int marker) +{ + return selection_make_matchdeps_common(pool, selection, 0, dep, flags, keyname, marker); } static inline int @@ -1064,10 +1727,81 @@ pool_is_kind(Pool *pool, Id name, Id kind) } } -void -selection_filter(Pool *pool, Queue *sel1, Queue *sel2) +static void +selection_filter_map(Pool *pool, Queue *sel, Map *m, int setflags) { int i, j, miss; + Queue q; + Id p, pp; + + queue_init(&q); + for (i = j = 0; i < sel->count; i += 2) + { + Id select = sel->elements[i] & SOLVER_SELECTMASK; + queue_empty(&q); + miss = 0; + if (select == SOLVER_SOLVABLE_ALL) + { + FOR_POOL_SOLVABLES(p) + { + if (map_tst(m, p)) + queue_push(&q, p); + else + miss = 1; + } + } + else if (select == SOLVER_SOLVABLE_REPO) + { + Solvable *s; + Repo *repo = pool_id2repo(pool, sel->elements[i + 1]); + if (repo) + { + FOR_REPO_SOLVABLES(repo, p, s) + { + if (map_tst(m, p)) + queue_push(&q, p); + else + miss = 1; + } + } + } + else + { + FOR_JOB_SELECT(p, pp, select, sel->elements[i + 1]) + { + if (map_tst(m, p)) + queue_pushunique(&q, p); + else + miss = 1; + } + } + if (!q.count) + continue; + if (!miss) + { + sel->elements[j] = sel->elements[i] | setflags; + sel->elements[j + 1] = sel->elements[i + 1]; + } + else if (q.count > 1) + { + sel->elements[j] = (sel->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags; + sel->elements[j + 1] = pool_queuetowhatprovides(pool, &q); + } + else + { + sel->elements[j] = (sel->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags; + sel->elements[j + 1] = q.elements[0]; + } + j += 2; + } + queue_truncate(sel, j); + queue_free(&q); +} + +static void +selection_filter_int(Pool *pool, Queue *sel1, Queue *sel2, int invert) +{ + int i, j; Id p, pp, q1filled = 0; Queue q1; Map m2; @@ -1075,10 +1809,12 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2) if (!sel1->count || !sel2->count) { + if (invert && !sel2->count) + return; queue_empty(sel1); return; } - if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL) + if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL && !invert) { /* XXX: not 100% correct, but very useful */ p = sel1->elements[0] & ~(SOLVER_SELECTMASK | SOLVER_SETMASK); /* job & jobflags */ @@ -1088,6 +1824,8 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2) sel1->elements[i] = (sel1->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | p ; return; } + + /* convert sel2 into a map */ queue_init(&q1); map_init(&m2, pool->nsolvables); for (i = 0; i < sel2->count; i += 2) @@ -1097,6 +1835,8 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2) { queue_free(&q1); map_free(&m2); + if (invert) + queue_empty(sel1); return; } if (select == SOLVER_SOLVABLE_REPO) @@ -1147,78 +1887,34 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2) map_set(&m2, p); } } - if (sel2->count == 2) /* XXX: AND all setmasks instead? */ - setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET; - for (i = j = 0; i < sel1->count; i += 2) - { - Id select = sel1->elements[i] & SOLVER_SELECTMASK; - queue_empty(&q1); - miss = 0; - if (select == SOLVER_SOLVABLE_ALL) - { - FOR_POOL_SOLVABLES(p) - { - if (map_tst(&m2, p)) - queue_push(&q1, p); - else - miss = 1; - } - } - else if (select == SOLVER_SOLVABLE_REPO) - { - Solvable *s; - Repo *repo = pool_id2repo(pool, sel1->elements[i + 1]); - if (repo) - { - FOR_REPO_SOLVABLES(repo, p, s) - { - if (map_tst(&m2, p)) - queue_push(&q1, p); - else - miss = 1; - } - } - } - else - { - FOR_JOB_SELECT(p, pp, select, sel1->elements[i + 1]) - { - if (map_tst(&m2, p)) - queue_pushunique(&q1, p); - else - miss = 1; - } - } - if (!q1.count) - continue; - if (!miss) - { - sel1->elements[j] = sel1->elements[i] | setflags; - sel1->elements[j + 1] = sel1->elements[i + 1]; - } - else if (q1.count > 1) - { - sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags; - sel1->elements[j + 1] = pool_queuetowhatprovides(pool, &q1); - } - else - { - sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags; - sel1->elements[j + 1] = q1.elements[0]; - } - j += 2; - } - queue_truncate(sel1, j); queue_free(&q1); + + /* now filter sel1 with the map */ + if (invert) + map_invertall(&m2); + if (sel2->count == 2) + setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET; + selection_filter_map(pool, sel1, &m2, setflags); map_free(&m2); } void +selection_filter(Pool *pool, Queue *sel1, Queue *sel2) +{ + selection_filter_int(pool, sel1, sel2, 0); +} + +void selection_add(Pool *pool, Queue *sel1, Queue *sel2) { - int i; - for (i = 0; i < sel2->count; i++) - queue_push(sel1, sel2->elements[i]); + if (sel2->count) + queue_insertn(sel1, sel1->count, sel2->count, sel2->elements); +} + +void +selection_subtract(Pool *pool, Queue *sel1, Queue *sel2) +{ + selection_filter_int(pool, sel1, sel2, 1); } const char * diff --git a/src/selection.h b/src/selection.h index 0dd6150..9938c2f 100644 --- a/src/selection.h +++ b/src/selection.h @@ -19,21 +19,45 @@ extern "C" { #endif +/* what to match */ #define SELECTION_NAME (1 << 0) #define SELECTION_PROVIDES (1 << 1) #define SELECTION_FILELIST (1 << 2) #define SELECTION_CANON (1 << 3) -#define SELECTION_DOTARCH (1 << 4) -#define SELECTION_REL (1 << 5) -#define SELECTION_INSTALLED_ONLY (1 << 8) +/* match extensions */ +#define SELECTION_DOTARCH (1 << 4) /* allow ".arch" suffix */ +#define SELECTION_REL (1 << 5) /* allow "<=> rel" suffix */ + +/* string comparison modifiers */ #define SELECTION_GLOB (1 << 9) -#define SELECTION_FLAT (1 << 10) #define SELECTION_NOCASE (1 << 11) + +/* extra flags */ +#define SELECTION_FLAT (1 << 10) /* flatten the resulting selection */ +#define SELECTION_SKIP_KIND (1 << 14) /* remove kind: name prefix in SELECTION_NAME matches */ +#define SELECTION_MATCH_DEPSTR (1 << 15) /* match dep2str result */ + +/* package selection */ +#define SELECTION_INSTALLED_ONLY (1 << 8) #define SELECTION_SOURCE_ONLY (1 << 12) #define SELECTION_WITH_SOURCE (1 << 13) -#define SELECTION_SKIP_KIND (1 << 14) -#define SELECTION_MATCH_DEPSTR (1 << 15) +#define SELECTION_WITH_DISABLED (1 << 16) +#define SELECTION_WITH_BADARCH (1 << 17) +#define SELECTION_WITH_ALL (SELECTION_WITH_SOURCE | SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH) + +/* result operator */ +#define SELECTION_REPLACE (0 << 28) +#define SELECTION_ADD (1 << 28) +#define SELECTION_SUBTRACT (2 << 28) +#define SELECTION_FILTER (3 << 28) + +#define SELECTION_MODEBITS (3 << 28) /* internal */ + +/* extra SELECTION_FILTER bits */ +#define SELECTION_FILTER_KEEP_IFEMPTY (1 << 30) +#define SELECTION_FILTER_SWAPPED (1 << 31) + extern int selection_make(Pool *pool, Queue *selection, const char *name, int flags); extern int selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker); @@ -41,6 +65,8 @@ extern int selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int extern void selection_filter(Pool *pool, Queue *sel1, Queue *sel2); extern void selection_add(Pool *pool, Queue *sel1, Queue *sel2); +extern void selection_subtract(Pool *pool, Queue *sel1, Queue *sel2); + extern void selection_solvables(Pool *pool, Queue *selection, Queue *pkgs); extern const char *pool_selection2str(Pool *pool, Queue *selection, Id flagmask); diff --git a/src/solver.c b/src/solver.c index 037b33d..6405f4a 100644 --- a/src/solver.c +++ b/src/solver.c @@ -31,82 +31,6 @@ #define RULES_BLOCK 63 -static Id -autouninstall(Solver *solv, Id *problem) -{ - Pool *pool = solv->pool; - int i; - int lastfeature = 0, lastupdate = 0; - Id v; - Id extraflags = -1; - Map *m = 0; - - if (!solv->allowuninstall && !solv->allowuninstall_all) - { - if (!solv->allowuninstallmap.size) - return 0; /* why did we get called? */ - m = &solv->allowuninstallmap; - } - for (i = 0; (v = problem[i]) != 0; i++) - { - if (v < 0) - extraflags &= solv->job.elements[-v - 1]; - if (v >= solv->updaterules && v < solv->updaterules_end) - { - Rule *r; - if (m && !MAPTST(m, v - solv->updaterules)) - continue; - /* check if identical to feature rule, we don't like that (except for orphans) */ - r = solv->rules + solv->featurerules + (v - solv->updaterules); - if (!r->p) - { - /* update rule == feature rule */ - if (v > lastfeature) - lastfeature = v; - /* prefer orphaned packages in dup mode */ - if (solv->keep_orphans) - { - r = solv->rules + v; - if (!r->d && !r->w2 && r->p == (solv->installed->start + (v - solv->updaterules))) - { - lastfeature = v; - lastupdate = 0; - break; - } - } - continue; - } - if (v > lastupdate) - lastupdate = v; - } - } - if (!lastupdate && !lastfeature) - return 0; - v = lastupdate ? lastupdate : lastfeature; - POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "allowuninstall disabling "); - solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + v); - solver_disableproblem(solv, v); - if (extraflags != -1 && (extraflags & SOLVER_CLEANDEPS) != 0 && solv->cleandepsmap.size) - { - /* add the package to the updatepkgs list, this will automatically turn - * on cleandeps mode */ - Id p = solv->rules[v].p; - if (!solv->cleandeps_updatepkgs) - { - solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue)); - queue_init(solv->cleandeps_updatepkgs); - } - if (p > 0) - { - int oldupdatepkgscnt = solv->cleandeps_updatepkgs->count; - queue_pushunique(solv->cleandeps_updatepkgs, p); - if (solv->cleandeps_updatepkgs->count != oldupdatepkgscnt) - solver_disablepolicyrules(solv); - } - } - return v; -} - /************************************************************************/ /* @@ -164,17 +88,18 @@ enabledisablelearntrules(Solver *solv) * If we find a conflict, disable rules and add them to problem queue. */ -static void -makeruledecisions(Solver *solv) +static int +makeruledecisions(Solver *solv, int disablerules) { Pool *pool = solv->pool; - int i, ri, ii; + int i, ri, ii, ori; Rule *r, *rr; Id v, vv; int decisionstart; int record_proof = 1; int oldproblemcount; int havedisabled = 0; + int doautouninstall; /* The system solvable is always installed first */ assert(solv->decisionq.count == 0); @@ -222,7 +147,7 @@ makeruledecisions(Solver *solv) if (!solv->decisionmap[vv]) /* if not yet decided */ { queue_push(&solv->decisionq, v); - queue_push(&solv->decisionq_why, r - solv->rules); + queue_push(&solv->decisionq_why, ri); solv->decisionmap[vv] = v > 0 ? 1 : -1; IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE) { @@ -254,14 +179,13 @@ makeruledecisions(Solver *solv) /* conflict with a learnt rule */ /* can happen when packages cannot be installed for multiple reasons. */ /* we disable the learnt rule in this case */ - /* (XXX: we should really call analyze_unsolvable_rule here!) */ + /* (XXX: we should really do something like analyze_unsolvable_rule here!) */ solver_disablerule(solv, r); continue; } POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "ANALYZE UNSOLVABLE ASSERTION ----------------------\n"); - IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE) - solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + ri); + assert(ri >= solv->pkgrules_end); /* must not have a conflict in the pkg rules! */ /* * find the decision which is the "opposite" of the rule @@ -270,136 +194,94 @@ makeruledecisions(Solver *solv) if (solv->decisionq.elements[i] == -v) break; assert(i < solv->decisionq.count); /* assert that we found it */ - oldproblemcount = solv->problems.count; - - /* - * conflict with system solvable ? - */ if (v == -SYSTEMSOLVABLE) + ori = 0; + else { - if (record_proof) - { - queue_push(&solv->problems, solv->learnt_pool.count); - queue_push(&solv->learnt_pool, ri); - queue_push(&solv->learnt_pool, 0); - } - else - queue_push(&solv->problems, 0); - POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with system solvable, disabling rule #%d\n", ri); - if (ri >= solv->jobrules && ri < solv->jobrules_end) - v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1); - else - v = ri; - queue_push(&solv->problems, v); - queue_push(&solv->problems, 0); - if (v >= solv->featurerules && v < solv->updaterules_end) - { - if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size) - if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0) - { - solv->problems.count = oldproblemcount; - havedisabled = 1; - break; /* start over */ - } - } - solver_disableproblem(solv, v); - havedisabled = 1; - break; /* start over */ + ori = solv->decisionq_why.elements[i]; /* the conflicting rule */ + assert(ori > 0); } - assert(solv->decisionq_why.elements[i] > 0); - IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE) - solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + solv->decisionq_why.elements[i]); - /* - * conflict with a pkg rule ? - */ - if (solv->decisionq_why.elements[i] < solv->pkgrules_end) + * record the problem + */ + doautouninstall = 0; + oldproblemcount = solv->problems.count; + queue_push(&solv->problems, 0); /* start problem */ + if (ori < solv->pkgrules_end) { - if (record_proof) + /* easy: conflict with system solvable or pkg rule */ + assert(v > 0 || v == -SYSTEMSOLVABLE); + IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE) { - queue_push(&solv->problems, solv->learnt_pool.count); - queue_push(&solv->learnt_pool, ri); - queue_push(&solv->learnt_pool, solv->decisionq_why.elements[i]); - queue_push(&solv->learnt_pool, 0); + if (ori) + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with pkg rule, disabling rule #%d\n", ri); + else + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with system solvable, disabling rule #%d\n", ri); + solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + ri); + if (ori) + solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + ori); } - else - queue_push(&solv->problems, 0); - assert(v > 0 || v == -SYSTEMSOLVABLE); - POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with pkg rule, disabling rule #%d\n", ri); - if (ri >= solv->jobrules && ri < solv->jobrules_end) - v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1); - else - v = ri; - queue_push(&solv->problems, v); - queue_push(&solv->problems, 0); - if (v >= solv->featurerules && v < solv->updaterules_end) + solver_recordproblem(solv, ri); + if (ri >= solv->featurerules && ri < solv->updaterules_end) + doautouninstall = 1; + } + else + { + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflicting update/job assertions over literal %d\n", vv); + /* + * push all of our rules (can only be feature or job rules) + * asserting this literal on the problem stack + */ + for (i = solv->pkgrules_end, rr = solv->rules + i; i < solv->learntrules; i++, rr++) { - if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size) - if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0) - { - solv->problems.count = oldproblemcount; - havedisabled = 1; - break; /* start over */ - } + if (rr->d < 0 /* disabled */ + || rr->w2) /* or no assertion */ + continue; + if (rr->p != vv /* not affecting the literal */ + && rr->p != -vv) + continue; + if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, i)) /* weak: silently ignore */ + continue; + + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, " - disabling rule #%d\n", i); + solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + i); + solver_recordproblem(solv, i); + if (i >= solv->featurerules && i < solv->updaterules_end) + doautouninstall = 1; } - solver_disableproblem(solv, v); - havedisabled = 1; - break; /* start over */ } + queue_push(&solv->problems, 0); /* finish problem */ - /* - * conflict with another job or update/feature rule - */ + /* try autouninstall if requested */ + if (doautouninstall) + { + if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size) + if (solver_autouninstall(solv, oldproblemcount) != 0) + { + solv->problems.count = oldproblemcount; + havedisabled = 1; + break; /* start over */ + } + } - /* record proof */ + /* record the proof if requested */ if (record_proof) { - queue_push(&solv->problems, solv->learnt_pool.count); + solv->problems.elements[oldproblemcount] = solv->learnt_pool.count; queue_push(&solv->learnt_pool, ri); - queue_push(&solv->learnt_pool, solv->decisionq_why.elements[i]); + if (ori) + queue_push(&solv->learnt_pool, ori); queue_push(&solv->learnt_pool, 0); } - else - queue_push(&solv->problems, 0); - POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflicting update/job assertions over literal %d\n", vv); - - /* - * push all of our rules (can only be feature or job rules) - * asserting this literal on the problem stack - */ - for (i = solv->featurerules, rr = solv->rules + i; i < solv->learntrules; i++, rr++) + if (!disablerules) { - if (rr->d < 0 /* disabled */ - || rr->w2) /* or no assertion */ - continue; - if (rr->p != vv /* not affecting the literal */ - && rr->p != -vv) - continue; - if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, i)) /* weak: silently ignore */ - continue; - - POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, " - disabling rule #%d\n", i); - solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + i); - - v = i; - if (i >= solv->jobrules && i < solv->jobrules_end) - v = -(solv->ruletojob.elements[i - solv->jobrules] + 1); - queue_push(&solv->problems, v); + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "UNSOLVABLE\n"); + return -1; } - queue_push(&solv->problems, 0); - - if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size) - if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0) - { - solv->problems.count = oldproblemcount; - havedisabled = 1; - break; /* start over */ - } - - for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++) - solver_disableproblem(solv, solv->problems.elements[i]); + /* disable all problem rules */ + solver_disableproblemset(solv, oldproblemcount); havedisabled = 1; break; /* start over */ } @@ -444,21 +326,15 @@ makeruledecisions(Solver *solv) continue; POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "assertion conflict, but I am weak, disabling "); - solver_printrule(solv, SOLV_DEBUG_UNSOLVABLE, r); - - if (ri >= solv->jobrules && ri < solv->jobrules_end) - v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1); - else - v = ri; - solver_disableproblem(solv, v); - if (v < 0) - solver_reenablepolicyrules(solv, -v); + solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, r); + solver_fixproblem(solv, ri); havedisabled = 1; break; /* start over */ } if (ii == solv->ruleassertions.count) break; /* finished! */ } + return 1; /* the new level */ } @@ -1013,36 +889,9 @@ analyze_unsolvable_rule(Solver *solv, Rule *r, Queue *weakq, Map *rseen) } if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, why) && weakq) queue_push(weakq, why); - /* do not add pkg rules to problem */ - if (why < solv->pkgrules_end) - return; - /* turn rule into problem */ - if (why >= solv->jobrules && why < solv->jobrules_end) - why = -(solv->ruletojob.elements[why - solv->jobrules] + 1); - /* normalize dup/infarch rules */ - if (why > solv->infarchrules && why < solv->infarchrules_end) - { - Id name = pool->solvables[-solv->rules[why].p].name; - while (why > solv->infarchrules && pool->solvables[-solv->rules[why - 1].p].name == name) - why--; - } - if (why > solv->duprules && why < solv->duprules_end) - { - Id name = pool->solvables[-solv->rules[why].p].name; - while (why > solv->duprules && pool->solvables[-solv->rules[why - 1].p].name == name) - why--; - } - - /* return if problem already countains our rule */ - if (solv->problems.count) - { - for (i = solv->problems.count - 1; i >= 0; i--) - if (solv->problems.elements[i] == 0) /* end of last problem reached? */ - break; - else if (solv->problems.elements[i] == why) - return; - } - queue_push(&solv->problems, why); + /* add non-pkg rules to problem and disable */ + if (why >= solv->pkgrules_end) + solver_recordproblem(solv, why); } @@ -1149,24 +998,18 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules) return 0; } queue_free(&weakq); - if (lastweak >= solv->jobrules && lastweak < solv->jobrules_end) - v = -(solv->ruletojob.elements[lastweak - solv->jobrules] + 1); - else - v = lastweak; POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "disabling "); solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + lastweak); if (lastweak >= solv->choicerules && lastweak < solv->choicerules_end) solver_disablechoicerules(solv, solv->rules + lastweak); - solver_disableproblem(solv, v); - if (v < 0) - solver_reenablepolicyrules(solv, -v); + solver_fixproblem(solv, lastweak); solver_reset(solv); return 0; } queue_free(&weakq); if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size) - if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0) + if (solver_autouninstall(solv, oldproblemcount) != 0) { solv->problems.count = oldproblemcount; solv->learnt_pool.count = oldlearntpoolcount; @@ -1184,8 +1027,7 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules) /* + 2: index + trailing zero */ if (disablerules && oldproblemcount + 2 < solv->problems.count) { - for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++) - solver_disableproblem(solv, solv->problems.elements[i]); + solver_disableproblemset(solv, oldproblemcount); /* XXX: might want to enable all weak rules again */ solver_reset(solv); return 0; @@ -2672,13 +2514,9 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) { if (level < 0) break; - makeruledecisions(solv); - level = 1; - if (!disablerules && solv->problems.count) - { - level = -1; - break; - } + level = makeruledecisions(solv, disablerules); + if (level < 0) + break; POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "initial propagate (propagate_index: %d; size decisionq: %d)...\n", solv->propagate_index, solv->decisionq.count); if ((r = propagate(solv, level)) != 0) { @@ -3494,7 +3332,6 @@ solver_solve(Solver *solv, Queue *job) deduceq2addedmap(solv, &addedmap); if (solv->nrules != initialnrules) solver_shrinkrules(solv, initialnrules); - solv->nrules = initialnrules; solv->lastpkgrule = 0; solv->pkgrules_end = 0; @@ -3588,7 +3425,7 @@ solver_solve(Solver *solv, Queue *job) } break; case SOLVER_DROP_ORPHANED: - if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid)) + if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid)) solv->droporphanedmap_all = 1; FOR_JOB_SELECT(p, pp, select, what) { @@ -4021,18 +3858,7 @@ solver_solve(Solver *solv, Queue *job) /* now create infarch and dup rules */ if (!solv->noinfarchcheck) - { - solver_addinfarchrules(solv, &addedmap); -#if 0 - if (pool->implicitobsoleteusescolors) - { - /* currently doesn't work well with infarch rules, so make - * them weak */ - for (i = solv->infarchrules; i < solv->infarchrules_end; i++) - queue_push(&solv->weakruleq, i); - } -#endif - } + solver_addinfarchrules(solv, &addedmap); else solv->infarchrules = solv->infarchrules_end = solv->nrules; @@ -4049,7 +3875,7 @@ solver_solve(Solver *solv, Queue *job) if (solv->bestupdatemap_all || solv->bestupdatemap.size || hasbestinstalljob) solver_addbestrules(solv, hasbestinstalljob); else - solv->bestrules = solv->bestrules_end = solv->nrules; + solv->bestrules = solv->bestrules_end = solv->bestrules_up = solv->nrules; if (needduprules) solver_freedupmaps(solv); /* no longer needed */ @@ -4079,7 +3905,7 @@ solver_solve(Solver *solv, Queue *job) map_free(&installcandidatemap); queue_free(&q); - POOL_DEBUG(SOLV_DEBUG_STATS, "%d pkg rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules\n", solv->pkgrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules); + POOL_DEBUG(SOLV_DEBUG_STATS, "%d pkg rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules, %d yumobs rules\n", solv->pkgrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules, solv->yumobsrules_end - solv->yumobsrules); POOL_DEBUG(SOLV_DEBUG_STATS, "overall rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024); /* create weak map */ @@ -4716,351 +4542,6 @@ pool_isemptyupdatejob(Pool *pool, Id how, Id what) return 1; } -static int -get_userinstalled_cmp(const void *ap, const void *bp, void *dp) -{ - return *(Id *)ap - *(Id *)bp; -} - -static int -get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp) -{ - Pool *pool = dp; - return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp)); -} - -static int -get_userinstalled_cmp_namearch(const void *ap, const void *bp, void *dp) -{ - Pool *pool = dp; - int r; - r = strcmp(pool_id2str(pool, ((Id *)ap)[0]), pool_id2str(pool, ((Id *)bp)[0])); - if (r) - return r; - return strcmp(pool_id2str(pool, ((Id *)ap)[1]), pool_id2str(pool, ((Id *)bp)[1])); -} - -static void -get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags) -{ - Id lastp = -1, lasta = -1; - int i, j; - if (q->count < ((flags & GET_USERINSTALLED_NAMEARCH) ? 4 : 2)) - return; - if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) - solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), get_userinstalled_cmp_namearch, pool); - else if ((flags & GET_USERINSTALLED_NAMES) != 0) - solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool); - else - solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0); - if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) - { - for (i = j = 0; i < q->count; i += 2) - if (q->elements[i] != lastp || q->elements[i + 1] != lasta) - { - q->elements[j++] = lastp = q->elements[i]; - q->elements[j++] = lasta = q->elements[i + 1]; - } - } - else - { - for (i = j = 0; i < q->count; i++) - if (q->elements[i] != lastp) - q->elements[j++] = lastp = q->elements[i]; - } - queue_truncate(q, j); -} - -static void -namearch2solvables(Pool *pool, Queue *q, Queue *qout, int job) -{ - int i; - if (!pool->installed) - return; - for (i = 0; i < q->count; i += 2) - { - Id p, pp, name = q->elements[i], arch = q->elements[i + 1]; - FOR_PROVIDES(p, pp, name) - { - Solvable *s = pool->solvables + p; - if (s->repo != pool->installed || s->name != name || (arch && s->arch != arch)) - continue; - if (job) - queue_push(qout, job); - queue_push(qout, p); - } - } -} - -void -solver_get_userinstalled(Solver *solv, Queue *q, int flags) -{ - Pool *pool = solv->pool; - Id p, p2, pp; - Solvable *s; - Repo *installed = solv->installed; - int i, j; - Map userinstalled; - - map_init(&userinstalled, 0); - queue_empty(q); - /* first process jobs */ - for (i = 0; i < solv->job.count; i += 2) - { - Id how = solv->job.elements[i]; - Id what, select; - if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED) - { - if (!userinstalled.size) - map_grow(&userinstalled, installed->end - installed->start); - what = solv->job.elements[i + 1]; - select = how & SOLVER_SELECTMASK; - if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid)) - { - FOR_REPO_SOLVABLES(installed, p, s) - MAPSET(&userinstalled, p - installed->start); - } - FOR_JOB_SELECT(p, pp, select, what) - if (pool->solvables[p].repo == installed) - MAPSET(&userinstalled, p - installed->start); - continue; - } - if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL) - continue; - if ((how & SOLVER_NOTBYUSER) != 0) - continue; - what = solv->job.elements[i + 1]; - select = how & SOLVER_SELECTMASK; - FOR_JOB_SELECT(p, pp, select, what) - if (solv->decisionmap[p] > 0) - { - queue_push(q, p); -#ifdef ENABLE_LINKED_PKGS - if (has_package_link(pool, pool->solvables + p)) - { - int j; - Queue lq; - queue_init(&lq); - find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0); - for (j = 0; j < lq.count; j++) - if (solv->decisionmap[lq.elements[j]] > 0) - queue_push(q, lq.elements[j]); - } -#endif - } - } - /* now process updates of userinstalled packages */ - if (installed && userinstalled.size) - { - for (i = 1; i < solv->decisionq.count; i++) - { - p = solv->decisionq.elements[i]; - if (p <= 0) - continue; - s = pool->solvables + p; - if (!s->repo) - continue; - if (s->repo == installed) - { - if (MAPTST(&userinstalled, p - installed->start)) - queue_push(q, p); - continue; - } - /* new package, check if we replace a userinstalled one */ - FOR_PROVIDES(p2, pp, s->name) - { - Solvable *ps = pool->solvables + p2; - if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start)) - continue; - if (!pool->implicitobsoleteusesprovides && s->name != ps->name) - continue; - if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps)) - continue; - queue_push(q, p); - break; - } - if (!p2 && s->repo != installed && s->obsoletes) - { - Id obs, *obsp = s->repo->idarraydata + s->obsoletes; - while ((obs = *obsp++) != 0) - { - FOR_PROVIDES(p2, pp, obs) - { - Solvable *ps = pool->solvables + p2; - if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start)) - continue; - if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) - continue; - if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) - continue; - queue_push(q, p); - break; - } - if (p2) - break; - } - } - } - } - map_free(&userinstalled); - - /* convert to desired output format */ - if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) - { - int qcount = q->count; - queue_insertn(q, 0, qcount, 0); - for (i = j = 0; i < qcount; i++) - { - s = pool->solvables + q->elements[i + qcount]; - q->elements[j++] = s->name; - q->elements[j++] = s->arch; - } - } - else if ((flags & GET_USERINSTALLED_NAMES) != 0) - { - for (i = 0; i < q->count; i++) - { - s = pool->solvables + q->elements[i]; - q->elements[i] = s->name; - } - } - /* sort and unify */ - get_userinstalled_sort_uniq(pool, q, flags); - - /* invert if asked for */ - if ((flags & GET_USERINSTALLED_INVERTED) != 0) - { - /* first generate queue with all installed packages */ - Queue invq; - queue_init(&invq); - for (i = 1; i < solv->decisionq.count; i++) - { - p = solv->decisionq.elements[i]; - if (p <= 0) - continue; - s = pool->solvables + p; - if (!s->repo) - continue; - if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) - queue_push2(&invq, s->name, s->arch); - else if ((flags & GET_USERINSTALLED_NAMES) != 0) - queue_push(&invq, s->name); - else - queue_push(&invq, p); - } - /* push q on invq, just in case... */ - queue_insertn(&invq, invq.count, q->count, q->elements); - get_userinstalled_sort_uniq(pool, &invq, flags); - /* subtract queues (easy as they are sorted and invq is a superset of q) */ - if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) - { - if (q->count) - { - for (i = j = 0; i < invq.count; i += 2) - if (invq.elements[i] == q->elements[j] && invq.elements[i + 1] == q->elements[j + 1]) - { - invq.elements[i] = invq.elements[i + 1] = 0; - j += 2; - if (j >= q->count) - break; - } - queue_empty(q); - } - for (i = 0; i < invq.count; i += 2) - if (invq.elements[i]) - queue_push2(q, invq.elements[i], invq.elements[i + 1]); - } - else - { - if (q->count) - { - for (i = j = 0; i < invq.count; i++) - if (invq.elements[i] == q->elements[j]) - { - invq.elements[i] = 0; - if (++j >= q->count) - break; - } - queue_empty(q); - } - for (i = 0; i < invq.count; i++) - if (invq.elements[i]) - queue_push(q, invq.elements[i]); - } - queue_free(&invq); - } -} - -void -pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags) -{ - int i; - - if ((flags & GET_USERINSTALLED_INVERTED) != 0) - { - Queue invq; - Id p, lastid; - Solvable *s; - int bad; - if (!pool->installed) - return; - queue_init(&invq); - if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) - flags &= ~GET_USERINSTALLED_NAMES; /* just in case */ - FOR_REPO_SOLVABLES(pool->installed, p, s) - queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p); - if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) - { - /* for namearch we convert to packages */ - namearch2solvables(pool, q, &invq, 0); - get_userinstalled_sort_uniq(pool, &invq, flags); - namearch2solvables(pool, q, &invq, 0); - flags = 0; - } - else - { - queue_insertn(&invq, invq.count, q->count, q->elements); - get_userinstalled_sort_uniq(pool, &invq, flags); - /* now the fun part, add q again, sort, and remove all dups */ - queue_insertn(&invq, invq.count, q->count, q->elements); - } - if (invq.count > 1) - { - if ((flags & GET_USERINSTALLED_NAMES) != 0) - solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool); - else - solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0); - } - lastid = -1; - bad = 1; - for (i = 0; i < invq.count; i++) - { - if (invq.elements[i] == lastid) - { - bad = 1; - continue; - } - if (!bad) - queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid); - bad = 0; - lastid = invq.elements[i]; - } - if (!bad) - queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid); - queue_free(&invq); - } - else - { - if (flags & GET_USERINSTALLED_NAMEARCH) - namearch2solvables(pool, q, job, SOLVER_USERINSTALLED | SOLVER_SOLVABLE); - else - { - for (i = 0; i < q->count; i++) - queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]); - } - } -} - int solver_alternatives_count(Solver *solv) { diff --git a/src/solver.h b/src/solver.h index 1b85fb3..ebb2232 100644 --- a/src/solver.h +++ b/src/solver.h @@ -68,6 +68,7 @@ struct _Solver { Id duprules_end; Id bestrules; /* rules from SOLVER_FORCEBEST */ + Id bestrules_up; /* update rule part starts here*/ Id bestrules_end; Id *bestrules_pkg; diff --git a/src/userinstalled.c b/src/userinstalled.c new file mode 100644 index 0000000..0efcdd7 --- /dev/null +++ b/src/userinstalled.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2017, SUSE LLC. + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +/* Functions that help getting/setting userinstalled packages. */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "solver.h" +#include "solver_private.h" +#include "bitmap.h" +#include "pool.h" +#include "util.h" +#include "poolarch.h" +#include "linkedpkg.h" + +static int +get_userinstalled_cmp(const void *ap, const void *bp, void *dp) +{ + return *(Id *)ap - *(Id *)bp; +} + +static int +get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp) +{ + Pool *pool = dp; + return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp)); +} + +static int +get_userinstalled_cmp_namearch(const void *ap, const void *bp, void *dp) +{ + Pool *pool = dp; + int r; + r = strcmp(pool_id2str(pool, ((Id *)ap)[0]), pool_id2str(pool, ((Id *)bp)[0])); + if (r) + return r; + return strcmp(pool_id2str(pool, ((Id *)ap)[1]), pool_id2str(pool, ((Id *)bp)[1])); +} + +static void +get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags) +{ + Id lastp = -1, lasta = -1; + int i, j; + if (q->count < ((flags & GET_USERINSTALLED_NAMEARCH) ? 4 : 2)) + return; + if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) + solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), get_userinstalled_cmp_namearch, pool); + else if ((flags & GET_USERINSTALLED_NAMES) != 0) + solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool); + else + solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0); + if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) + { + for (i = j = 0; i < q->count; i += 2) + if (q->elements[i] != lastp || q->elements[i + 1] != lasta) + { + q->elements[j++] = lastp = q->elements[i]; + q->elements[j++] = lasta = q->elements[i + 1]; + } + } + else + { + for (i = j = 0; i < q->count; i++) + if (q->elements[i] != lastp) + q->elements[j++] = lastp = q->elements[i]; + } + queue_truncate(q, j); +} + +static void +namearch2solvables(Pool *pool, Queue *q, Queue *qout, int job) +{ + int i; + if (!pool->installed) + return; + for (i = 0; i < q->count; i += 2) + { + Id p, pp, name = q->elements[i], arch = q->elements[i + 1]; + FOR_PROVIDES(p, pp, name) + { + Solvable *s = pool->solvables + p; + if (s->repo != pool->installed || s->name != name || (arch && s->arch != arch)) + continue; + if (job) + queue_push(qout, job); + queue_push(qout, p); + } + } +} + +void +solver_get_userinstalled(Solver *solv, Queue *q, int flags) +{ + Pool *pool = solv->pool; + Id p, p2, pp; + Solvable *s; + Repo *installed = solv->installed; + int i, j; + Map userinstalled; + + map_init(&userinstalled, 0); + queue_empty(q); + /* first process jobs */ + for (i = 0; i < solv->job.count; i += 2) + { + Id how = solv->job.elements[i]; + Id what, select; + if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED) + { + if (!userinstalled.size) + map_grow(&userinstalled, installed->end - installed->start); + what = solv->job.elements[i + 1]; + select = how & SOLVER_SELECTMASK; + if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid)) + { + FOR_REPO_SOLVABLES(installed, p, s) + MAPSET(&userinstalled, p - installed->start); + } + FOR_JOB_SELECT(p, pp, select, what) + if (pool->solvables[p].repo == installed) + MAPSET(&userinstalled, p - installed->start); + continue; + } + if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL) + continue; + if ((how & SOLVER_NOTBYUSER) != 0) + continue; + what = solv->job.elements[i + 1]; + select = how & SOLVER_SELECTMASK; + FOR_JOB_SELECT(p, pp, select, what) + if (solv->decisionmap[p] > 0) + { + queue_push(q, p); +#ifdef ENABLE_LINKED_PKGS + if (has_package_link(pool, pool->solvables + p)) + { + int j; + Queue lq; + queue_init(&lq); + find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0); + for (j = 0; j < lq.count; j++) + if (solv->decisionmap[lq.elements[j]] > 0) + queue_push(q, lq.elements[j]); + } +#endif + } + } + /* now process updates of userinstalled packages */ + if (installed && userinstalled.size) + { + for (i = 1; i < solv->decisionq.count; i++) + { + p = solv->decisionq.elements[i]; + if (p <= 0) + continue; + s = pool->solvables + p; + if (!s->repo) + continue; + if (s->repo == installed) + { + if (MAPTST(&userinstalled, p - installed->start)) + queue_push(q, p); + continue; + } + /* new package, check if we replace a userinstalled one */ + FOR_PROVIDES(p2, pp, s->name) + { + Solvable *ps = pool->solvables + p2; + if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start)) + continue; + if (!pool->implicitobsoleteusesprovides && s->name != ps->name) + continue; + if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + queue_push(q, p); + break; + } + if (!p2 && s->repo != installed && s->obsoletes) + { + Id obs, *obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + { + FOR_PROVIDES(p2, pp, obs) + { + Solvable *ps = pool->solvables + p2; + if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start)) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + queue_push(q, p); + break; + } + if (p2) + break; + } + } + } + } + map_free(&userinstalled); + + /* convert to desired output format */ + if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) + { + int qcount = q->count; + queue_insertn(q, 0, qcount, 0); + for (i = j = 0; i < qcount; i++) + { + s = pool->solvables + q->elements[i + qcount]; + q->elements[j++] = s->name; + q->elements[j++] = s->arch; + } + } + else if ((flags & GET_USERINSTALLED_NAMES) != 0) + { + for (i = 0; i < q->count; i++) + { + s = pool->solvables + q->elements[i]; + q->elements[i] = s->name; + } + } + /* sort and unify */ + get_userinstalled_sort_uniq(pool, q, flags); + + /* invert if asked for */ + if ((flags & GET_USERINSTALLED_INVERTED) != 0) + { + /* first generate queue with all installed packages */ + Queue invq; + queue_init(&invq); + for (i = 1; i < solv->decisionq.count; i++) + { + p = solv->decisionq.elements[i]; + if (p <= 0) + continue; + s = pool->solvables + p; + if (!s->repo) + continue; + if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) + queue_push2(&invq, s->name, s->arch); + else if ((flags & GET_USERINSTALLED_NAMES) != 0) + queue_push(&invq, s->name); + else + queue_push(&invq, p); + } + /* push q on invq, just in case... */ + queue_insertn(&invq, invq.count, q->count, q->elements); + get_userinstalled_sort_uniq(pool, &invq, flags); + /* subtract queues (easy as they are sorted and invq is a superset of q) */ + if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) + { + if (q->count) + { + for (i = j = 0; i < invq.count; i += 2) + if (invq.elements[i] == q->elements[j] && invq.elements[i + 1] == q->elements[j + 1]) + { + invq.elements[i] = invq.elements[i + 1] = 0; + j += 2; + if (j >= q->count) + break; + } + queue_empty(q); + } + for (i = 0; i < invq.count; i += 2) + if (invq.elements[i]) + queue_push2(q, invq.elements[i], invq.elements[i + 1]); + } + else + { + if (q->count) + { + for (i = j = 0; i < invq.count; i++) + if (invq.elements[i] == q->elements[j]) + { + invq.elements[i] = 0; + if (++j >= q->count) + break; + } + queue_empty(q); + } + for (i = 0; i < invq.count; i++) + if (invq.elements[i]) + queue_push(q, invq.elements[i]); + } + queue_free(&invq); + } +} + +void +pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags) +{ + int i; + + if ((flags & GET_USERINSTALLED_INVERTED) != 0) + { + Queue invq; + Id p, lastid; + Solvable *s; + int bad; + if (!pool->installed) + return; + queue_init(&invq); + if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) + flags &= ~GET_USERINSTALLED_NAMES; /* just in case */ + FOR_REPO_SOLVABLES(pool->installed, p, s) + queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p); + if ((flags & GET_USERINSTALLED_NAMEARCH) != 0) + { + /* for namearch we convert to packages */ + namearch2solvables(pool, q, &invq, 0); + get_userinstalled_sort_uniq(pool, &invq, flags); + namearch2solvables(pool, q, &invq, 0); + flags = 0; + } + else + { + queue_insertn(&invq, invq.count, q->count, q->elements); + get_userinstalled_sort_uniq(pool, &invq, flags); + /* now the fun part, add q again, sort, and remove all dups */ + queue_insertn(&invq, invq.count, q->count, q->elements); + } + if (invq.count > 1) + { + if ((flags & GET_USERINSTALLED_NAMES) != 0) + solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool); + else + solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0); + } + lastid = -1; + bad = 1; + for (i = 0; i < invq.count; i++) + { + if (invq.elements[i] == lastid) + { + bad = 1; + continue; + } + if (!bad) + queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid); + bad = 0; + lastid = invq.elements[i]; + } + if (!bad) + queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid); + queue_free(&invq); + } + else + { + if (flags & GET_USERINSTALLED_NAMEARCH) + namearch2solvables(pool, q, job, SOLVER_USERINSTALLED | SOLVER_SOLVABLE); + else + { + for (i = 0; i < q->count; i++) + queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]); + } + } +} + |