+ 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_info[rid - solv->bestrules] < 0)
+ rid = -(solv->ruletojob.elements[-solv->bestrules_info[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]);
+}
+
+#ifdef SUSE
+static inline int
+suse_isptf(Pool *pool, Solvable *s)
+{
+ if (!strncmp("ptf-", pool_id2str(pool, s->name), 4))
+ return 1;
+ return 0;
+}
+#endif
+
+/*-------------------------------------------------------------------
+ * 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;
+ Id p = solv->installed->start + (v - solv->updaterules);
+ if (m && !MAPTST(m, v - solv->updaterules))
+ continue;
+#ifdef SUSE
+ if (suse_isptf(pool, pool->solvables + p))
+ continue; /* do not autouninstall ptf packages */
+#endif
+ if (pool->considered && !MAPTST(pool->considered, p))
+ continue; /* do not uninstalled disabled packages */
+ if (solv->bestrules_info && solv->bestrules_end > solv->bestrules)
+ {
+ int j;
+ for (j = start + 1; j < solv->problems.count - 1; j++)
+ {
+ Id vv = solv->problems.elements[j];
+ if (vv >= solv->bestrules && vv < solv->bestrules_end && solv->bestrules_info[vv - solv->bestrules] == p)
+ break;
+ }
+ if (j < solv->problems.count - 1)
+ continue; /* best rule involved, do not uninstall */
+ }
+ /* 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 == p)
+ {
+ 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;